task-hookrs-0.9.0/.cargo_vcs_info.json0000644000000001360000000000100133070ustar { "git": { "sha1": "7d2e57174ae70408d2e270290aca1f39eb82ff9a" }, "path_in_vcs": "" }task-hookrs-0.9.0/.github/dependabot.yml000064400000000000000000000002711046102023000162670ustar 00000000000000version: 2 updates: - package-ecosystem: cargo directory: "/" schedule: interval: monthly - package-ecosystem: github-actions directory: "/" schedule: interval: monthly task-hookrs-0.9.0/.github/workflows/ci.yml000064400000000000000000000037731046102023000166240ustar 00000000000000on: [push, pull_request] name: CI jobs: check: name: Check runs-on: ubuntu-latest strategy: matrix: rust: - 1.63.0 - stable steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - uses: actions-rs/cargo@v1 with: command: check fmt: name: format needs: check runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master with: toolchain: 1.63.0 - run: rustup component add rustfmt - name: cargo-fmt uses: actions-rs/cargo@v1 with: command: fmt args: -- --check test: name: Test Suite runs-on: ubuntu-latest strategy: matrix: rust: - 1.63.0 - stable steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - run: sudo apt-get update && sudo apt-get install -y taskwarrior - uses: actions-rs/cargo@v1 with: command: test - run: | # prepare taskwarrior, initial setup task rc.confirmation=off || echo 0 cargo run --example create_task | tee /tmp/create_task task import /tmp/create_task || exit 1 cat /tmp/create_task | cargo run --example import_task || exit 1 clippy: needs: check name: clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master with: toolchain: 1.63.0 - uses: swatinem/rust-cache@v2 - run: rustup component add clippy - name: cargo-clippy run: cargo clippy --all --all-targets --all-features -- -D warnings ci: name: CI if: ${{ success() }} needs: - check - clippy - test runs-on: ubuntu-latest steps: - name: CI succeeded run: exit 0 task-hookrs-0.9.0/.github/workflows/commit-lint.yml000064400000000000000000000006611046102023000204560ustar 00000000000000on: pull_request name: CI jobs: commitlint: name: Commit lint runs-on: ubuntu-latest steps: - uses: actions/checkout@master with: fetch-depth: 0 - name: Setup Python uses: actions/setup-python@v4.6.1 with: python-version: '3.x' - name: Install gitlint run: pip install gitlint - run: gitlint --commits $(git merge-base origin/master HEAD).. task-hookrs-0.9.0/.gitignore000064400000000000000000000000221046102023000140610ustar 00000000000000target Cargo.lock task-hookrs-0.9.0/.gitlint000064400000000000000000000115451046102023000135600ustar 00000000000000# Edit this file as you like. # # All these sections are optional. Each section with the exception of [general] represents # one rule and each key in it is an option for that specific rule. # # Rules and sections can be referenced by their full name or by id. For example # section "[body-max-line-length]" could also be written as "[B1]". Full section names are # used in here for clarity. # [general] # Ignore certain rules, this example uses both full name and id ignore=body-is-missing # verbosity should be a value between 1 and 3, the commandline -v flags take precedence over this # verbosity = 2 # By default gitlint will ignore merge, revert, fixup and squash commits. ignore-merge-commits=true # ignore-revert-commits=true ignore-fixup-commits=false ignore-squash-commits=false # Ignore any data send to gitlint via stdin # ignore-stdin=true # Fetch additional meta-data from the local repository when manually passing a # commit message to gitlint via stdin or --commit-msg. Disabled by default. # staged=true # Hard fail when the target commit range is empty. Note that gitlint will # already fail by default on invalid commit ranges. This option is specifically # to tell gitlint to fail on *valid but empty* commit ranges. # Disabled by default. # fail-without-commits=true # Enable debug mode (prints more output). Disabled by default. # debug=true # Enable community contributed rules # See http://jorisroovers.github.io/gitlint/contrib_rules for details contrib=CC1, CC2 # Set the extra-path where gitlint will search for user defined rules # See http://jorisroovers.github.io/gitlint/user_defined_rules for details # extra-path=examples/ # This is an example of how to configure the "title-max-length" rule and # set the line-length it enforces to 50 # [title-max-length] # line-length=50 # Conversely, you can also enforce minimal length of a title with the # "title-min-length" rule: # [title-min-length] # min-length=5 # [title-must-not-contain-word] # Comma-separated list of words that should not occur in the title. Matching is case # insensitive. It's fine if the keyword occurs as part of a larger word (so "WIPING" # will not cause a violation, but "WIP: my title" will. # words=wip # [title-match-regex] # python-style regex that the commit-msg title must match # Note that the regex can contradict with other rules if not used correctly # (e.g. title-must-not-contain-word). # regex=^US[0-9]* # [body-max-line-length] # line-length=72 # [body-min-length] # min-length=5 # [body-is-missing] # Whether to ignore this rule on merge commits (which typically only have a title) # default = True # ignore-merge-commits=false # [body-changed-file-mention] # List of files that need to be explicitly mentioned in the body when they are changed # This is useful for when developers often erroneously edit certain files or git submodules. # By specifying this rule, developers can only change the file when they explicitly reference # it in the commit message. # files=gitlint-core/gitlint/rules.py,README.md # [body-match-regex] # python-style regex that the commit-msg body must match. # E.g. body must end in My-Commit-Tag: foo # regex=My-Commit-Tag: foo$ # [author-valid-email] # python-style regex that the commit author email address must match. # For example, use the following regex if you only want to allow email addresses from foo.com # regex=[^@]+@foo.com # [ignore-by-title] # Ignore certain rules for commits of which the title matches a regex # E.g. Match commit titles that start with "Release" # regex=^Release(.*) # Ignore certain rules, you can reference them by their id or by their full name # Use 'all' to ignore all rules # ignore=T1,body-min-length # [ignore-by-body] # Ignore certain rules for commits of which the body has a line that matches a regex # E.g. Match bodies that have a line that that contain "release" # regex=(.*)release(.*) # # Ignore certain rules, you can reference them by their id or by their full name # Use 'all' to ignore all rules # ignore=T1,body-min-length # [ignore-body-lines] # Ignore certain lines in a commit body that match a regex. # E.g. Ignore all lines that start with 'Co-Authored-By' # regex=^Co-Authored-By [ignore-by-author-name] # Ignore certain rules for commits of which the author name matches a regex # E.g. Match commits made by dependabot regex=(.*)dependabot(.*) # # Ignore certain rules, you can reference them by their id or by their full name # Use 'all' to ignore all rules # ignore=T1,body-min-length # This is a contrib rule - a community contributed rule. These are disabled by default. # You need to explicitly enable them one-by-one by adding them to the "contrib" option # under [general] section above. # [contrib-title-conventional-commits] # Specify allowed commit types. For details see: https://www.conventionalcommits.org/ # types = bugfix,user-story,epic [contrib-body-requires-signed-off-by] [contrib-disallow-cleanup-commits] task-hookrs-0.9.0/.travis.yml000064400000000000000000000011211046102023000142030ustar 00000000000000language: rust rust: - stable - beta - nightly cache: cargo matrix: allow_failures: - rust: nightly before_install: - sudo apt-get -y install task script: | cargo build || exit 1 cargo test || exit 1 # prepare taskwarrior, initial setup yes | task cargo run --example create_task | tee /tmp/create_task task import /tmp/create_task || exit 1 cat /tmp/create_task | cargo run --example import_task || exit 1 notifications: irc: channels: - chat.freenode.net#imag template: - "%{repository_name} (%{branch} @ %{commit} by %{author}): %{result}" task-hookrs-0.9.0/CHANGELOG.md000064400000000000000000000031001046102023000137020ustar 00000000000000# Changelog This changelog was started with the 0.4.0 release. ## Next ## 0.9.0 * Support for taskwarrior 2.6.0 serialization format * MSRV was updated to 1.63.0 ## 0.8.0 * Replace "failure" with "thiserror" * Cleanup the codebase for rust edition 2021 ## 0.7.0 * Bugfix: "imask" field in taskwarrior is a float, change type to f64. This is a breaking API change, thus the minor version is bumped. * CI setup was fixed to actually fail when an error happens during compilation/testing. We had luck that it worked before without issue. * All rustc versions from 1.32 until beta are tested now via travis, for maximum backwards compat ## 0.6.0 * Switched error handling to `failure` * Added TaskBuilder * Added support for user defined attributes (short UDA) via the task-hookrs::uda module. (This reintroduced the dependency to the "log" crate.) ## 0.5.0 * The "uuid" dependency was updated from 0.5 to 0.6 ## 0.4.0 * Dependencies updated * Unused "log" dependency removed * Interface changed: The provided types are only data containers with read/write functionality, no aggregate functionality anymore. This mainly means that `Task::add_annotation()` and `Task::add_annotations()` were removed, but all other data-accessor functions got a mutable variant. ## 0.3.0 * Dependencies updated * Update serde so we do not need to de/ser on our own anymore * Added examples * Test task importing in travis-ci ## 0.2.2 * Dependencies updated ## 0.2.1 * Relicensed to MPL2.0 ## 0.2.0 * Annotation support * Documentation * Dead code removed ## 0.1.0 * Initial version task-hookrs-0.9.0/Cargo.lock0000644000000447260000000000100112770ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] [[package]] name = "android_system_properties" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" version = "3.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" [[package]] name = "cc" version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ "iana-time-zone", "js-sys", "num-integer", "num-traits", "time", "wasm-bindgen", "winapi", ] [[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 = "core-foundation-sys" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cxx" version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27874566aca772cb515af4c6e997b5fe2119820bca447689145e39bb734d19a0" dependencies = [ "cc", "cxxbridge-flags", "cxxbridge-macro", "link-cplusplus", ] [[package]] name = "cxx-build" version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7bb951f2523a49533003656a72121306b225ec16a49a09dc6b0ba0d6f3ec3c0" dependencies = [ "cc", "codespan-reporting", "once_cell", "proc-macro2", "quote", "scratch", "syn", ] [[package]] name = "cxxbridge-flags" version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be778b6327031c1c7b61dd2e48124eee5361e6aa76b8de93692f011b08870ab4" [[package]] name = "cxxbridge-macro" version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b8a2b87662fe5a0a0b38507756ab66aff32638876a0866e5a5fc82ceb07ee49" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "darling" version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" dependencies = [ "darling_core", "darling_macro", ] [[package]] name = "darling_core" version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", "syn", ] [[package]] name = "darling_macro" version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" dependencies = [ "darling_core", "quote", "syn", ] [[package]] name = "derive_builder" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" dependencies = [ "derive_builder_macro", ] [[package]] name = "derive_builder_core" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" dependencies = [ "darling", "proc-macro2", "quote", "syn", ] [[package]] name = "derive_builder_macro" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" dependencies = [ "derive_builder_core", "syn", ] [[package]] name = "env_logger" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" dependencies = [ "humantime", "is-terminal", "log", "regex", "termcolor", ] [[package]] name = "errno" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" dependencies = [ "errno-dragonfly", "libc", "winapi", ] [[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 = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "getrandom" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", "libc", "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] name = "hermit-abi" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" dependencies = [ "libc", ] [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "iana-time-zone" version = "0.1.53" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", "winapi", ] [[package]] name = "iana-time-zone-haiku" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" dependencies = [ "cxx", "cxx-build", ] [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "io-lifetimes" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" dependencies = [ "libc", "windows-sys", ] [[package]] name = "is-terminal" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330" dependencies = [ "hermit-abi", "io-lifetimes", "rustix", "windows-sys", ] [[package]] name = "itoa" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "js-sys" version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] [[package]] name = "libc" version = "0.2.138" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" [[package]] name = "link-cplusplus" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" dependencies = [ "cc", ] [[package]] name = "linux-raw-sys" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "log" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "num-integer" version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", "num-traits", ] [[package]] name = "num-traits" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" [[package]] name = "proc-macro2" version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9d89e5dba24725ae5678020bf8f1357a9aa7ff10736b551adbcd3f8d17d766f" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "556d0f47a940e895261e77dc200d5eadfc6ef644c179c6f5edfc105e3a2292c8" dependencies = [ "proc-macro2", ] [[package]] name = "regex" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "rustix" version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "ryu" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "scratch" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" [[package]] name = "serde" version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8778cc0b528968fe72abec38b5db5a20a70d148116cd9325d2bc5f5180ca3faf" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ee3a69cd2c7e06684677e5629b3878b253af05e4714964204279c6bc02cf0b" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "task-hookrs" version = "0.9.0" dependencies = [ "chrono", "derive_builder", "env_logger", "log", "serde", "serde_json", "thiserror", "uuid", ] [[package]] name = "termcolor" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "time" version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" dependencies = [ "libc", "wasi 0.10.0+wasi-snapshot-preview1", "winapi", ] [[package]] name = "unicode-ident" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-width" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "uuid" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" dependencies = [ "getrandom", "serde", ] [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[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-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] [[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-sys" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" [[package]] name = "windows_aarch64_msvc" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" [[package]] name = "windows_i686_gnu" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" [[package]] name = "windows_i686_msvc" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" [[package]] name = "windows_x86_64_gnu" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" [[package]] name = "windows_x86_64_gnullvm" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" [[package]] name = "windows_x86_64_msvc" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" task-hookrs-0.9.0/Cargo.toml0000644000000025520000000000100113110ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "task-hookrs" version = "0.9.0" authors = [ "Matthias Beyer ", "Mario Krehl ", "Malte Brandy ", ] description = "Crate to work with taskwarrior exported JSON" readme = "./README.md" keywords = [ "json", "taskwarrior", "todo", "import", "export", ] license = "MPL-2.0" repository = "https://github.com/matthiasbeyer/task-hookrs" resolver = "2" [dependencies.chrono] version = "0.4" [dependencies.derive_builder] version = "0.12.0" [dependencies.log] version = "0.4" [dependencies.serde] version = "1" features = ["derive"] [dependencies.serde_json] version = "1" [dependencies.thiserror] version = "1" [dependencies.uuid] version = "1.2" features = [ "serde", "v4", ] [dev-dependencies.env_logger] version = "0.10" [badges.maintenance] status = "passively-maintained" task-hookrs-0.9.0/Cargo.toml.orig000064400000000000000000000014371046102023000147730ustar 00000000000000[package] name = "task-hookrs" version = "0.9.0" authors = ["Matthias Beyer ", "Mario Krehl ", "Malte Brandy ", ] description = "Crate to work with taskwarrior exported JSON" keywords = ["json", "taskwarrior", "todo", "import", "export"] repository = "https://github.com/matthiasbeyer/task-hookrs" readme = "./README.md" license = "MPL-2.0" edition = "2021" resolver = "2" [badges] maintenance = { status = "passively-maintained" } [dependencies] chrono = "0.4" serde = { version = "1", features = ["derive"] } serde_json = "1" uuid = { version = "1.2", features = ["serde", "v4"] } log = "0.4" derive_builder = "0.12.0" thiserror = "1" [dev-dependencies] env_logger = "0.10" task-hookrs-0.9.0/LICENSE000064400000000000000000000405261046102023000131130ustar 00000000000000Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. task-hookrs-0.9.0/README.md000064400000000000000000000022461046102023000133620ustar 00000000000000# task-hookrs `task-hookrs` is a rust library for writing [taskwarrior](https://taskwarrior.org) hooks. * [crate](https://crates.io/crates/task-hookrs/) * [Documentation](https://docs.rs/task-hookrs/) [![Crates.io](https://img.shields.io/crates/v/task-hookrs.svg?maxAge=2592000)]() [![Crates.io](https://img.shields.io/crates/d/task-hookrs.svg?maxAge=2592000)]() [![Crates.io](https://img.shields.io/crates/dv/task-hookrs.svg?maxAge=2592000)]() [![Crates.io](https://img.shields.io/crates/l/task-hookrs.svg?maxAge=2592000)]() `task-hookrs` is able to import and export the JSON taskwarrior understands, so you can write taskwarrior hooks without having to deal with the JSON import/export foo by simply using this crate. # MSRV 1.63.0 # License task-hookrs - A Rust library for writing taskwarrior hooks and general interfacing with taskwarrior Copyright (C) 2016-2023 Matthias Beyer This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. For more information and the full license text, see [the LICENSE file](./LICENSE). task-hookrs-0.9.0/bors.toml000064400000000000000000000001471046102023000137430ustar 00000000000000# Must pass on the merge with the master branch status = [ "CI" ] delete_merged_branches = true task-hookrs-0.9.0/deny.toml000064400000000000000000000043161046102023000137370ustar 00000000000000[licenses] # The lint level for crates which do not have a detectable license unlicensed = "deny" # List of explictly allowed licenses # See https://spdx.org/licenses/ for list of possible licenses # [possible values: any SPDX 3.7 short identifier (+ optional exception)]. allow = ["MPL-2.0"] # List of explictly disallowed licenses # See https://spdx.org/licenses/ for list of possible licenses # [possible values: any SPDX 3.7 short identifier (+ optional exception)]. deny = [] # The lint level for licenses considered copyleft copyleft = "deny" # Blanket approval or denial for OSI-approved or FSF Free/Libre licenses # * both - The license will only be approved if it is both OSI-approved *AND* FSF/Free # * either - The license will be approved if it is either OSI-approved *OR* FSF/Free # * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF/Free # * fsf-only - The license will be approved if is FSF/Free *AND NOT* OSI-approved # * neither - The license will be denied if is FSF/Free *OR* OSI-approved allow-osi-fsf-free = "either" # The confidence threshold for detecting a license from license text. # The higher the value, the more closely the license text must be to the # canonical license text of a valid SPDX license file. # [possible values: any between 0.0 and 1.0]. confidence-threshold = 0.8 [bans] # Lint level for when multiple versions of the same crate are detected multiple-versions = "warn" # The graph highlighting used when creating dotgraphs for crates # with multiple versions # * lowest-version - The path to the lowest versioned duplicate is highlighted # * simplest-path - The path to the version with the fewest edges is highlighted # * all - Both lowest-version and simplest-path are used highlight = "all" # List of crates that are allowed. Use with care! allow = [ ] # List of crates to deny deny = [ ] # Certain crates/versions that will be skipped when doing duplicate detection. skip = [ ] # Similarly to `skip` allows you to skip certain crates during duplicate detection, # unlike skip, it also includes the entire tree of transitive dependencies starting at # the specified crate, up to a certain depth, which is by default infinite skip-tree = [ ] [advisories] ignore = [ ] task-hookrs-0.9.0/examples/create_task.rs000064400000000000000000000015741046102023000165570ustar 00000000000000extern crate chrono; extern crate serde_json; extern crate task_hookrs; extern crate uuid; use task_hookrs::status::TaskStatus; use task_hookrs::task::Task; use task_hookrs::task::TW26; use task_hookrs::uda::UDA; use chrono::NaiveDateTime; use serde_json::to_string; use uuid::Uuid; fn main() { let uuid = Uuid::nil(); let date = NaiveDateTime::parse_from_str("2016-12-31 12:13:14", "%Y-%m-%d %H:%M:%S").unwrap(); let t: Task = Task::new( Some(12), TaskStatus::Pending, uuid, date.into(), "Test task".to_string(), None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, UDA::default(), ); println!("[{}]", to_string(&t).unwrap()); } task-hookrs-0.9.0/examples/import_task.rs000064400000000000000000000011121046102023000166120ustar 00000000000000extern crate chrono; extern crate serde_json; extern crate task_hookrs; extern crate uuid; use std::io::stdin; use task_hookrs::import::import; use task_hookrs::status::TaskStatus; use task_hookrs::task::Task; fn main() { let mut tasks = import(stdin()).unwrap(); assert_eq!(tasks.len(), 1); let t: Task = tasks.pop().unwrap(); assert_eq!(*t.status(), TaskStatus::Pending); assert_eq!(*t.description(), "Test task".to_owned()); assert_eq!(t.priority(), None); assert_eq!(t.uda().get("priority"), None); println!("Successfully imported:\n{:?}", t); } task-hookrs-0.9.0/rustfmt.toml000064400000000000000000000000121046102023000144710ustar 00000000000000# default task-hookrs-0.9.0/src/annotation.rs000064400000000000000000000024051046102023000154070ustar 00000000000000// // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // //! Module containing types and functions for annotations of tasks use crate::date::Date; /// Annotation type for task annotations. /// Each annotation in taskwarrior consists of a date and a description, /// the date is named "entry", the description "description" in the JSON export. #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)] pub struct Annotation { entry: Date, description: String, } impl Annotation { /// Create a new Annotation object pub fn new(entry: Date, description: String) -> Annotation { Annotation { entry, description } } /// Get the entry date pub fn entry(&self) -> &Date { &self.entry } /// Get the entry date mutable pub fn entry_mut(&mut self) -> &mut Date { &mut self.entry } /// Get the description text pub fn description(&self) -> &String { &self.description } /// Get the description text mutable pub fn description_mut(&mut self) -> &mut String { &mut self.description } } #[cfg(test)] mod test {} task-hookrs-0.9.0/src/date.rs000064400000000000000000000044471046102023000141620ustar 00000000000000// // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // //! Module for wrapping chrono::naive::datetime::NaiveDateTime use std::ops::{Deref, DerefMut}; use chrono::NaiveDateTime; use serde::de::Error as SerdeError; use serde::de::Visitor; use serde::Deserialize; use serde::Deserializer; use serde::Serialize; use serde::Serializer; /// Date is a NaiveDateTime-Wrapper object to be able to implement foreign traits on it #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Date(NaiveDateTime); impl Deref for Date { type Target = NaiveDateTime; fn deref(&self) -> &NaiveDateTime { &self.0 } } impl DerefMut for Date { fn deref_mut(&mut self) -> &mut NaiveDateTime { &mut self.0 } } impl From for Date { fn from(ndt: NaiveDateTime) -> Date { Date(ndt) } } /// The date-time parsing template used to parse the date time data exported by taskwarrior. pub static TASKWARRIOR_DATETIME_TEMPLATE: &str = "%Y%m%dT%H%M%SZ"; impl Serialize for Date { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let formatted = self.0.format(TASKWARRIOR_DATETIME_TEMPLATE); serializer.serialize_str(&format!("{}", formatted)) } } impl<'de> Deserialize<'de> for Date { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct DateVisitor; impl<'v> Visitor<'v> for DateVisitor { type Value = Date; fn expecting(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { write!( fmt, "a string which matches {}", TASKWARRIOR_DATETIME_TEMPLATE ) } fn visit_str(self, value: &str) -> Result where E: SerdeError, { NaiveDateTime::parse_from_str(value, TASKWARRIOR_DATETIME_TEMPLATE) .map(Date) .map_err(|e| SerdeError::custom(e.to_string())) } } deserializer.deserialize_str(DateVisitor) } } task-hookrs-0.9.0/src/error.rs000064400000000000000000000022011046102023000143600ustar 00000000000000// // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // //! Definitions for error handling with failure /// Failure error kind type, defining error messages #[derive(Debug, thiserror::Error)] pub enum Error { /// Error kind indicating that the JSON parser failed #[error("Failed to create a Task from JSON")] ParserError, /// Error kind indicating that the Reader failed to read something #[error("Failed to read tasks from a Reader")] ReaderError, /// Error kind indicating that a call to the task warrior binary failed #[error("There was a problem while calling the external 'task' binary")] TaskCmdError, /// Error kind indicating that a conversion to JSON failed #[error("A Task could not be converted to JSON")] SerializeError, /// Error wrapper for std::io::Error #[error(transparent)] Io(#[from] std::io::Error), /// Error wrapper for serde_json::Error #[error(transparent)] SerdeJson(#[from] serde_json::Error), } task-hookrs-0.9.0/src/import.rs000064400000000000000000000164201046102023000145510ustar 00000000000000// // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // //! Module containing the `import()` function use std::io::BufRead; use std::io::Read; use serde_json; use crate::error::Error; use crate::task::{Task, TaskWarriorVersion}; /// Import taskwarrior-exported JSON. This expects an JSON Array of objects, as exported by /// taskwarrior. pub fn import(r: R) -> Result>, Error> { serde_json::from_reader(r).map_err(Error::from) } /// Import a single JSON-formatted Task pub fn import_task(s: &str) -> Result, Error> { serde_json::from_str(s).map_err(Error::from) } /// Reads line by line and tries to parse a task-object per line. pub fn import_tasks(r: BR) -> Vec, Error>> { let mut vt = Vec::new(); for line in r.lines() { if let Err(err) = line { vt.push(Err(Error::from(err))); continue; } // Unwrap is safe because of continue above if line.as_ref().unwrap().is_empty() { // Empty strings are not usable, and shall be silently ignored continue; } vt.push(import_task(line.unwrap().as_str())); } vt } #[cfg(test)] mod test { use crate::import::{import, import_task, import_tasks}; use crate::task::{Task, TW25, TW26}; #[test] fn test_one_tw25() { let s = r#" [ { "id": 1, "description": "some description", "entry": "20150619T165438Z", "modified": "20160327T164007Z", "project": "someproject", "status": "waiting", "tags": ["some", "tags", "are", "here"], "uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0", "depends": "8ca953d5-18b5-4eb9-bd56-18f2e5b752f0", "wait": "20160508T164007Z", "urgency": 0.583562 } ] "#; let imported = import::(s.as_bytes()); assert!(imported.is_ok()); let imported = imported.unwrap(); assert!(imported.len() == 1); } #[test] fn test_one_tw26() { let s = r#" [ { "id": 1, "description": "some description", "entry": "20150619T165438Z", "modified": "20160327T164007Z", "project": "someproject", "status": "waiting", "tags": ["some", "tags", "are", "here"], "uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0", "depends": ["8ca953d5-18b5-4eb9-bd56-18f2e5b752f0"], "wait": "20160508T164007Z", "urgency": 0.583562 } ] "#; let imported = import::(s.as_bytes()); assert!(imported.is_ok()); let imported = imported.unwrap(); assert!(imported.len() == 1); } #[test] fn test_two_tw25() { let s = r#" [ { "id" : 1, "description" : "test", "entry" : "20150619T165438Z", "modified" : "20160327T164007Z", "project" : "self.software", "status" : "waiting", "tags" : ["check", "this", "crate", "out"], "uuid" : "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0", "wait" : "20160508T164007Z", "urgency" : 0.583562 }, { "id" : 2, "description" : "another test", "entry" : "20150623T181011Z", "modified" : "20160327T163718Z", "priority" : "L", "project" : "studying", "status" : "waiting", "tags" : ["I", "have", "awesome", "kittens"], "uuid" : "54d49ffc-a06b-4dd8-b7d1-db5f50594312", "wait" : "20160508T163718Z", "annotations" : [ { "entry" : "20150623T181018Z", "description" : "fooooooobar" } ], "urgency" : 3.16164 }, { "id" : 3, "description" : "I love kittens, really!", "entry" : "20150919T222323Z", "modified" : "20160327T163718Z", "project" : "getkittens", "status" : "waiting", "tags" : ["kittens", "are", "so", "damn", "awesome"], "uuid" : "08ee8dce-cb97-4c8c-9940-c9a440e90119", "wait" : "20160508T163718Z", "urgency" : 1.07397 } ] "#; assert!(import::(s.as_bytes()).unwrap().len() == 3); } #[test] fn test_one_single_tw25() { use crate::date::Date; use crate::date::TASKWARRIOR_DATETIME_TEMPLATE; use crate::status::TaskStatus; use chrono::NaiveDateTime; use uuid::Uuid; fn mkdate(s: &str) -> Date { let n = NaiveDateTime::parse_from_str(s, TASKWARRIOR_DATETIME_TEMPLATE); Date::from(n.unwrap()) } let s = r#" { "id": 1, "description": "some description", "entry": "20150619T165438Z", "modified": "20160327T164007Z", "project": "someproject", "status": "waiting", "tags": ["some", "tags", "are", "here"], "uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0", "wait": "20160508T164007Z", "urgency": 0.583562 } "#; let imported = import_task(s); assert!(imported.is_ok()); // Check for every information let task: Task = imported.unwrap(); assert_eq!(*task.status(), TaskStatus::Waiting); assert_eq!(task.description(), "some description"); assert_eq!(*task.entry(), mkdate("20150619T165438Z")); assert_eq!( *task.uuid(), Uuid::parse_str("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0").unwrap() ); assert_eq!(task.modified(), Some(&mkdate("20160327T164007Z"))); assert_eq!(task.project(), Some(&String::from("someproject"))); if let Some(tags) = task.tags() { for tag in tags { let any_tag = ["some", "tags", "are", "here"].iter().any(|t| tag == *t); assert!(any_tag, "Tag {} missing", tag); } } else { panic!("Tags completely missing"); } assert_eq!(task.wait(), Some(&mkdate("20160508T164007Z"))); } #[test] fn test_two_single_tw25() { use crate::status::TaskStatus; use std::io::BufReader; let s = r#" {"id":1,"description":"some description","entry":"20150619T165438Z","modified":"20160327T164007Z","project":"someproject","status":"waiting","tags":["some","tags","are","here"],"uuid":"8ca953d5-18b4-4eb9-bd56-18f2e5b752f0","wait":"20160508T164007Z","urgency":0.583562} {"id":1,"description":"some description","entry":"20150619T165438Z","modified":"20160327T164007Z","project":"someproject","status":"waiting","tags":["some","tags","are","here"],"uuid":"8ca953d5-18b4-4eb9-bd56-18f2e5b752f0","wait":"20160508T164007Z","urgency":0.583562}"#; let imported = import_tasks(BufReader::new(s.as_bytes())); assert_eq!(imported.len(), 2); assert!(imported[0].is_ok()); assert!(imported[1].is_ok()); let import0: &Task = imported[0].as_ref().unwrap(); let import1 = imported[1].as_ref().unwrap(); assert_eq!(*import0.status(), TaskStatus::Waiting); assert_eq!(*import1.status(), TaskStatus::Waiting); } } task-hookrs-0.9.0/src/lib.rs000064400000000000000000000024751046102023000140120ustar 00000000000000// // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // //! This crate exports functionality to import and export taskwarrior-compatible JSON by //! translating the JSON into rust types and vice-versa. //! //! For example: //! //! ``` //! use std::io::stdin; //! //! use task_hookrs::task::{Task, TW26}; //! use task_hookrs::import::import; //! //! if let Ok(tasks) = import::(stdin()) { //! for task in tasks { //! println!("Task: {}, entered {:?} is {} -> {}", //! task.uuid(), //! task.entry(), //! task.status(), //! task.description()); //! } //! } //! ``` //! #![deny(missing_docs)] #![deny( dead_code, non_camel_case_types, non_snake_case, path_statements, trivial_numeric_casts, unstable_features, unused_allocation, unused_import_braces, unused_imports, unused_must_use, unused_mut, unused_qualifications, while_true )] pub mod annotation; pub mod date; pub mod error; pub mod import; pub mod priority; pub mod project; pub mod status; pub mod tag; pub mod task; pub mod tw; pub mod uda; pub mod urgency; task-hookrs-0.9.0/src/priority.rs000064400000000000000000000005011046102023000151110ustar 00000000000000// // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // //! Module containing `TaskPriority` type /// type definition for TaskPriority pub type TaskPriority = String; task-hookrs-0.9.0/src/project.rs000064400000000000000000000004601046102023000147020ustar 00000000000000// // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // //! Module containing `Project` type /// Typedef for Project type. pub type Project = String; task-hookrs-0.9.0/src/status.rs000064400000000000000000000024111046102023000145550ustar 00000000000000// // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // //! Module containing `TaskStatus` type and trait impls use std::fmt::{Display, Error as FmtError, Formatter}; /// Enum for status taskwarrior supports. #[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] pub enum TaskStatus { /// Pending status type #[serde(rename = "pending")] Pending, /// Deleted status type #[serde(rename = "deleted")] Deleted, /// Completed status type #[serde(rename = "completed")] Completed, /// Waiting status type #[serde(rename = "waiting")] Waiting, /// Recurring status type #[serde(rename = "recurring")] Recurring, } impl Display for TaskStatus { fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> { match self { TaskStatus::Pending => write!(fmt, "Pending"), TaskStatus::Deleted => write!(fmt, "Deleted"), TaskStatus::Completed => write!(fmt, "Completed"), TaskStatus::Waiting => write!(fmt, "Waiting"), TaskStatus::Recurring => write!(fmt, "Recurring"), } } } task-hookrs-0.9.0/src/tag.rs000064400000000000000000000004471046102023000140140ustar 00000000000000// // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // //! Module containing `Tag` type /// type definition for tags pub type Tag = String; task-hookrs-0.9.0/src/task.rs000064400000000000000000001021021046102023000141720ustar 00000000000000// // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // //! Module containing `Task` type as well as trait implementations use std::marker::PhantomData; use std::result::Result as RResult; use chrono::Utc; use serde::{de, Deserialize, Deserializer}; use serde::{Serialize, Serializer}; use uuid::Uuid; use crate::annotation::Annotation; use crate::date::Date; use crate::priority::TaskPriority; use crate::project::Project; use crate::status::TaskStatus; use crate::tag::Tag; use crate::uda::UDA; use crate::urgency::Urgency; /// Unit struct used to represent taskwarrior format 2.6.0 and newer. /// See [Task] for more information. #[derive(Debug, Clone)] pub struct TW26; /// Unit struct used to represent taskwarrior format 2.5.3 and older. /// See [Task] for more information. #[derive(Debug, Clone)] pub struct TW25; // Prevents folks outside this crate from implementing their own versions mod private { pub trait Sealed {} impl Sealed for super::TW26 {} impl Sealed for super::TW25 {} } /// Trait used to represent taskwarrior version types pub trait TaskWarriorVersion: private::Sealed {} impl TaskWarriorVersion for TW26 {} impl TaskWarriorVersion for TW25 {} /// Task type /// /// A task must have four things: /// /// - A Status /// - An UUID /// - An Entry-Date /// - A Description /// /// all other Data is optional by taskwarrior. This type is a simple rust representation of the /// JSON exported by taskwarrior. /// /// For further explanations of the fields please consult the documentation on https://taskwarrior.org/ /// /// It is deserializeable and serializeable via serde_json, so importing and exporting taskwarrior /// tasks is simply serializing and deserializing objects of this type. /// /// As of taskwarrior version 2.6.0 and newer, the representation of `depends` has changed from /// being a comma seperated string of uuid's to being a proper json array. You can select which /// behaviour you want at compiletime by providing either [TW26] (the default) or [TW25] to `Task` as its /// type parameter. #[derive(Debug, Clone, PartialEq, derive_builder::Builder, Serialize, Deserialize)] #[builder(setter(into))] pub struct Task { /// The temporary assigned task id #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] id: Option, /// The status of the task #[builder(default = "TaskStatus::Pending")] status: TaskStatus, /// The uuid which identifies the task and is important for syncing #[builder(default = "Uuid::new_v4()")] uuid: Uuid, /// The entry date, when this task was created #[builder(default = "Date::from(Utc::now().naive_utc())")] entry: Date, /// The description of the task (i.e. its main content) /// This field is the only mandatory field, when using the TaskBuilder. description: String, /// A list of annotations with timestamps #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] annotations: Option>, /// The uuids of other tasks which have to be completed before this one becomes unblocked. #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] #[serde(serialize_with = "serialize_depends::<_, Version>")] #[serde(deserialize_with = "deserialize_depends::<_, Version>", default)] depends: Option>, /// The due date of the task #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] due: Option, /// When the task was last deleted or completed #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] end: Option, /// The imask is used internally for recurrence #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] imask: Option, /// The mask is used internally for recurrence #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] mask: Option, /// When the task was last modified #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] modified: Option, /// A task can have a parent task #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] parent: Option, /// The priority of the task #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] priority: Option, /// A task can be part of a project. Typically of the form "project.subproject.subsubproject" #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] project: Option, /// The timespan after which this task should recur #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] recur: Option, /// When the task becomes ready #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] scheduled: Option, /// When the task becomes active #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] start: Option, /// The tags associated with the task #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] tags: Option>, /// When the recurrence stops #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] until: Option, /// This hides the task until the wait date #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] wait: Option, /// This contains the urgency of the task #[builder(default)] #[serde(skip_serializing_if = "Option::is_none")] urgency: Option, /// A map of user defined attributes #[builder(default)] #[serde(default)] #[serde(skip_serializing_if = "UDA::is_empty")] #[serde(flatten)] uda: UDA, #[doc(hidden)] #[builder(setter(skip))] #[serde(skip)] _version: PhantomData, } /* * TODO: We do not fail if the JSON parsing fails. This panics. We rely on taskwarrior to be nice * to us. I guess this should be fixed. */ impl Task { /// Create a new Task instance #[allow(clippy::too_many_arguments)] pub fn new( id: Option, status: TaskStatus, uuid: Uuid, entry: Date, description: String, annotations: Option>, depends: Option>, due: Option, end: Option, imask: Option, mask: Option, modified: Option, parent: Option, priority: Option, project: Option, recur: Option, scheduled: Option, start: Option, tags: Option>, until: Option, wait: Option, urgency: Option, uda: UDA, ) -> Task { Task { id, status, uuid, entry, description, annotations, depends, due, end, imask, mask, modified, parent, priority, project, recur, scheduled, start, tags, until, wait, urgency, uda, _version: PhantomData, } } /// Get the id of the task pub fn id(&self) -> Option { self.id } /// Get the status of the task pub fn status(&self) -> &TaskStatus { &self.status } /// Get the status of the task mutable pub fn status_mut(&mut self) -> &mut TaskStatus { &mut self.status } /// Get the uuid of the task pub fn uuid(&self) -> &Uuid { &self.uuid } /// Get the uuid of the task mutable pub fn uuid_mut(&mut self) -> &mut Uuid { &mut self.uuid } /// Get the entry date of the task pub fn entry(&self) -> &Date { &self.entry } /// Get the entry date of the task mutable pub fn entry_mut(&mut self) -> &mut Date { &mut self.entry } /// Get the description of the task pub fn description(&self) -> &String { &self.description } /// Get the description of the task mutable pub fn description_mut(&mut self) -> &mut String { &mut self.description } /// Get the annotations of the task pub fn annotations(&self) -> Option<&Vec> { self.annotations.as_ref() } /// Get the annotations of the task mutable pub fn annotations_mut(&mut self) -> Option<&mut Vec> { self.annotations.as_mut() } /// Set annotations pub fn set_annotations(&mut self, new: Option) where T: IntoIterator, T::Item: Into, { self.annotations = new.map(|x| x.into_iter().map(Into::into).collect()); } /// Get the dependencies of the task pub fn depends(&self) -> Option<&Vec> { self.depends.as_ref() } /// Get the dependencies of the task mutable pub fn depends_mut(&mut self) -> Option<&mut Vec> { self.depends.as_mut() } /// Set depends pub fn set_depends(&mut self, new: Option) where T: IntoIterator, T::Item: Into, { self.depends = new.map(|x| x.into_iter().map(Into::into).collect()); } /// Get the due date of the task pub fn due(&self) -> Option<&Date> { self.due.as_ref() } /// Get the due date of the task mutable pub fn due_mut(&mut self) -> Option<&mut Date> { self.due.as_mut() } /// Set due pub fn set_due(&mut self, new: Option) where T: Into, { self.due = new.map(Into::into) } /// Get the end date of the task pub fn end(&self) -> Option<&Date> { self.end.as_ref() } /// Get the end date of the task mutable pub fn end_mut(&mut self) -> Option<&mut Date> { self.end.as_mut() } /// Set end pub fn set_end(&mut self, new: Option) where T: Into, { self.end = new.map(Into::into) } /// Get the imask of the task pub fn imask(&self) -> Option<&f64> { self.imask.as_ref() } /// Get the imask of the task mutable pub fn imask_mut(&mut self) -> Option<&mut f64> { self.imask.as_mut() } /// Set imask pub fn set_imask(&mut self, new: Option) where T: Into, { self.imask = new.map(Into::into) } /// Get the mask of the task pub fn mask(&self) -> Option<&String> { self.mask.as_ref() } /// Get the mask of the task mutable pub fn mask_mut(&mut self) -> Option<&mut String> { self.mask.as_mut() } /// Set mask pub fn set_mask(&mut self, new: Option) where T: Into, { self.mask = new.map(Into::into) } /// Get the modified date of the task pub fn modified(&self) -> Option<&Date> { self.modified.as_ref() } /// Get the modified date of the task mutable pub fn modified_mut(&mut self) -> Option<&mut Date> { self.modified.as_mut() } /// Set modified pub fn set_modified(&mut self, new: Option) where T: Into, { self.modified = new.map(Into::into) } /// Get the parent of the task pub fn parent(&self) -> Option<&Uuid> { self.parent.as_ref() } /// Get the parent of the task mutable pub fn parent_mut(&mut self) -> Option<&mut Uuid> { self.parent.as_mut() } /// Set parent pub fn set_parent(&mut self, new: Option) where T: Into, { self.parent = new.map(Into::into) } /// Get the priority of the task pub fn priority(&self) -> Option<&TaskPriority> { self.priority.as_ref() } /// Get the priority of the task mutable pub fn priority_mut(&mut self) -> Option<&mut TaskPriority> { self.priority.as_mut() } /// Set priority pub fn set_priority(&mut self, new: Option) where T: Into, { self.priority = new.map(Into::into) } /// Get the project of the task pub fn project(&self) -> Option<&Project> { self.project.as_ref() } /// Get the project of the task mutable pub fn project_mut(&mut self) -> Option<&mut Project> { self.project.as_mut() } /// Set project pub fn set_project(&mut self, new: Option) where T: Into, { self.project = new.map(Into::into) } /// Get the recur of the task /// /// This is exported as String by now. This might change in future versions of this crate. pub fn recur(&self) -> Option<&String> { self.recur.as_ref() } /// This is exported as String by now. This might change in future versions of this crate. /// mutable pub fn recur_mut(&mut self) -> Option<&mut String> { self.recur.as_mut() } /// Set recur pub fn set_recur(&mut self, new: Option) where T: Into, { self.recur = new.map(Into::into) } /// Get the scheduled date of the task pub fn scheduled(&self) -> Option<&Date> { self.scheduled.as_ref() } /// Get the scheduled date of the task mutable pub fn scheduled_mut(&mut self) -> Option<&mut Date> { self.scheduled.as_mut() } /// Set scheduled pub fn set_scheduled(&mut self, new: Option) where T: Into, { self.scheduled = new.map(Into::into) } /// Get the start date of the task pub fn start(&self) -> Option<&Date> { self.start.as_ref() } /// Get the start date of the task mutable pub fn start_mut(&mut self) -> Option<&mut Date> { self.start.as_mut() } /// Set start pub fn set_start(&mut self, new: Option) where T: Into, { self.start = new.map(Into::into) } /// Get the tags of the task pub fn tags(&self) -> Option<&Vec> { self.tags.as_ref() } /// Get the tags of the task mutable pub fn tags_mut(&mut self) -> Option<&mut Vec> { self.tags.as_mut() } /// Set tags pub fn set_tags(&mut self, new: Option) where T: IntoIterator, T::Item: Into, { self.tags = new.map(|x| x.into_iter().map(Into::into).collect()); } /// Get the until date of the task pub fn until(&self) -> Option<&Date> { self.until.as_ref() } /// Get the until date of the task mutable pub fn until_mut(&mut self) -> Option<&mut Date> { self.until.as_mut() } /// Set until pub fn set_until(&mut self, new: Option) where T: Into, { self.until = new.map(Into::into); } /// Get the urgency of the task pub fn urgency(&self) -> Option<&Urgency> { self.urgency.as_ref() } /// Get the urgency of the task pub fn urgency_mut(&mut self) -> Option<&mut Urgency> { self.urgency.as_mut() } /// Set the urgency of the task pub fn set_urgency(&mut self, new: Option) where T: Into, { self.urgency = new.map(Into::into); } /// Get the wait date of the task pub fn wait(&self) -> Option<&Date> { self.wait.as_ref() } /// Get the wait date of the task mutable pub fn wait_mut(&mut self) -> Option<&mut Date> { self.wait.as_mut() } /// Set wait pub fn set_wait(&mut self, new: Option) where T: Into, { self.wait = new.map(Into::into); } /// Get the BTreeMap that contains the UDA pub fn uda(&self) -> &UDA { &self.uda } /// Get the BTreeMap that contains the UDA mutable pub fn uda_mut(&mut self) -> &mut UDA { &mut self.uda } } fn serialize_depends( field: &Option>, serializer: S, ) -> RResult where S: Serializer, { if std::any::TypeId::of::() == std::any::TypeId::of::() { let value = field.as_ref().unwrap(); let v: Vec = value.iter().map(Uuid::to_string).collect(); serializer.serialize_str(&v.join(",")) } else { field.serialize(serializer) } } fn deserialize_depends<'de, D, T: 'static>(deserializer: D) -> RResult>, D::Error> where D: Deserializer<'de>, { if std::any::TypeId::of::() == std::any::TypeId::of::() { let raw: String = String::deserialize(deserializer)?; let mut uuids = vec![]; for uuid in raw.split(',') { uuids.push(Uuid::parse_str(uuid).map_err(de::Error::custom)?); } Ok(Some(uuids)) } else { let value: Option> = Option::deserialize(deserializer)?; Ok(value) } } #[cfg(test)] mod test { use crate::annotation::Annotation; use crate::date::Date; use crate::date::TASKWARRIOR_DATETIME_TEMPLATE; use crate::status::TaskStatus; use crate::task::{Task, TW25, TW26}; use crate::uda::UDAValue; use chrono::NaiveDateTime; use serde_json; use uuid::{uuid, Uuid}; fn mklogger() { env_logger::init(); log::debug!("Env-logger enabled"); } fn mkdate(s: &str) -> Date { let n = NaiveDateTime::parse_from_str(s, TASKWARRIOR_DATETIME_TEMPLATE); Date::from(n.unwrap()) } #[test] fn test_deser() { let s = r#"{ "id": 1, "description": "test", "entry": "20150619T165438Z", "status": "waiting", "uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0", "urgency": 5.3 }"#; println!("{}", s); let task = serde_json::from_str(s); println!("{:?}", task); assert!(task.is_ok()); let task: Task = task.unwrap(); assert_eq!(*task.status(), TaskStatus::Waiting); assert_eq!(task.description(), "test"); assert_eq!(*task.entry(), mkdate("20150619T165438Z")); assert_eq!( *task.uuid(), Uuid::parse_str("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0").unwrap() ); assert_eq!(task.urgency(), Some(&5.3)); let back = serde_json::to_string(&task).unwrap(); assert!(back.contains("description")); assert!(back.contains("test")); assert!(back.contains("entry")); assert!(back.contains("20150619T165438Z")); assert!(back.contains("status")); assert!(back.contains("waiting")); assert!(back.contains("uuid")); assert!(back.contains("urgency")); assert!(back.contains("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0")); } #[test] fn test_deser_more_tw26() { let s = r#"{ "id": 1, "description": "some description", "entry": "20150619T165438Z", "modified": "20160327T164007Z", "project": "someproject", "status": "waiting", "tags": ["some", "tags", "are", "here"], "uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0", "depends": ["8ca953d5-18b4-4eb9-bd56-18f2e5b752f0","5a04bb1e-3f4b-49fb-b9ba-44407ca223b5"], "wait": "20160508T164007Z", "urgency": 0.583562 }"#; let task = serde_json::from_str(s); assert!(task.is_ok()); let task: Task = task.unwrap(); assert_eq!(*task.status(), TaskStatus::Waiting); assert_eq!(task.description(), "some description"); assert_eq!(*task.entry(), mkdate("20150619T165438Z")); assert_eq!( *task.uuid(), Uuid::parse_str("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0").unwrap() ); assert_eq!(task.urgency(), Some(&0.583562)); assert_eq!(task.modified(), Some(&mkdate("20160327T164007Z"))); assert_eq!(task.project(), Some(&String::from("someproject"))); if let Some(tags) = task.tags() { for tag in tags { let any_tag = ["some", "tags", "are", "here"].iter().any(|t| tag == *t); assert!(any_tag, "Tag {} missing", tag); } } else { panic!("Tags completely missing"); } assert_eq!(task.wait(), Some(&mkdate("20160508T164007Z"))); if let Some(depends) = task.depends() { assert_eq!(depends.len(), 2); assert!(depends.contains(&uuid!("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0"))); assert!(depends.contains(&uuid!("5a04bb1e-3f4b-49fb-b9ba-44407ca223b5"))); } else { panic!("Depends completely missing"); } assert_eq!(task.wait(), Some(&mkdate("20160508T164007Z"))); let back = serde_json::to_string(&task).unwrap(); assert!(back.contains("description")); assert!(back.contains("some description")); assert!(back.contains("entry")); assert!(back.contains("20150619T165438Z")); assert!(back.contains("project")); assert!(back.contains("someproject")); assert!(back.contains("status")); assert!(back.contains("waiting")); assert!(back.contains("tags")); assert!(back.contains("some")); assert!(back.contains("tags")); assert!(back.contains("are")); assert!(back.contains("here")); assert!(back.contains("uuid")); assert!(back.contains("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0")); assert!(back.contains( r#"["8ca953d5-18b4-4eb9-bd56-18f2e5b752f0","5a04bb1e-3f4b-49fb-b9ba-44407ca223b5"]"#, )); } #[test] fn test_deser_more_tw25() { mklogger(); let s = r#"{ "id": 1, "description": "some description", "entry": "20150619T165438Z", "modified": "20160327T164007Z", "project": "someproject", "status": "waiting", "tags": ["some", "tags", "are", "here"], "uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0", "depends": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0,5a04bb1e-3f4b-49fb-b9ba-44407ca223b5", "wait": "20160508T164007Z", "urgency": 0.583562 }"#; println!("{}", s); let task = serde_json::from_str(s); println!("{:?}", task); assert!(task.is_ok()); let task: Task = task.unwrap(); assert_eq!(*task.status(), TaskStatus::Waiting); assert_eq!(task.description(), "some description"); assert_eq!(*task.entry(), mkdate("20150619T165438Z")); assert_eq!( *task.uuid(), Uuid::parse_str("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0").unwrap() ); assert_eq!(task.urgency(), Some(&0.583562)); assert_eq!(task.modified(), Some(&mkdate("20160327T164007Z"))); assert_eq!(task.project(), Some(&String::from("someproject"))); if let Some(tags) = task.tags() { for tag in tags { let any_tag = ["some", "tags", "are", "here"].iter().any(|t| tag == *t); assert!(any_tag, "Tag {} missing", tag); } } else { panic!("Tags completely missing"); } if let Some(depends) = task.depends() { assert_eq!(depends.len(), 2); assert!(depends.contains(&uuid!("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0"))); assert!(depends.contains(&uuid!("5a04bb1e-3f4b-49fb-b9ba-44407ca223b5"))); } else { panic!("Depends completely missing"); } assert_eq!(task.wait(), Some(&mkdate("20160508T164007Z"))); let back = serde_json::to_string(&task).unwrap(); assert!(back.contains("description")); assert!(back.contains("some description")); assert!(back.contains("entry")); assert!(back.contains("20150619T165438Z")); assert!(back.contains("project")); assert!(back.contains("someproject")); assert!(back.contains("status")); assert!(back.contains("waiting")); assert!(back.contains("tags")); assert!(back.contains("some")); assert!(back.contains("tags")); assert!(back.contains("are")); assert!(back.contains("here")); assert!(back.contains("uuid")); assert!(back.contains("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0")); assert!( back.contains( "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0,5a04bb1e-3f4b-49fb-b9ba-44407ca223b5", ) ); } #[test] fn test_deser_annotation() { let s = r#"{ "id":192, "description":"Some long description for a task", "entry":"20160423T125820Z", "modified":"20160423T125942Z", "project":"project", "status":"pending", "tags":["search","things"], "uuid":"5a04bb1e-3f4b-49fb-b9ba-44407ca223b5", "annotations":[{"entry":"20160423T125911Z","description":"An Annotation"}, {"entry":"20160423T125926Z","description":"Another Annotation"}, {"entry":"20160422T125942Z","description":"A Third Anno"} ], "urgency": -5 }"#; println!("{}", s); let task = serde_json::from_str(s); println!("{:?}", task); assert!(task.is_ok()); let task: Task = task.unwrap(); assert_eq!(task.urgency(), Some(&-5.0)); let all_annotations = vec![ Annotation::new(mkdate("20160423T125911Z"), String::from("An Annotation")), Annotation::new( mkdate("20160423T125926Z"), String::from("Another Annotation"), ), Annotation::new(mkdate("20160422T125942Z"), String::from("A Third Anno")), ]; if let Some(annotations) = task.annotations() { for annotation in annotations { let r = all_annotations.iter().any(|ann| { let descr = ann.description() == annotation.description(); let entry = ann.entry() == annotation.entry(); descr && entry }); assert!(r, "Annotation {:?} missing or buggy", annotation); } } else { panic!("Annotations missing"); } } #[test] fn test_uda() { let s = r#"{ "description":"Some long description for a task", "entry":"20160423T125820Z", "modified":"20160423T125942Z", "project":"project", "status":"pending", "uuid":"5a04bb1e-3f4b-49fb-b9ba-44407ca223b5", "test_str_uda":"test_str_uda_value", "test_float_uda":-17.1234, "test_int_uda":1234 }"#; println!("{}", s); let task = serde_json::from_str(s); println!("{:?}", task); assert!(task.is_ok()); let task: Task = task.unwrap(); let str_uda = task.uda().get(&"test_str_uda".to_owned()); assert!(str_uda.is_some()); let str_uda = str_uda.unwrap(); assert_eq!(str_uda, &UDAValue::Str("test_str_uda_value".to_owned())); let float_uda = task.uda().get(&"test_float_uda".to_owned()); assert!(float_uda.is_some()); let float_uda = float_uda.unwrap(); assert_eq!(float_uda, &UDAValue::F64(-17.1234)); let int_uda = task.uda().get(&"test_int_uda".to_owned()); assert!(int_uda.is_some()); let int_uda = int_uda.unwrap(); assert_eq!(int_uda, &UDAValue::U64(1234)); let back = serde_json::to_string_pretty(&task); assert!(back.is_ok()); let back = back.unwrap(); println!("{}", back); assert!(back.contains("description")); assert!(back.contains("Some long description for a task")); assert!(back.contains("entry")); assert!(back.contains("20160423T125820Z")); assert!(back.contains("project")); assert!(back.contains("status")); assert!(back.contains("pending")); assert!(back.contains("uuid")); assert!(back.contains("5a04bb1e-3f4b-49fb-b9ba-44407ca223b5")); assert!(back.contains("test_str_uda")); assert!(back.contains("test_str_uda_value")); assert!(back.contains("test_float_uda")); assert!(back.contains("-17.1234")); assert!(back.contains("test_int_uda")); assert!(back.contains("1234")); } #[test] fn test_priority() { let s = r#"{ "id":9, "description":"Some long description for a task", "entry":"20201021T065503Z", "estimate":"30", "modified":"20210213T233603Z", "priority":"U", "status":"pending", "uuid":"6c4c9ee8-d6c4-4d64-a84d-bf9cb710684e", "urgency":23 }"#; println!("{}", s); let task = serde_json::from_str(s); println!("{:?}", task); assert!(task.is_ok()); let task: Task = task.unwrap(); if let Some(priority) = task.priority() { assert_eq!(*priority, "U".to_string()); } else { panic!("Priority completely missing"); } let back = serde_json::to_string_pretty(&task); assert!(back.is_ok()); let back = back.unwrap(); println!("{}", back); assert!(back.contains("description")); assert!(back.contains("Some long description for a task")); assert!(back.contains("entry")); assert!(back.contains("20201021T065503Z")); assert!(back.contains("priority")); assert!(back.contains("status")); assert!(back.contains("pending")); assert!(back.contains("uuid")); assert!(back.contains("6c4c9ee8-d6c4-4d64-a84d-bf9cb710684e")); } #[test] fn test_builder_simple() { use crate::task::TaskBuilder; let t = TaskBuilder::::default() .description("test") .entry(mkdate("20150619T165438Z")) .build(); println!("{:?}", t); assert!(t.is_ok()); let t = t.unwrap(); assert_eq!(t.status(), &TaskStatus::Pending); assert_eq!(t.description(), "test"); assert_eq!(t.entry(), &mkdate("20150619T165438Z")); } #[test] fn test_builder_extensive() { use crate::task::TaskBuilder; use crate::task::TW25; use crate::uda::{UDAValue, UDA}; let mut uda = UDA::new(); uda.insert( "test_str_uda".into(), UDAValue::Str("test_str_uda_value".into()), ); uda.insert("test_int_uda".into(), UDAValue::U64(1234)); uda.insert("test_float_uda".into(), UDAValue::F64(-17.1234)); let t = TaskBuilder::::default() .description("test") .entry(mkdate("20150619T165438Z")) .id(192) .modified(mkdate("20160423T125942Z")) .project("project".to_owned()) .tags(vec!["search".to_owned(), "things".to_owned()]) .uda(uda) .build(); println!("{:?}", t); assert!(t.is_ok()); let t = t.unwrap(); assert!(t.id().is_some()); assert_eq!(t.id().unwrap(), 192); assert_eq!(t.status(), &TaskStatus::Pending); assert_eq!(t.description(), "test"); assert_eq!(t.entry(), &mkdate("20150619T165438Z")); assert!(t.modified().is_some()); assert_eq!(t.modified().unwrap(), &mkdate("20160423T125942Z")); assert!(t.project().is_some()); assert_eq!(t.project().unwrap(), "project"); assert!(t.tags().is_some()); assert!(t.urgency().is_none()); assert_eq!( t.tags().unwrap(), &vec!["search".to_owned(), "things".to_owned()] ); } #[test] fn test_builder_defaults() { use crate::task::TaskBuilder; assert!(TaskBuilder::::default() .description("Nice Task") .build() .is_ok()); } #[test] fn test_builder_fail() { use crate::task::TaskBuilder; assert!(TaskBuilder::::default().build().is_err()); } const FIELD_NAMES_TO_NOT_SERIALIZE: [&str; 20] = [ r#""id":"#, r#"""annotations:""#, r#""depends:""#, r#""due:""#, r#""end:""#, r#""imask:""#, r#""mask:""#, r#""modified:""#, r#""parent:""#, r#""priority:""#, r#""project:""#, r#""recur:""#, r#""scheduled:""#, r#""start:""#, r#""tags:""#, r#""until:""#, r#""wait:""#, r#""urgency:""#, r#""uda:""#, r#""_version:""#, ]; #[test] fn test_null_fields_not_serialized_tw25() { use crate::task::TaskBuilder; let task = TaskBuilder::::default() .description("Test Task") .build() .expect("Task to be built"); let task_as_str = serde_json::to_string_pretty(&task).expect("Task serialized as string"); for field_name in FIELD_NAMES_TO_NOT_SERIALIZE { assert!( !task_as_str.contains(field_name), "'{}' should not have been in {}", field_name, task_as_str ); } } #[test] fn test_null_fields_not_serialized_tw26() { use crate::task::TaskBuilder; let task = TaskBuilder::::default() .description("Test Task") .build() .expect("Task to be built"); let task_as_str = serde_json::to_string_pretty(&task).expect("Task serialized as string"); for field_name in FIELD_NAMES_TO_NOT_SERIALIZE { assert!( !task_as_str.contains(field_name), "'{}' should not have been in {}", field_name, task_as_str ); } } } task-hookrs-0.9.0/src/tw.rs000064400000000000000000000051341046102023000136710ustar 00000000000000// // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // //! This module offers functions to interact with taskwarrior. This will expect the `task` binary //! in your path. This will always call task and never interact with your `.task` directory itself. //! (This is in accordance with the taskwarrior api guide lines.) use crate::error::Error; use crate::import::import; use crate::task::Task; use std::io::Write; use std::iter::once; use std::process::{Child, Command, Stdio}; use serde_json; /// This will give you all tasks which match the given query in the taskwarrior query syntax. /// This is not sanitized. Never get the query string from an untrusted user. pub fn query(query: &str) -> Result, Error> { let mut cmd = add_query_to_cmd(query, Command::new("task")); cmd.stdout(Stdio::piped()); run_query_cmd(cmd) } /// This will take a Command, and append the given query string splited at whitespace followed by /// the "export" command to the arguments of the Command. pub fn add_query_to_cmd(query: &str, mut cmd: Command) -> Command { for x in query.split_whitespace().chain(once("export")) { cmd.arg(x); } cmd } /// This executes the given Command and trys to convert the Result into a Vec. pub fn run_query_cmd(mut cmd: Command) -> Result, Error> { let mut export = cmd.spawn()?; export.wait()?; import(export.stdout.ok_or(Error::TaskCmdError)?) } /// This function runs the given Command, pipes the tasks as JSON to it and returns a handle to the child process. pub fn save_to_cmd(tasks: Vec<&'_ Task>, mut cmd: Command) -> Result { let input_buffer = serde_json::to_string(&tasks)?; let mut import = cmd.spawn()?; import .stdin .as_mut() .ok_or(Error::TaskCmdError)? .write_all(input_buffer.as_bytes())?; Ok(import) } /// This will save the given tasks to taskwarrior. Call with `Some(&task)` if you just have one /// task. /// This will block until the save was successful. pub fn save<'a, T>(tasks: T) -> Result<(), Error> where T: IntoIterator, { save_async(tasks)?.wait()?; Ok(()) } /// This function returns the handle to a child process which saves the given tasks. pub fn save_async<'a, T>(tasks: T) -> Result where T: IntoIterator, { let mut cmd = Command::new("task"); cmd.arg("import").stdin(Stdio::piped()); save_to_cmd(tasks.into_iter().collect(), cmd) } task-hookrs-0.9.0/src/uda.rs000064400000000000000000000040071046102023000140060ustar 00000000000000//! Module containing the types for User Defined Attributes (UDA) use std::collections::BTreeMap; use std::fmt; use std::result::Result as RResult; use serde::de; use serde::de::Visitor; use serde::Deserialize; use serde::Deserializer; use serde::Serialize; use serde::Serializer; /// The name of a UDA is just a string. pub type UDAName = String; /// A UDA can have different value types. #[derive(Clone, Debug, PartialEq, PartialOrd)] pub enum UDAValue { /// UDA is a string Str(String), /// UDA is an integer U64(u64), /// UDA is a float F64(f64), } impl Serialize for UDAValue { fn serialize(&self, serializer: S) -> RResult where S: Serializer, { match self { UDAValue::Str(ref s) => s.serialize(serializer), UDAValue::U64(s) => s.serialize(serializer), UDAValue::F64(s) => s.serialize(serializer), } } } struct UDAVisitor; impl<'de> Visitor<'de> for UDAVisitor { type Value = UDAValue; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("an UDA value like a string, float or int") } fn visit_u64(self, value: u64) -> Result where E: de::Error, { Ok(UDAValue::U64(value)) } fn visit_f64(self, value: f64) -> Result where E: de::Error, { Ok(UDAValue::F64(value)) } fn visit_str(self, value: &str) -> Result where E: de::Error, { Ok(UDAValue::Str(value.to_owned())) } } impl<'de> Deserialize<'de> for UDAValue { fn deserialize(deserializer: D) -> RResult where D: Deserializer<'de>, { deserializer.deserialize_any(UDAVisitor) } } /// The UDA Type is just a BTreeMap in which all fields of a task are saved, /// which are not part of the taskwarrior standard. (This makes them user defined attributes.) pub type UDA = BTreeMap; task-hookrs-0.9.0/src/urgency.rs000064400000000000000000000004571046102023000147160ustar 00000000000000// // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // //! Module containing `Urgency` type /// type definition for Urgency pub type Urgency = f64;