From 779a3310034db1761c113fa2d8a9dba7ea05b0ae Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 23 Nov 2020 14:08:42 -0500 Subject: [PATCH 1/6] reorganize into separate crates - taskchampion -- core implementation of a replica - taskchampion-cli -- command-line interface - taskchampion-sync-server -- server implementation (not much yet!) --- Cargo.lock | 316 +++++++++++------- Cargo.toml | 24 +- cli/Cargo.toml | 10 + src/bin/task.rs => cli/src/main.rs | 1 - sync-server/Cargo.toml | 9 + src/server.rs => sync-server/src/lib.rs | 0 taskchampion/Cargo.toml | 18 + README.md => taskchampion/README.md | 0 {docs => taskchampion/docs}/.gitignore | 0 {docs => taskchampion/docs}/README.md | 0 {docs => taskchampion/docs}/book.toml | 0 {docs => taskchampion/docs}/src/SUMMARY.md | 0 {docs => taskchampion/docs}/src/data-model.md | 0 .../docs}/src/installation.md | 0 {docs => taskchampion/docs}/src/plans.md | 0 {docs => taskchampion/docs}/src/storage.md | 0 {docs => taskchampion/docs}/src/sync.md | 0 {docs => taskchampion/docs}/src/taskdb.md | 0 {docs => taskchampion/docs}/src/tasks.md | 0 {docs => taskchampion/docs}/src/usage.md | 0 {src => taskchampion/src}/errors.rs | 0 {src => taskchampion/src}/lib.rs | 3 +- {src => taskchampion/src}/operation.rs | 0 {src => taskchampion/src}/replica.rs | 0 taskchampion/src/server.rs | 20 ++ {src => taskchampion/src}/task.rs | 0 {src => taskchampion/src}/taskdb.rs | 2 +- .../src}/taskstorage/inmemory.rs | 0 {src => taskchampion/src}/taskstorage/kv.rs | 0 {src => taskchampion/src}/taskstorage/mod.rs | 0 .../tests}/data/tdb2-test.data | 0 .../tests}/operation_transform_invariant.rs | 0 taskchampion/tests/shared/mod.rs | 3 + taskchampion/tests/shared/testserver.rs | 81 +++++ {tests => taskchampion/tests}/sync.rs | 9 +- .../tests}/sync_action_sequences.rs | 7 +- 36 files changed, 349 insertions(+), 154 deletions(-) create mode 100644 cli/Cargo.toml rename src/bin/task.rs => cli/src/main.rs (99%) create mode 100644 sync-server/Cargo.toml rename src/server.rs => sync-server/src/lib.rs (100%) create mode 100644 taskchampion/Cargo.toml rename README.md => taskchampion/README.md (100%) rename {docs => taskchampion/docs}/.gitignore (100%) rename {docs => taskchampion/docs}/README.md (100%) rename {docs => taskchampion/docs}/book.toml (100%) rename {docs => taskchampion/docs}/src/SUMMARY.md (100%) rename {docs => taskchampion/docs}/src/data-model.md (100%) rename {docs => taskchampion/docs}/src/installation.md (100%) rename {docs => taskchampion/docs}/src/plans.md (100%) rename {docs => taskchampion/docs}/src/storage.md (100%) rename {docs => taskchampion/docs}/src/sync.md (100%) rename {docs => taskchampion/docs}/src/taskdb.md (100%) rename {docs => taskchampion/docs}/src/tasks.md (100%) rename {docs => taskchampion/docs}/src/usage.md (100%) rename {src => taskchampion/src}/errors.rs (100%) rename {src => taskchampion/src}/lib.rs (91%) rename {src => taskchampion/src}/operation.rs (100%) rename {src => taskchampion/src}/replica.rs (100%) create mode 100644 taskchampion/src/server.rs rename {src => taskchampion/src}/task.rs (100%) rename {src => taskchampion/src}/taskdb.rs (99%) rename {src => taskchampion/src}/taskstorage/inmemory.rs (100%) rename {src => taskchampion/src}/taskstorage/kv.rs (100%) rename {src => taskchampion/src}/taskstorage/mod.rs (100%) rename {tests => taskchampion/tests}/data/tdb2-test.data (100%) rename {tests => taskchampion/tests}/operation_transform_invariant.rs (100%) create mode 100644 taskchampion/tests/shared/mod.rs create mode 100644 taskchampion/tests/shared/testserver.rs rename {tests => taskchampion/tests}/sync.rs (95%) rename {tests => taskchampion/tests}/sync_action_sequences.rs (94%) diff --git a/Cargo.lock b/Cargo.lock index 61a97dabe..e20743939 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,20 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "addr2line" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c0929d69e78dd9bf5408269919fcbcaeb2e35e5d43e5815517cdc6a8e11a423" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" + [[package]] name = "ansi_term" version = "0.11.0" @@ -11,10 +26,11 @@ dependencies = [ [[package]] name = "atty" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ + "hermit-abi", "libc", "winapi", ] @@ -26,41 +42,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" [[package]] -name = "backtrace" -version = "0.3.40" +name = "autocfg" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "backtrace" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598" dependencies = [ - "backtrace-sys", - "cfg-if", + "addr2line", + "cfg-if 1.0.0", "libc", + "miniz_oxide", + "object", "rustc-demangle", ] -[[package]] -name = "backtrace-sys" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "bit-set" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e84c238982c4b1e1ee668d136c510c67a13465279c0cb367ea6baf6310620a80" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" -version = "0.5.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb" +checksum = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3" [[package]] name = "bitflags" @@ -70,24 +84,15 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "byteorder" -version = "1.3.2" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" - -[[package]] -name = "c2-chacha" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" -dependencies = [ - "ppv-lite86", -] +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "cc" -version = "1.0.48" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76" +checksum = "95752358c8f7552394baf48cd82695b345628ad3f170d607de3ca03b8dacca15" [[package]] name = "cfg-if" @@ -96,22 +101,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] -name = "chrono" -version = "0.4.10" +name = "cfg-if" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" dependencies = [ + "libc", "num-integer", "num-traits", "serde", "time", + "winapi", ] [[package]] name = "clap" -version = "2.33.0" +version = "2.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ "ansi_term", "atty", @@ -133,9 +146,9 @@ dependencies = [ [[package]] name = "failure" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" +checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" dependencies = [ "backtrace", "failure_derive", @@ -143,9 +156,9 @@ dependencies = [ [[package]] name = "failure_derive" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" +checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ "proc-macro2", "quote", @@ -155,9 +168,9 @@ dependencies = [ [[package]] name = "fnv" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fuchsia-cprng" @@ -167,20 +180,35 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "getrandom" -version = "0.1.13" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" +checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" + +[[package]] +name = "hermit-abi" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" dependencies = [ - "cfg-if", "libc", - "wasi", ] [[package]] name = "itoa" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" [[package]] name = "kv" @@ -203,9 +231,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.66" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" [[package]] name = "lmdb-rkv" @@ -231,50 +259,66 @@ dependencies = [ ] [[package]] -name = "num-integer" -version = "0.1.41" +name = "miniz_oxide" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" +checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" dependencies = [ - "autocfg", + "adler", + "autocfg 1.0.1", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg 1.0.1", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.10" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ - "autocfg", + "autocfg 1.0.1", ] [[package]] -name = "pkg-config" -version = "0.3.17" +name = "object" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" +checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" + +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" [[package]] name = "ppv-lite86" -version = "0.2.6" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "proc-macro2" -version = "1.0.7" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0319972dcae462681daf4da1adeeaa066e3ebd29c69be96c6abb1259d2ee2bcc" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ "unicode-xid", ] [[package]] name = "proptest" -version = "0.9.4" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf147e022eacf0c8a054ab864914a7602618adba841d800a9a9868a5237a529f" +checksum = "01c477819b845fe023d33583ebf10c9f62518c8d79a0960ba5c36d6ac8a55a5b" dependencies = [ "bit-set", "bitflags", @@ -292,15 +336,15 @@ dependencies = [ [[package]] name = "quick-error" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.2" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ "proc-macro2", ] @@ -324,7 +368,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" dependencies = [ - "autocfg", + "autocfg 0.1.7", "libc", "rand_chacha 0.1.1", "rand_core 0.4.2", @@ -339,13 +383,13 @@ dependencies = [ [[package]] name = "rand" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ "getrandom", "libc", - "rand_chacha 0.2.1", + "rand_chacha 0.2.2", "rand_core 0.5.1", "rand_hc 0.2.0", ] @@ -356,17 +400,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" dependencies = [ - "autocfg", + "autocfg 0.1.7", "rand_core 0.3.1", ] [[package]] name = "rand_chacha" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ - "c2-chacha", + "ppv-lite86", "rand_core 0.5.1", ] @@ -452,7 +496,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" dependencies = [ - "autocfg", + "autocfg 0.1.7", "rand_core 0.4.2", ] @@ -476,21 +520,21 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "regex-syntax" -version = "0.6.12" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" [[package]] name = "remove_dir_all" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ "winapi", ] @@ -507,9 +551,9 @@ dependencies = [ [[package]] name = "rmp-serde" -version = "0.14.0" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a31c0798045f039ace94e0166f76478b3ba83116ec7c9d4bc934c5b13b8df21" +checksum = "4ce7d70c926fe472aed493b902010bccc17fa9f7284145cb8772fd22fdb052d8" dependencies = [ "byteorder", "rmp", @@ -518,9 +562,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.16" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" [[package]] name = "rusty-fork" @@ -536,24 +580,24 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "serde" -version = "1.0.104" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" +checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.104" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" +checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" dependencies = [ "proc-macro2", "quote", @@ -562,9 +606,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.44" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c575e0cc52bdd09b47f330f646cf59afc586e9c4e3ccd6fc1f625b8ea1dad7" +checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" dependencies = [ "itoa", "ryu", @@ -579,20 +623,24 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" -version = "1.0.12" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc157159e2a7df58cd67b1cace10b8ed256a404fb0070593f137d8ba6bef4de" +checksum = "443b4178719c5a851e1bde36ce12da21d74a0e60b4d982ec3385a933c812f0f6" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] +[[package]] +name = "sync-server" +version = "0.1.0" + [[package]] name = "synstructure" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ "proc-macro2", "quote", @@ -605,7 +653,6 @@ name = "taskchampion" version = "0.1.0" dependencies = [ "chrono", - "clap", "failure", "kv", "lmdb-rkv", @@ -616,6 +663,15 @@ dependencies = [ "uuid", ] +[[package]] +name = "taskchampion-cli" +version = "0.1.0" +dependencies = [ + "clap", + "taskchampion", + "uuid", +] + [[package]] name = "tempdir" version = "0.3.7" @@ -632,9 +688,9 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", - "rand 0.7.2", + "rand 0.7.3", "redox_syscall", "remove_dir_all", "winapi", @@ -651,18 +707,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.9" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f357d1814b33bc2dc221243f8424104bfe72dbe911d5b71b3816a2dff1c977e" +checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.9" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2e25d25307eb8436894f727aba8f65d07adf02e5b35a13cebed48bd282bfef" +checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56" dependencies = [ "proc-macro2", "quote", @@ -671,35 +727,35 @@ dependencies = [ [[package]] name = "time" -version = "0.1.42" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", - "redox_syscall", + "wasi 0.10.0+wasi-snapshot-preview1", "winapi", ] [[package]] name = "toml" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d1404644c8b12b16bfcffa4322403a91a451584daaaa7c28d3152e6cbc98cf" +checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645" dependencies = [ "serde", ] [[package]] name = "unicode-width" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "unicode-xid" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "uuid" @@ -707,15 +763,15 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" dependencies = [ - "rand 0.7.2", + "rand 0.7.3", "serde", ] [[package]] name = "vec_map" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "wait-timeout" @@ -728,15 +784,21 @@ dependencies = [ [[package]] name = "wasi" -version = "0.7.0" +version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "winapi" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", diff --git a/Cargo.toml b/Cargo.toml index 8e5c07492..cd6520de1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,19 +1,7 @@ -[package] -name = "taskchampion" -version = "0.1.0" -authors = ["Dustin J. Mitchell "] -edition = "2018" +[workspace] -[dependencies] -uuid = { version = "0.8.1", features = ["serde", "v4"] } -serde = "1.0.104" -serde_json = "1.0" -chrono = { version = "0.4.10", features = ["serde"] } -failure = {version = "0.1.5", features = ["derive"] } -clap = "~2.33.0" -kv = {version = "0.10.0", features = ["msgpack-value"]} -lmdb-rkv = {version = "0.12.3"} - -[dev-dependencies] -proptest = "0.9.4" -tempdir = "0.3.7" +members = [ + "taskchampion", + "cli", + "sync-server" +] diff --git a/cli/Cargo.toml b/cli/Cargo.toml new file mode 100644 index 000000000..948b05eb7 --- /dev/null +++ b/cli/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "taskchampion-cli" +version = "0.1.0" +authors = ["Dustin J. Mitchell "] +edition = "2018" + +[dependencies] +clap = "~2.33.0" +uuid = { version = "0.8.1", features = ["serde", "v4"] } +taskchampion = { path = "../taskchampion" } diff --git a/src/bin/task.rs b/cli/src/main.rs similarity index 99% rename from src/bin/task.rs rename to cli/src/main.rs index 0362eb83f..058b1c940 100644 --- a/src/bin/task.rs +++ b/cli/src/main.rs @@ -1,4 +1,3 @@ -extern crate clap; use clap::{App, Arg, SubCommand}; use std::path::Path; use taskchampion::{taskstorage, Replica, Status, DB}; diff --git a/sync-server/Cargo.toml b/sync-server/Cargo.toml new file mode 100644 index 000000000..e2df075ed --- /dev/null +++ b/sync-server/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "sync-server" +version = "0.1.0" +authors = ["Dustin J. Mitchell "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/server.rs b/sync-server/src/lib.rs similarity index 100% rename from src/server.rs rename to sync-server/src/lib.rs diff --git a/taskchampion/Cargo.toml b/taskchampion/Cargo.toml new file mode 100644 index 000000000..1f6681454 --- /dev/null +++ b/taskchampion/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "taskchampion" +version = "0.1.0" +authors = ["Dustin J. Mitchell "] +edition = "2018" + +[dependencies] +uuid = { version = "0.8.1", features = ["serde", "v4"] } +serde = "1.0.104" +serde_json = "1.0" +chrono = { version = "0.4.10", features = ["serde"] } +failure = {version = "0.1.5", features = ["derive"] } +kv = {version = "0.10.0", features = ["msgpack-value"]} +lmdb-rkv = {version = "0.12.3"} + +[dev-dependencies] +proptest = "0.9.4" +tempdir = "0.3.7" diff --git a/README.md b/taskchampion/README.md similarity index 100% rename from README.md rename to taskchampion/README.md diff --git a/docs/.gitignore b/taskchampion/docs/.gitignore similarity index 100% rename from docs/.gitignore rename to taskchampion/docs/.gitignore diff --git a/docs/README.md b/taskchampion/docs/README.md similarity index 100% rename from docs/README.md rename to taskchampion/docs/README.md diff --git a/docs/book.toml b/taskchampion/docs/book.toml similarity index 100% rename from docs/book.toml rename to taskchampion/docs/book.toml diff --git a/docs/src/SUMMARY.md b/taskchampion/docs/src/SUMMARY.md similarity index 100% rename from docs/src/SUMMARY.md rename to taskchampion/docs/src/SUMMARY.md diff --git a/docs/src/data-model.md b/taskchampion/docs/src/data-model.md similarity index 100% rename from docs/src/data-model.md rename to taskchampion/docs/src/data-model.md diff --git a/docs/src/installation.md b/taskchampion/docs/src/installation.md similarity index 100% rename from docs/src/installation.md rename to taskchampion/docs/src/installation.md diff --git a/docs/src/plans.md b/taskchampion/docs/src/plans.md similarity index 100% rename from docs/src/plans.md rename to taskchampion/docs/src/plans.md diff --git a/docs/src/storage.md b/taskchampion/docs/src/storage.md similarity index 100% rename from docs/src/storage.md rename to taskchampion/docs/src/storage.md diff --git a/docs/src/sync.md b/taskchampion/docs/src/sync.md similarity index 100% rename from docs/src/sync.md rename to taskchampion/docs/src/sync.md diff --git a/docs/src/taskdb.md b/taskchampion/docs/src/taskdb.md similarity index 100% rename from docs/src/taskdb.md rename to taskchampion/docs/src/taskdb.md diff --git a/docs/src/tasks.md b/taskchampion/docs/src/tasks.md similarity index 100% rename from docs/src/tasks.md rename to taskchampion/docs/src/tasks.md diff --git a/docs/src/usage.md b/taskchampion/docs/src/usage.md similarity index 100% rename from docs/src/usage.md rename to taskchampion/docs/src/usage.md diff --git a/src/errors.rs b/taskchampion/src/errors.rs similarity index 100% rename from src/errors.rs rename to taskchampion/src/errors.rs diff --git a/src/lib.rs b/taskchampion/src/lib.rs similarity index 91% rename from src/lib.rs rename to taskchampion/src/lib.rs index 97b25b9a8..c9a242359 100644 --- a/src/lib.rs +++ b/taskchampion/src/lib.rs @@ -8,14 +8,13 @@ extern crate failure; mod errors; mod operation; mod replica; -mod server; +pub mod server; mod task; mod taskdb; pub mod taskstorage; pub use operation::Operation; pub use replica::Replica; -pub use server::Server; pub use task::Priority; pub use task::Status; pub use task::Task; diff --git a/src/operation.rs b/taskchampion/src/operation.rs similarity index 100% rename from src/operation.rs rename to taskchampion/src/operation.rs diff --git a/src/replica.rs b/taskchampion/src/replica.rs similarity index 100% rename from src/replica.rs rename to taskchampion/src/replica.rs diff --git a/taskchampion/src/server.rs b/taskchampion/src/server.rs new file mode 100644 index 000000000..233838fa6 --- /dev/null +++ b/taskchampion/src/server.rs @@ -0,0 +1,20 @@ +pub type Blob = Vec; + +pub enum VersionAdd { + // OK, version added + Ok, + // Rejected, must be based on the the given version + ExpectedVersion(u64), +} + +/// A value implementing this trait can act as a server against which a replica can sync. +pub trait Server { + /// Get a vector of all versions after `since_version` + fn get_versions(&self, username: &str, since_version: u64) -> Vec; + + /// Add a new version. If the given version number is incorrect, this responds with the + /// appropriate version and expects the caller to try again. + fn add_version(&mut self, username: &str, version: u64, blob: Blob) -> VersionAdd; + + fn add_snapshot(&mut self, username: &str, version: u64, blob: Blob); +} diff --git a/src/task.rs b/taskchampion/src/task.rs similarity index 100% rename from src/task.rs rename to taskchampion/src/task.rs diff --git a/src/taskdb.rs b/taskchampion/src/taskdb.rs similarity index 99% rename from src/taskdb.rs rename to taskchampion/src/taskdb.rs index 33306f359..5850b1571 100644 --- a/src/taskdb.rs +++ b/taskchampion/src/taskdb.rs @@ -170,7 +170,7 @@ impl DB { } /// Sync to the given server, pulling remote changes and pushing local changes. - pub fn sync(&mut self, username: &str, server: &mut Server) -> Fallible<()> { + pub fn sync(&mut self, username: &str, server: &mut dyn Server) -> Fallible<()> { let mut txn = self.storage.txn()?; // retry synchronizing until the server accepts our version (this allows for races between diff --git a/src/taskstorage/inmemory.rs b/taskchampion/src/taskstorage/inmemory.rs similarity index 100% rename from src/taskstorage/inmemory.rs rename to taskchampion/src/taskstorage/inmemory.rs diff --git a/src/taskstorage/kv.rs b/taskchampion/src/taskstorage/kv.rs similarity index 100% rename from src/taskstorage/kv.rs rename to taskchampion/src/taskstorage/kv.rs diff --git a/src/taskstorage/mod.rs b/taskchampion/src/taskstorage/mod.rs similarity index 100% rename from src/taskstorage/mod.rs rename to taskchampion/src/taskstorage/mod.rs diff --git a/tests/data/tdb2-test.data b/taskchampion/tests/data/tdb2-test.data similarity index 100% rename from tests/data/tdb2-test.data rename to taskchampion/tests/data/tdb2-test.data diff --git a/tests/operation_transform_invariant.rs b/taskchampion/tests/operation_transform_invariant.rs similarity index 100% rename from tests/operation_transform_invariant.rs rename to taskchampion/tests/operation_transform_invariant.rs diff --git a/taskchampion/tests/shared/mod.rs b/taskchampion/tests/shared/mod.rs new file mode 100644 index 000000000..8a5a5ee0c --- /dev/null +++ b/taskchampion/tests/shared/mod.rs @@ -0,0 +1,3 @@ +mod testserver; + +pub use testserver::TestServer; diff --git a/taskchampion/tests/shared/testserver.rs b/taskchampion/tests/shared/testserver.rs new file mode 100644 index 000000000..2cb1cde2e --- /dev/null +++ b/taskchampion/tests/shared/testserver.rs @@ -0,0 +1,81 @@ +use std::collections::HashMap; +use taskchampion::server::{Blob, Server, VersionAdd}; + +pub struct TestServer { + users: HashMap, +} + +struct User { + // versions, indexed at v-1 + versions: Vec, + snapshots: HashMap, +} + +impl TestServer { + pub fn new() -> TestServer { + TestServer { + users: HashMap::new(), + } + } + + fn get_user_mut(&mut self, username: &str) -> &mut User { + self.users + .entry(username.to_string()) + .or_insert_with(User::new) + } +} + +impl Server for TestServer { + /// Get a vector of all versions after `since_version` + fn get_versions(&self, username: &str, since_version: u64) -> Vec { + self.users + .get(username) + .map(|user| user.get_versions(since_version)) + .unwrap_or_else(|| vec![]) + } + + /// Add a new version. If the given version number is incorrect, this responds with the + /// appropriate version and expects the caller to try again. + fn add_version(&mut self, username: &str, version: u64, blob: Blob) -> VersionAdd { + self.get_user_mut(username).add_version(version, blob) + } + + fn add_snapshot(&mut self, username: &str, version: u64, blob: Blob) { + self.get_user_mut(username).add_snapshot(version, blob); + } +} + +impl User { + fn new() -> User { + User { + versions: vec![], + snapshots: HashMap::new(), + } + } + + fn get_versions(&self, since_version: u64) -> Vec { + let last_version = self.versions.len(); + if last_version == since_version as usize { + return vec![]; + } + self.versions[since_version as usize..last_version] + .iter() + .map(|r| r.clone()) + .collect::>() + } + + fn add_version(&mut self, version: u64, blob: Blob) -> VersionAdd { + // of by one here: client wants to send version 1 first + let expected_version = self.versions.len() as u64 + 1; + if version != expected_version { + return VersionAdd::ExpectedVersion(expected_version); + } + self.versions.push(blob); + + VersionAdd::Ok + } + + fn add_snapshot(&mut self, version: u64, blob: Blob) { + self.snapshots.insert(version, blob); + } +} diff --git a/tests/sync.rs b/taskchampion/tests/sync.rs similarity index 95% rename from tests/sync.rs rename to taskchampion/tests/sync.rs index fc594bbd3..197708dee 100644 --- a/tests/sync.rs +++ b/taskchampion/tests/sync.rs @@ -1,14 +1,17 @@ use chrono::Utc; -use taskchampion::{taskstorage, Operation, Server, DB}; +use taskchampion::{taskstorage, Operation, DB}; use uuid::Uuid; +mod shared; +use shared::TestServer; + fn newdb() -> DB { DB::new(Box::new(taskstorage::InMemoryStorage::new())) } #[test] fn test_sync() { - let mut server = Server::new(); + let mut server = TestServer::new(); let mut db1 = newdb(); db1.sync("me", &mut server).unwrap(); @@ -68,7 +71,7 @@ fn test_sync() { #[test] fn test_sync_create_delete() { - let mut server = Server::new(); + let mut server = TestServer::new(); let mut db1 = newdb(); db1.sync("me", &mut server).unwrap(); diff --git a/tests/sync_action_sequences.rs b/taskchampion/tests/sync_action_sequences.rs similarity index 94% rename from tests/sync_action_sequences.rs rename to taskchampion/tests/sync_action_sequences.rs index 741988b11..2c94d6dd8 100644 --- a/tests/sync_action_sequences.rs +++ b/taskchampion/tests/sync_action_sequences.rs @@ -1,8 +1,11 @@ use chrono::Utc; use proptest::prelude::*; -use taskchampion::{taskstorage, Operation, Server, DB}; +use taskchampion::{taskstorage, Operation, DB}; use uuid::Uuid; +mod shared; +use shared::TestServer; + fn newdb() -> DB { DB::new(Box::new(taskstorage::InMemoryStorage::new())) } @@ -46,7 +49,7 @@ proptest! { // and delete operations that results in a task existing in one DB but not existing in // another. So, the generated sequences focus on a single task UUID. fn transform_sequences_of_operations(action_sequence in action_sequence_strategy()) { - let mut server = Server::new(); + let mut server = TestServer::new(); let mut dbs = [newdb(), newdb(), newdb()]; for (action, db) in action_sequence { From ba55d298ceebfc6e661705e8f59a088cc9f7c285 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 23 Nov 2020 14:29:51 -0500 Subject: [PATCH 2/6] stop ignoring dead code and unused variables --- taskchampion/src/errors.rs | 3 --- taskchampion/src/lib.rs | 4 ---- taskchampion/src/taskstorage/kv.rs | 4 ---- 3 files changed, 11 deletions(-) diff --git a/taskchampion/src/errors.rs b/taskchampion/src/errors.rs index 8a180936f..08884376f 100644 --- a/taskchampion/src/errors.rs +++ b/taskchampion/src/errors.rs @@ -4,7 +4,4 @@ use failure::Fail; pub enum Error { #[fail(display = "Task Database Error: {}", _0)] DBError(String), - - #[fail(display = "Replica Error: {}", _0)] - ReplicaError(String), } diff --git a/taskchampion/src/lib.rs b/taskchampion/src/lib.rs index c9a242359..9042dc2de 100644 --- a/taskchampion/src/lib.rs +++ b/taskchampion/src/lib.rs @@ -1,7 +1,3 @@ -// TODO: remove this eventually when there's an API -#![allow(dead_code)] -#![allow(unused_variables)] - #[macro_use] extern crate failure; diff --git a/taskchampion/src/taskstorage/kv.rs b/taskchampion/src/taskstorage/kv.rs index 08f2ade01..da275c90e 100644 --- a/taskchampion/src/taskstorage/kv.rs +++ b/taskchampion/src/taskstorage/kv.rs @@ -177,7 +177,6 @@ impl<'t> TaskStorageTxn for Txn<'t> { fn all_tasks(&mut self) -> Fallible> { let bucket = self.tasks_bucket(); let kvtxn = self.kvtxn(); - let curs = kvtxn.read_cursor(bucket)?; let all_tasks: Result, Error> = kvtxn .read_cursor(bucket)? .iter() @@ -189,7 +188,6 @@ impl<'t> TaskStorageTxn for Txn<'t> { fn all_task_uuids(&mut self) -> Fallible> { let bucket = self.tasks_bucket(); let kvtxn = self.kvtxn(); - let curs = kvtxn.read_cursor(bucket)?; Ok(kvtxn .read_cursor(bucket)? .iter() @@ -224,7 +222,6 @@ impl<'t> TaskStorageTxn for Txn<'t> { fn operations(&mut self) -> Fallible> { let bucket = self.operations_bucket(); let kvtxn = self.kvtxn(); - let curs = kvtxn.read_cursor(bucket)?; let all_ops: Result, Error> = kvtxn .read_cursor(bucket)? .iter() @@ -299,7 +296,6 @@ impl<'t> TaskStorageTxn for Txn<'t> { res.push(None) } - let curs = kvtxn.read_cursor(working_set_bucket)?; for (i, u) in kvtxn.read_cursor(working_set_bucket)?.iter() { let i: u64 = i.into(); res[i as usize] = Some(u.inner()?.to_serde()); From 8f4924f90316f9a28c60f9167d9407c94fb60fbd Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 23 Nov 2020 15:02:37 -0500 Subject: [PATCH 3/6] remove unnecessary 'extern crate' --- taskchampion/src/lib.rs | 3 --- taskchampion/src/task.rs | 2 +- taskchampion/src/taskstorage/inmemory.rs | 2 +- taskchampion/src/taskstorage/kv.rs | 2 +- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/taskchampion/src/lib.rs b/taskchampion/src/lib.rs index 9042dc2de..e3e4bc00d 100644 --- a/taskchampion/src/lib.rs +++ b/taskchampion/src/lib.rs @@ -1,6 +1,3 @@ -#[macro_use] -extern crate failure; - mod errors; mod operation; mod replica; diff --git a/taskchampion/src/task.rs b/taskchampion/src/task.rs index a8b95989b..75e852fda 100644 --- a/taskchampion/src/task.rs +++ b/taskchampion/src/task.rs @@ -1,7 +1,7 @@ use crate::replica::Replica; use crate::taskstorage::TaskMap; use chrono::prelude::*; -use failure::Fallible; +use failure::{format_err, Fallible}; use std::convert::TryFrom; use uuid::Uuid; diff --git a/taskchampion/src/taskstorage/inmemory.rs b/taskchampion/src/taskstorage/inmemory.rs index 6b272874c..559a2a06f 100644 --- a/taskchampion/src/taskstorage/inmemory.rs +++ b/taskchampion/src/taskstorage/inmemory.rs @@ -1,6 +1,6 @@ use crate::operation::Operation; use crate::taskstorage::{TaskMap, TaskStorage, TaskStorageTxn}; -use failure::Fallible; +use failure::{format_err, Fallible}; use std::collections::hash_map::Entry; use std::collections::HashMap; use uuid::Uuid; diff --git a/taskchampion/src/taskstorage/kv.rs b/taskchampion/src/taskstorage/kv.rs index da275c90e..14198428c 100644 --- a/taskchampion/src/taskstorage/kv.rs +++ b/taskchampion/src/taskstorage/kv.rs @@ -1,6 +1,6 @@ use crate::operation::Operation; use crate::taskstorage::{TaskMap, TaskStorage, TaskStorageTxn}; -use failure::Fallible; +use failure::{format_err, Fallible}; use kv::msgpack::Msgpack; use kv::{Bucket, Config, Error, Integer, Serde, Store, ValueBuf}; use std::convert::TryInto; From 8e2b4c3f6ccdc85bcf0834d53e2a6a5c62d30d2e Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 23 Nov 2020 15:59:37 -0500 Subject: [PATCH 4/6] Reorganize taskchampion crate for docs and tests The public API of the taskchampion crate now contains the expected parts and no more, and has some better documentation. This moves the crate's external `tests/` into internal tests, as the TaskDB is no longer exposed as part of the crate API. --- cli/src/main.rs | 11 +- taskchampion/src/lib.rs | 32 ++- taskchampion/src/replica.rs | 42 ++-- taskchampion/src/task.rs | 65 ++++-- taskchampion/src/taskdb.rs | 190 +++++++++++++++++- taskchampion/src/taskstorage/inmemory.rs | 5 +- taskchampion/src/taskstorage/kv.rs | 3 +- taskchampion/src/taskstorage/mod.rs | 25 ++- .../src/{ => taskstorage}/operation.rs | 79 ++++++++ taskchampion/src/testing/mod.rs | 1 + .../shared => src/testing}/testserver.rs | 4 +- taskchampion/tests/data/tdb2-test.data | 2 - .../tests/operation_transform_invariant.rs | 85 -------- taskchampion/tests/shared/mod.rs | 3 - taskchampion/tests/sync.rs | 123 ------------ taskchampion/tests/sync_action_sequences.rs | 72 ------- 16 files changed, 395 insertions(+), 347 deletions(-) rename taskchampion/src/{ => taskstorage}/operation.rs (73%) create mode 100644 taskchampion/src/testing/mod.rs rename taskchampion/{tests/shared => src/testing}/testserver.rs (96%) delete mode 100644 taskchampion/tests/data/tdb2-test.data delete mode 100644 taskchampion/tests/operation_transform_invariant.rs delete mode 100644 taskchampion/tests/shared/mod.rs delete mode 100644 taskchampion/tests/sync.rs delete mode 100644 taskchampion/tests/sync_action_sequences.rs diff --git a/cli/src/main.rs b/cli/src/main.rs index 058b1c940..1ae6b148e 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,6 +1,6 @@ use clap::{App, Arg, SubCommand}; use std::path::Path; -use taskchampion::{taskstorage, Replica, Status, DB}; +use taskchampion::{taskstorage, Replica, Status}; use uuid::Uuid; fn main() { @@ -20,12 +20,9 @@ fn main() { .subcommand(SubCommand::with_name("gc").about("run garbage collection")) .get_matches(); - let mut replica = Replica::new( - DB::new(Box::new( - taskstorage::KVStorage::new(Path::new("/tmp/tasks")).unwrap(), - )) - .into(), - ); + let mut replica = Replica::new(Box::new( + taskstorage::KVStorage::new(Path::new("/tmp/tasks")).unwrap(), + )); match matches.subcommand() { ("add", Some(matches)) => { diff --git a/taskchampion/src/lib.rs b/taskchampion/src/lib.rs index e3e4bc00d..d5d1d13be 100644 --- a/taskchampion/src/lib.rs +++ b/taskchampion/src/lib.rs @@ -1,14 +1,38 @@ +/*! + +This crate implements the core of TaskChampion, the [replica](crate::Replica). + +A TaskChampion replica is a local copy of a user's task data. As the name suggests, several +replicas of the same data can exist (such as on a user's laptop and on their phone) and can +synchronize with one another. + +# Task Storage + +The [`taskstorage`](crate::taskstorage) module supports pluggable storage for a replica's data. +An implementation is provided, but users of this crate can provide their own implementation as well. + +# Server + +Replica synchronization takes place against a server. +The [`server`](crate::server) module defines the interface a server must meet. + +# See Also + +See the [TaskChampion Book](https://github.com/djmitche/taskchampion/blob/main/docs/src/SUMMARY.md) +for more information about the design and usage of the tool. + + */ mod errors; -mod operation; mod replica; pub mod server; mod task; mod taskdb; pub mod taskstorage; -pub use operation::Operation; pub use replica::Replica; pub use task::Priority; pub use task::Status; -pub use task::Task; -pub use taskdb::DB; +pub use task::{Task, TaskMut}; + +#[cfg(test)] +pub(crate) mod testing; diff --git a/taskchampion/src/replica.rs b/taskchampion/src/replica.rs index 507ea9291..874e74eca 100644 --- a/taskchampion/src/replica.rs +++ b/taskchampion/src/replica.rs @@ -1,21 +1,29 @@ use crate::errors::Error; -use crate::operation::Operation; +use crate::server::Server; use crate::task::{Status, Task}; use crate::taskdb::DB; -use crate::taskstorage::TaskMap; +use crate::taskstorage::{Operation, TaskMap, TaskStorage}; use chrono::Utc; use failure::Fallible; use std::collections::HashMap; use uuid::Uuid; -/// A replica represents an instance of a user's task data. +/// A replica represents an instance of a user's task data, providing an easy interface +/// for querying and modifying that data. pub struct Replica { - taskdb: Box, + taskdb: DB, } impl Replica { - pub fn new(taskdb: Box) -> Replica { - return Replica { taskdb }; + pub fn new(storage: Box) -> Replica { + return Replica { + taskdb: DB::new(storage), + }; + } + + #[cfg(test)] + pub fn new_inmemory() -> Replica { + Replica::new(Box::new(crate::taskstorage::InMemoryStorage::new())) } /// Update an existing task. If the value is Some, the property is added or updated. If the @@ -45,7 +53,7 @@ impl Replica { } /// Get all tasks represented as a map keyed by UUID - pub fn all_tasks<'a>(&'a mut self) -> Fallible> { + pub fn all_tasks(&mut self) -> Fallible> { let mut res = HashMap::new(); for (uuid, tm) in self.taskdb.all_tasks()?.drain(..) { res.insert(uuid.clone(), Task::new(uuid.clone(), tm)); @@ -54,7 +62,7 @@ impl Replica { } /// Get the UUIDs of all tasks - pub fn all_task_uuids<'a>(&'a mut self) -> Fallible> { + pub fn all_task_uuids(&mut self) -> Fallible> { self.taskdb.all_task_uuids() } @@ -124,6 +132,11 @@ impl Replica { self.taskdb.apply(Operation::Delete { uuid }) } + /// Synchronize this replica against the given server. + pub fn sync(&mut self, username: &str, server: &mut dyn Server) -> Fallible<()> { + self.taskdb.sync(username, server) + } + /// Perform "garbage collection" on this replica. In particular, this renumbers the working /// set to contain only pending tasks. pub fn gc(&mut self) -> Fallible<()> { @@ -138,12 +151,11 @@ impl Replica { mod tests { use super::*; use crate::task::Status; - use crate::taskdb::DB; use uuid::Uuid; #[test] fn new_task() { - let mut rep = Replica::new(DB::new_inmemory().into()); + let mut rep = Replica::new_inmemory(); let uuid = Uuid::new_v4(); let t = rep @@ -156,7 +168,7 @@ mod tests { #[test] fn modify_task() { - let mut rep = Replica::new(DB::new_inmemory().into()); + let mut rep = Replica::new_inmemory(); let uuid = Uuid::new_v4(); let t = rep @@ -183,7 +195,7 @@ mod tests { #[test] fn delete_task() { - let mut rep = Replica::new(DB::new_inmemory().into()); + let mut rep = Replica::new_inmemory(); let uuid = Uuid::new_v4(); rep.new_task(uuid.clone(), Status::Pending, "a task".into()) @@ -195,7 +207,7 @@ mod tests { #[test] fn get_and_modify() { - let mut rep = Replica::new(DB::new_inmemory().into()); + let mut rep = Replica::new_inmemory(); let uuid = Uuid::new_v4(); rep.new_task(uuid.clone(), Status::Pending, "another task".into()) @@ -215,7 +227,7 @@ mod tests { #[test] fn new_pending_adds_to_working_set() { - let mut rep = Replica::new(DB::new_inmemory().into()); + let mut rep = Replica::new_inmemory(); let uuid = Uuid::new_v4(); rep.new_task(uuid.clone(), Status::Pending, "to-be-pending".into()) @@ -233,7 +245,7 @@ mod tests { #[test] fn get_does_not_exist() { - let mut rep = Replica::new(DB::new_inmemory().into()); + let mut rep = Replica::new_inmemory(); let uuid = Uuid::new_v4(); assert_eq!(rep.get_task(&uuid).unwrap(), None); } diff --git a/taskchampion/src/task.rs b/taskchampion/src/task.rs index 75e852fda..c0ccc1480 100644 --- a/taskchampion/src/task.rs +++ b/taskchampion/src/task.rs @@ -1,34 +1,37 @@ use crate::replica::Replica; use crate::taskstorage::TaskMap; use chrono::prelude::*; -use failure::{format_err, Fallible}; -use std::convert::TryFrom; +use failure::Fallible; use uuid::Uuid; pub type Timestamp = DateTime; +/// The priority of a task #[derive(Debug, PartialEq)] pub enum Priority { + /// Low L, + /// Medium M, + /// High H, } -impl TryFrom<&str> for Priority { - type Error = failure::Error; - - fn try_from(s: &str) -> Result { +#[allow(dead_code)] +impl Priority { + /// Get a Priority from the 1-character value in a TaskMap, + /// defaulting to M + pub(crate) fn from_taskmap(s: &str) -> Priority { match s { - "L" => Ok(Priority::L), - "M" => Ok(Priority::M), - "H" => Ok(Priority::H), - _ => Err(format_err!("invalid status {}", s)), + "L" => Priority::L, + "M" => Priority::M, + "H" => Priority::H, + _ => Priority::M, } } -} -impl AsRef for Priority { - fn as_ref(&self) -> &str { + /// Get the 1-character value for this priority to use in the TaskMap. + pub(crate) fn to_taskmap(&self) -> &str { match self { Priority::L => "L", Priority::M => "M", @@ -36,6 +39,8 @@ impl AsRef for Priority { } } } + +/// The status of a task. The default status in "Pending". #[derive(Debug, PartialEq)] pub enum Status { Pending, @@ -86,8 +91,8 @@ pub struct Task { taskmap: TaskMap, } -/// A mutable task, with setter methods. Calling a setter will update the Replica, as well as the -/// included Task. +/// A mutable task, with setter methods. Most methods are simple setters and not further +/// described. Calling a setter will update the Replica, as well as the included Task. pub struct TaskMut<'r> { task: Task, replica: &'r mut Replica, @@ -145,7 +150,8 @@ impl Task { } impl<'r> TaskMut<'r> { - /// Get the immutable task + /// Get the immutable version of this object. Note that TaskMut [`std::ops::Deref`]s to + /// [`crate::task::Task`], so all of that struct's getter methods can be used on TaskMut. pub fn into_immut(self) -> Task { self.task } @@ -160,12 +166,10 @@ impl<'r> TaskMut<'r> { self.set_string("status", Some(String::from(status.to_taskmap()))) } - /// Set the task's description pub fn set_description(&mut self, description: String) -> Fallible<()> { self.set_string("description", Some(description)) } - /// Set the task's description pub fn set_modified(&mut self, modified: DateTime) -> Fallible<()> { self.set_timestamp("modified", Some(modified)) } @@ -213,3 +217,28 @@ impl<'r> std::ops::Deref for TaskMut<'r> { &self.task } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_priority() { + assert_eq!(Priority::L.to_taskmap(), "L"); + assert_eq!(Priority::M.to_taskmap(), "M"); + assert_eq!(Priority::H.to_taskmap(), "H"); + assert_eq!(Priority::from_taskmap("L"), Priority::L); + assert_eq!(Priority::from_taskmap("M"), Priority::M); + assert_eq!(Priority::from_taskmap("H"), Priority::H); + } + + #[test] + fn test_status() { + assert_eq!(Status::Pending.to_taskmap(), "P"); + assert_eq!(Status::Completed.to_taskmap(), "C"); + assert_eq!(Status::Deleted.to_taskmap(), "D"); + assert_eq!(Status::from_taskmap("P"), Status::Pending); + assert_eq!(Status::from_taskmap("C"), Status::Completed); + assert_eq!(Status::from_taskmap("D"), Status::Deleted); + } +} diff --git a/taskchampion/src/taskdb.rs b/taskchampion/src/taskdb.rs index 5850b1571..7fe9362fc 100644 --- a/taskchampion/src/taskdb.rs +++ b/taskchampion/src/taskdb.rs @@ -1,7 +1,6 @@ use crate::errors::Error; -use crate::operation::Operation; use crate::server::{Server, VersionAdd}; -use crate::taskstorage::{TaskMap, TaskStorage, TaskStorageTxn}; +use crate::taskstorage::{Operation, TaskMap, TaskStorage, TaskStorageTxn}; use failure::Fallible; use serde::{Deserialize, Serialize}; use std::collections::HashSet; @@ -268,7 +267,8 @@ impl DB { // functions for supporting tests - pub fn sorted_tasks(&mut self) -> Vec<(Uuid, Vec<(String, String)>)> { + #[cfg(test)] + pub(crate) fn sorted_tasks(&mut self) -> Vec<(Uuid, Vec<(String, String)>)> { let mut res: Vec<(Uuid, Vec<(String, String)>)> = self .all_tasks() .unwrap() @@ -286,7 +286,8 @@ impl DB { res } - pub fn operations(&mut self) -> Vec { + #[cfg(test)] + pub(crate) fn operations(&mut self) -> Vec { let mut txn = self.storage.txn().unwrap(); txn.operations() .unwrap() @@ -299,7 +300,10 @@ impl DB { #[cfg(test)] mod tests { use super::*; + use crate::taskstorage::InMemoryStorage; + use crate::testing::testserver::TestServer; use chrono::Utc; + use proptest::prelude::*; use std::collections::HashMap; use uuid::Uuid; @@ -508,4 +512,182 @@ mod tests { Ok(()) } + + fn newdb() -> DB { + DB::new(Box::new(InMemoryStorage::new())) + } + + #[test] + fn test_sync() { + let mut server = TestServer::new(); + + let mut db1 = newdb(); + db1.sync("me", &mut server).unwrap(); + + let mut db2 = newdb(); + db2.sync("me", &mut server).unwrap(); + + // make some changes in parallel to db1 and db2.. + let uuid1 = Uuid::new_v4(); + db1.apply(Operation::Create { uuid: uuid1 }).unwrap(); + db1.apply(Operation::Update { + uuid: uuid1, + property: "title".into(), + value: Some("my first task".into()), + timestamp: Utc::now(), + }) + .unwrap(); + + let uuid2 = Uuid::new_v4(); + db2.apply(Operation::Create { uuid: uuid2 }).unwrap(); + db2.apply(Operation::Update { + uuid: uuid2, + property: "title".into(), + value: Some("my second task".into()), + timestamp: Utc::now(), + }) + .unwrap(); + + // and synchronize those around + db1.sync("me", &mut server).unwrap(); + db2.sync("me", &mut server).unwrap(); + db1.sync("me", &mut server).unwrap(); + assert_eq!(db1.sorted_tasks(), db2.sorted_tasks()); + + // now make updates to the same task on both sides + db1.apply(Operation::Update { + uuid: uuid2, + property: "priority".into(), + value: Some("H".into()), + timestamp: Utc::now(), + }) + .unwrap(); + db2.apply(Operation::Update { + uuid: uuid2, + property: "project".into(), + value: Some("personal".into()), + timestamp: Utc::now(), + }) + .unwrap(); + + // and synchronize those around + db1.sync("me", &mut server).unwrap(); + db2.sync("me", &mut server).unwrap(); + db1.sync("me", &mut server).unwrap(); + assert_eq!(db1.sorted_tasks(), db2.sorted_tasks()); + } + + #[test] + fn test_sync_create_delete() { + let mut server = TestServer::new(); + + let mut db1 = newdb(); + db1.sync("me", &mut server).unwrap(); + + let mut db2 = newdb(); + db2.sync("me", &mut server).unwrap(); + + // create and update a task.. + let uuid = Uuid::new_v4(); + db1.apply(Operation::Create { uuid }).unwrap(); + db1.apply(Operation::Update { + uuid: uuid, + property: "title".into(), + value: Some("my first task".into()), + timestamp: Utc::now(), + }) + .unwrap(); + + // and synchronize those around + db1.sync("me", &mut server).unwrap(); + db2.sync("me", &mut server).unwrap(); + db1.sync("me", &mut server).unwrap(); + assert_eq!(db1.sorted_tasks(), db2.sorted_tasks()); + + // delete and re-create the task on db1 + db1.apply(Operation::Delete { uuid }).unwrap(); + db1.apply(Operation::Create { uuid }).unwrap(); + db1.apply(Operation::Update { + uuid: uuid, + property: "title".into(), + value: Some("my second task".into()), + timestamp: Utc::now(), + }) + .unwrap(); + + // and on db2, update a property of the task + db2.apply(Operation::Update { + uuid: uuid, + property: "project".into(), + value: Some("personal".into()), + timestamp: Utc::now(), + }) + .unwrap(); + + db1.sync("me", &mut server).unwrap(); + db2.sync("me", &mut server).unwrap(); + db1.sync("me", &mut server).unwrap(); + assert_eq!(db1.sorted_tasks(), db2.sorted_tasks()); + } + + #[derive(Debug)] + enum Action { + Op(Operation), + Sync, + } + + fn action_sequence_strategy() -> impl Strategy> { + // Create, Update, Delete, or Sync on client 1, 2, .., followed by a round of syncs + "([CUDS][123])*S1S2S3S1S2".prop_map(|seq| { + let uuid = Uuid::parse_str("83a2f9ef-f455-4195-b92e-a54c161eebfc").unwrap(); + seq.as_bytes() + .chunks(2) + .map(|action_on| { + let action = match action_on[0] { + b'C' => Action::Op(Operation::Create { uuid }), + b'U' => Action::Op(Operation::Update { + uuid, + property: "title".into(), + value: Some("foo".into()), + timestamp: Utc::now(), + }), + b'D' => Action::Op(Operation::Delete { uuid }), + b'S' => Action::Sync, + _ => unreachable!(), + }; + let acton = action_on[1] - b'1'; + (action, acton) + }) + .collect::>() + }) + } + + proptest! { + #[test] + // check that various sequences of operations on mulitple db's do not get the db's into an + // incompatible state. The main concern here is that there might be a sequence of create + // and delete operations that results in a task existing in one DB but not existing in + // another. So, the generated sequences focus on a single task UUID. + fn transform_sequences_of_operations(action_sequence in action_sequence_strategy()) { + let mut server = TestServer::new(); + let mut dbs = [newdb(), newdb(), newdb()]; + + for (action, db) in action_sequence { + println!("{:?} on db {}", action, db); + + let db = &mut dbs[db as usize]; + match action { + Action::Op(op) => { + if let Err(e) = db.apply(op) { + println!(" {:?} (ignored)", e); + } + }, + Action::Sync => db.sync("me", &mut server).unwrap(), + } + } + + assert_eq!(dbs[0].sorted_tasks(), dbs[0].sorted_tasks()); + assert_eq!(dbs[1].sorted_tasks(), dbs[2].sorted_tasks()); + } + } } diff --git a/taskchampion/src/taskstorage/inmemory.rs b/taskchampion/src/taskstorage/inmemory.rs index 559a2a06f..cdc376c5e 100644 --- a/taskchampion/src/taskstorage/inmemory.rs +++ b/taskchampion/src/taskstorage/inmemory.rs @@ -1,5 +1,4 @@ -use crate::operation::Operation; -use crate::taskstorage::{TaskMap, TaskStorage, TaskStorageTxn}; +use crate::taskstorage::{Operation, TaskMap, TaskStorage, TaskStorageTxn}; use failure::{format_err, Fallible}; use std::collections::hash_map::Entry; use std::collections::HashMap; @@ -139,6 +138,8 @@ impl<'t> TaskStorageTxn for Txn<'t> { } } +/// InMemoryStorage is a simple in-memory task storage implementation. It is not useful for +/// production data, but is useful for testing purposes. #[derive(PartialEq, Debug, Clone)] pub struct InMemoryStorage { data: Data, diff --git a/taskchampion/src/taskstorage/kv.rs b/taskchampion/src/taskstorage/kv.rs index 14198428c..8cbe033b7 100644 --- a/taskchampion/src/taskstorage/kv.rs +++ b/taskchampion/src/taskstorage/kv.rs @@ -1,5 +1,4 @@ -use crate::operation::Operation; -use crate::taskstorage::{TaskMap, TaskStorage, TaskStorageTxn}; +use crate::taskstorage::{Operation, TaskMap, TaskStorage, TaskStorageTxn}; use failure::{format_err, Fallible}; use kv::msgpack::Msgpack; use kv::{Bucket, Config, Error, Integer, Serde, Store, ValueBuf}; diff --git a/taskchampion/src/taskstorage/mod.rs b/taskchampion/src/taskstorage/mod.rs index b5681f4de..bc0fb30a3 100644 --- a/taskchampion/src/taskstorage/mod.rs +++ b/taskchampion/src/taskstorage/mod.rs @@ -1,14 +1,16 @@ -use crate::Operation; use failure::Fallible; use std::collections::HashMap; use uuid::Uuid; mod inmemory; mod kv; +mod operation; pub use self::kv::KVStorage; pub use inmemory::InMemoryStorage; +pub use operation::Operation; + /// An in-memory representation of a task as a simple hashmap pub type TaskMap = HashMap; @@ -22,10 +24,18 @@ fn taskmap_with(mut properties: Vec<(String, String)>) -> TaskMap { } /// A TaskStorage transaction, in which storage operations are performed. -/// Serializable consistency is maintained, and implementations do not optimize -/// for concurrent access so some may simply apply a mutex to limit access to -/// one transaction at a time. Transactions are aborted if they are dropped. -/// It's safe to drop transactions that did not modify any data. +/// +/// # Concurrency +/// +/// Serializable consistency must be maintained. Concurrent access is unusual +/// and some implementations may simply apply a mutex to limit access to +/// one transaction at a time. +/// +/// # Commiting and Aborting +/// +/// A transaction is not visible to other readers until it is committed with +/// [`crate::taskstorage::TaskStorageTxn::commit`]. Transactions are aborted if they are dropped. +/// It is safe and performant to drop transactions that did not modify any data without committing. pub trait TaskStorageTxn { /// Get an (immutable) task, if it is in the storage fn get_task(&mut self, uuid: &Uuid) -> Fallible>; @@ -83,9 +93,8 @@ pub trait TaskStorageTxn { fn commit(&mut self) -> Fallible<()>; } -/// A trait for objects able to act as backing storage for a DB. This API is optimized to be -/// easy to implement, with all of the semantic meaning of the data located in the DB -/// implementation, which is the sole consumer of this trait. +/// A trait for objects able to act as task storage. Most of the interesting behavior is in the +/// [`crate::taskstorage::TaskStorageTxn`] trait. pub trait TaskStorage { /// Begin a transaction fn txn<'a>(&'a mut self) -> Fallible>; diff --git a/taskchampion/src/operation.rs b/taskchampion/src/taskstorage/operation.rs similarity index 73% rename from taskchampion/src/operation.rs rename to taskchampion/src/taskstorage/operation.rs index cba88c808..f8927a133 100644 --- a/taskchampion/src/operation.rs +++ b/taskchampion/src/taskstorage/operation.rs @@ -129,7 +129,9 @@ impl Operation { mod test { use super::*; use crate::taskdb::DB; + use crate::taskstorage::InMemoryStorage; use chrono::{Duration, Utc}; + use proptest::prelude::*; // note that `tests/operation_transform_invariant.rs` tests the transform function quite // thoroughly, so this testing is light. @@ -273,4 +275,81 @@ mod test { None, ); } + + fn uuid_strategy() -> impl Strategy { + prop_oneof![ + Just(Uuid::parse_str("83a2f9ef-f455-4195-b92e-a54c161eebfc").unwrap()), + Just(Uuid::parse_str("56e0be07-c61f-494c-a54c-bdcfdd52d2a7").unwrap()), + Just(Uuid::parse_str("4b7ed904-f7b0-4293-8a10-ad452422c7b3").unwrap()), + Just(Uuid::parse_str("9bdd0546-07c8-4e1f-a9bc-9d6299f4773b").unwrap()), + ] + } + + fn operation_strategy() -> impl Strategy { + prop_oneof![ + uuid_strategy().prop_map(|uuid| Operation::Create { uuid }), + uuid_strategy().prop_map(|uuid| Operation::Delete { uuid }), + (uuid_strategy(), "(title|project|status)").prop_map(|(uuid, property)| { + Operation::Update { + uuid, + property, + value: Some("true".into()), + timestamp: Utc::now(), + } + }), + ] + } + + proptest! { + #![proptest_config(ProptestConfig { + cases: 1024, .. ProptestConfig::default() + })] + #[test] + // check that the two operation sequences have the same effect, enforcing the invariant of + // the transform function. + fn transform_invariant_holds(o1 in operation_strategy(), o2 in operation_strategy()) { + let (o1p, o2p) = Operation::transform(o1.clone(), o2.clone()); + + let mut db1 = DB::new(Box::new(InMemoryStorage::new())); + let mut db2 = DB::new(Box::new(InMemoryStorage::new())); + + // Ensure that any expected tasks already exist + if let Operation::Update{ ref uuid, .. } = o1 { + let _ = db1.apply(Operation::Create{uuid: uuid.clone()}); + let _ = db2.apply(Operation::Create{uuid: uuid.clone()}); + } + + if let Operation::Update{ ref uuid, .. } = o2 { + let _ = db1.apply(Operation::Create{uuid: uuid.clone()}); + let _ = db2.apply(Operation::Create{uuid: uuid.clone()}); + } + + if let Operation::Delete{ ref uuid } = o1 { + let _ = db1.apply(Operation::Create{uuid: uuid.clone()}); + let _ = db2.apply(Operation::Create{uuid: uuid.clone()}); + } + + if let Operation::Delete{ ref uuid } = o2 { + let _ = db1.apply(Operation::Create{uuid: uuid.clone()}); + let _ = db2.apply(Operation::Create{uuid: uuid.clone()}); + } + + // if applying the initial operations fail, that indicates the operation was invalid + // in the base state, so consider the case successful. + if let Err(_) = db1.apply(o1) { + return Ok(()); + } + if let Err(_) = db2.apply(o2) { + return Ok(()); + } + + if let Some(o) = o2p { + db1.apply(o).map_err(|e| TestCaseError::Fail(format!("Applying to db1: {}", e).into()))?; + } + if let Some(o) = o1p { + db2.apply(o).map_err(|e| TestCaseError::Fail(format!("Applying to db2: {}", e).into()))?; + } + assert_eq!(db1.sorted_tasks(), db2.sorted_tasks()); + } + } } diff --git a/taskchampion/src/testing/mod.rs b/taskchampion/src/testing/mod.rs new file mode 100644 index 000000000..766fbbda5 --- /dev/null +++ b/taskchampion/src/testing/mod.rs @@ -0,0 +1 @@ +pub mod testserver; diff --git a/taskchampion/tests/shared/testserver.rs b/taskchampion/src/testing/testserver.rs similarity index 96% rename from taskchampion/tests/shared/testserver.rs rename to taskchampion/src/testing/testserver.rs index 2cb1cde2e..b81063b1b 100644 --- a/taskchampion/tests/shared/testserver.rs +++ b/taskchampion/src/testing/testserver.rs @@ -1,7 +1,7 @@ +use crate::server::{Blob, Server, VersionAdd}; use std::collections::HashMap; -use taskchampion::server::{Blob, Server, VersionAdd}; -pub struct TestServer { +pub(crate) struct TestServer { users: HashMap, } diff --git a/taskchampion/tests/data/tdb2-test.data b/taskchampion/tests/data/tdb2-test.data deleted file mode 100644 index 60477651b..000000000 --- a/taskchampion/tests/data/tdb2-test.data +++ /dev/null @@ -1,2 +0,0 @@ -[description:"https:\/\/phabricator.services.example.com\/D7364 &open;taskgraph&close; Download debian packages" end:"1541705209" entry:"1538520624" modified:"1541705209" phabricatorid:"D7364" priority:"M" project:"moz" status:"completed" tags:"phabricator,respond" uuid:"ca33f6d6-1688-4503-90be-3b3526a32b5a" wait:"1570118809"] -[annotation_1541461824:"https:\/\/github.com\/taskcluster\/taskcluster-worker-manager\/pull\/3" description:"https:\/\/github.com\/taskcluster\/taskcluster-worker-manager\/pull\/3 More changes" end:"1541702602" entry:"1541451283" githubbody:"some notes:\n\n1. This is a huge PR, so I'm not expecting a quick turn around at all. If you have questions, let me know and I can hope on Vidyo.\n1. Data persistence is written in a semi-janky way. My intention is to use the time while this is under review to make progress on postgres stuff so that the next review cycle will be using new Postgres things. Which means.... There's a lot of bugs in the concurrency because there's no synchronisation at all in this janky-ish model.\n1. The API is the minimum api required to get provisioning-ish things working\n1. I intend to write a system for automatically testing provider and bidding strategy implementations, so that you can do instantiate a provider\/strategy, stub\/spy it as needed then run a test suite against it and have it do its thing. The idea is that each provider will need to mock their underlying api system in their own way, but the set of tests we run for Provider API conformance would be pretty standardized. This should make writing tests for new providers a lot easier.\n1. The provider\/strategy loading system is intentionally simple. The idea is that these aren't general purpose plugins, but rather special ones. The idea is that the config files would essentially declare instances and then provide constructor arguments to initialize them all... This would make enabling\/disabling providers\/strategies fairly trivial\n1. I decided to drop fake implementations of providers and strategies for testing the provisioning logic and instead opt for Sinon stubs, which I think give us a better testing story\n1. I still intend to have fake providers and bidding strategies for doing API testing.\n\nLet me know, and again, I don't expect or need a quick turn around on this PR.\n" githubcreatedon:"1541451283" githubnamespace:"djmitche" githubnumber:"3.000000" githubrepo:"taskcluster\/taskcluster-worker-manager" githubtitle:"More changes" githubtype:"pull_request" githubupdatedat:"1541699191" githuburl:"https:\/\/github.com\/taskcluster\/taskcluster-worker-manager\/pull\/3" githubuser:"jhford" modified:"1541702602" priority:"H" project:"moz" status:"completed" tags:"respond" uuid:"2186f981-d1f5-4642-b833-5b16b3a2d334"] diff --git a/taskchampion/tests/operation_transform_invariant.rs b/taskchampion/tests/operation_transform_invariant.rs deleted file mode 100644 index 461aba009..000000000 --- a/taskchampion/tests/operation_transform_invariant.rs +++ /dev/null @@ -1,85 +0,0 @@ -use chrono::Utc; -use proptest::prelude::*; -use taskchampion::{taskstorage, Operation, DB}; -use uuid::Uuid; - -fn newdb() -> DB { - DB::new(Box::new(taskstorage::InMemoryStorage::new())) -} - -fn uuid_strategy() -> impl Strategy { - prop_oneof![ - Just(Uuid::parse_str("83a2f9ef-f455-4195-b92e-a54c161eebfc").unwrap()), - Just(Uuid::parse_str("56e0be07-c61f-494c-a54c-bdcfdd52d2a7").unwrap()), - Just(Uuid::parse_str("4b7ed904-f7b0-4293-8a10-ad452422c7b3").unwrap()), - Just(Uuid::parse_str("9bdd0546-07c8-4e1f-a9bc-9d6299f4773b").unwrap()), - ] -} - -fn operation_strategy() -> impl Strategy { - prop_oneof![ - uuid_strategy().prop_map(|uuid| Operation::Create { uuid }), - uuid_strategy().prop_map(|uuid| Operation::Delete { uuid }), - (uuid_strategy(), "(title|project|status)").prop_map(|(uuid, property)| { - Operation::Update { - uuid, - property, - value: Some("true".into()), - timestamp: Utc::now(), - } - }), - ] -} - -proptest! { - #![proptest_config(ProptestConfig { - cases: 1024, .. ProptestConfig::default() - })] - #[test] - // check that the two operation sequences have the same effect, enforcing the invariant of - // the transform function. - fn transform_invariant_holds(o1 in operation_strategy(), o2 in operation_strategy()) { - let (o1p, o2p) = Operation::transform(o1.clone(), o2.clone()); - - let mut db1 = newdb(); - let mut db2 = newdb(); - - // Ensure that any expected tasks already exist - if let Operation::Update{ ref uuid, .. } = o1 { - let _ = db1.apply(Operation::Create{uuid: uuid.clone()}); - let _ = db2.apply(Operation::Create{uuid: uuid.clone()}); - } - - if let Operation::Update{ ref uuid, .. } = o2 { - let _ = db1.apply(Operation::Create{uuid: uuid.clone()}); - let _ = db2.apply(Operation::Create{uuid: uuid.clone()}); - } - - if let Operation::Delete{ ref uuid } = o1 { - let _ = db1.apply(Operation::Create{uuid: uuid.clone()}); - let _ = db2.apply(Operation::Create{uuid: uuid.clone()}); - } - - if let Operation::Delete{ ref uuid } = o2 { - let _ = db1.apply(Operation::Create{uuid: uuid.clone()}); - let _ = db2.apply(Operation::Create{uuid: uuid.clone()}); - } - - // if applying the initial operations fail, that indicates the operation was invalid - // in the base state, so consider the case successful. - if let Err(_) = db1.apply(o1) { - return Ok(()); - } - if let Err(_) = db2.apply(o2) { - return Ok(()); - } - - if let Some(o) = o2p { - db1.apply(o).map_err(|e| TestCaseError::Fail(format!("Applying to db1: {}", e).into()))?; - } - if let Some(o) = o1p { - db2.apply(o).map_err(|e| TestCaseError::Fail(format!("Applying to db2: {}", e).into()))?; - } - assert_eq!(db1.sorted_tasks(), db2.sorted_tasks()); - } -} diff --git a/taskchampion/tests/shared/mod.rs b/taskchampion/tests/shared/mod.rs deleted file mode 100644 index 8a5a5ee0c..000000000 --- a/taskchampion/tests/shared/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod testserver; - -pub use testserver::TestServer; diff --git a/taskchampion/tests/sync.rs b/taskchampion/tests/sync.rs deleted file mode 100644 index 197708dee..000000000 --- a/taskchampion/tests/sync.rs +++ /dev/null @@ -1,123 +0,0 @@ -use chrono::Utc; -use taskchampion::{taskstorage, Operation, DB}; -use uuid::Uuid; - -mod shared; -use shared::TestServer; - -fn newdb() -> DB { - DB::new(Box::new(taskstorage::InMemoryStorage::new())) -} - -#[test] -fn test_sync() { - let mut server = TestServer::new(); - - let mut db1 = newdb(); - db1.sync("me", &mut server).unwrap(); - - let mut db2 = newdb(); - db2.sync("me", &mut server).unwrap(); - - // make some changes in parallel to db1 and db2.. - let uuid1 = Uuid::new_v4(); - db1.apply(Operation::Create { uuid: uuid1 }).unwrap(); - db1.apply(Operation::Update { - uuid: uuid1, - property: "title".into(), - value: Some("my first task".into()), - timestamp: Utc::now(), - }) - .unwrap(); - - let uuid2 = Uuid::new_v4(); - db2.apply(Operation::Create { uuid: uuid2 }).unwrap(); - db2.apply(Operation::Update { - uuid: uuid2, - property: "title".into(), - value: Some("my second task".into()), - timestamp: Utc::now(), - }) - .unwrap(); - - // and synchronize those around - db1.sync("me", &mut server).unwrap(); - db2.sync("me", &mut server).unwrap(); - db1.sync("me", &mut server).unwrap(); - assert_eq!(db1.sorted_tasks(), db2.sorted_tasks()); - - // now make updates to the same task on both sides - db1.apply(Operation::Update { - uuid: uuid2, - property: "priority".into(), - value: Some("H".into()), - timestamp: Utc::now(), - }) - .unwrap(); - db2.apply(Operation::Update { - uuid: uuid2, - property: "project".into(), - value: Some("personal".into()), - timestamp: Utc::now(), - }) - .unwrap(); - - // and synchronize those around - db1.sync("me", &mut server).unwrap(); - db2.sync("me", &mut server).unwrap(); - db1.sync("me", &mut server).unwrap(); - assert_eq!(db1.sorted_tasks(), db2.sorted_tasks()); -} - -#[test] -fn test_sync_create_delete() { - let mut server = TestServer::new(); - - let mut db1 = newdb(); - db1.sync("me", &mut server).unwrap(); - - let mut db2 = newdb(); - db2.sync("me", &mut server).unwrap(); - - // create and update a task.. - let uuid = Uuid::new_v4(); - db1.apply(Operation::Create { uuid }).unwrap(); - db1.apply(Operation::Update { - uuid: uuid, - property: "title".into(), - value: Some("my first task".into()), - timestamp: Utc::now(), - }) - .unwrap(); - - // and synchronize those around - db1.sync("me", &mut server).unwrap(); - db2.sync("me", &mut server).unwrap(); - db1.sync("me", &mut server).unwrap(); - assert_eq!(db1.sorted_tasks(), db2.sorted_tasks()); - - // delete and re-create the task on db1 - db1.apply(Operation::Delete { uuid }).unwrap(); - db1.apply(Operation::Create { uuid }).unwrap(); - db1.apply(Operation::Update { - uuid: uuid, - property: "title".into(), - value: Some("my second task".into()), - timestamp: Utc::now(), - }) - .unwrap(); - - // and on db2, update a property of the task - db2.apply(Operation::Update { - uuid: uuid, - property: "project".into(), - value: Some("personal".into()), - timestamp: Utc::now(), - }) - .unwrap(); - - db1.sync("me", &mut server).unwrap(); - db2.sync("me", &mut server).unwrap(); - db1.sync("me", &mut server).unwrap(); - assert_eq!(db1.sorted_tasks(), db2.sorted_tasks()); -} diff --git a/taskchampion/tests/sync_action_sequences.rs b/taskchampion/tests/sync_action_sequences.rs deleted file mode 100644 index 2c94d6dd8..000000000 --- a/taskchampion/tests/sync_action_sequences.rs +++ /dev/null @@ -1,72 +0,0 @@ -use chrono::Utc; -use proptest::prelude::*; -use taskchampion::{taskstorage, Operation, DB}; -use uuid::Uuid; - -mod shared; -use shared::TestServer; - -fn newdb() -> DB { - DB::new(Box::new(taskstorage::InMemoryStorage::new())) -} - -#[derive(Debug)] -enum Action { - Op(Operation), - Sync, -} - -fn action_sequence_strategy() -> impl Strategy> { - // Create, Update, Delete, or Sync on client 1, 2, .., followed by a round of syncs - "([CUDS][123])*S1S2S3S1S2".prop_map(|seq| { - let uuid = Uuid::parse_str("83a2f9ef-f455-4195-b92e-a54c161eebfc").unwrap(); - seq.as_bytes() - .chunks(2) - .map(|action_on| { - let action = match action_on[0] { - b'C' => Action::Op(Operation::Create { uuid }), - b'U' => Action::Op(Operation::Update { - uuid, - property: "title".into(), - value: Some("foo".into()), - timestamp: Utc::now(), - }), - b'D' => Action::Op(Operation::Delete { uuid }), - b'S' => Action::Sync, - _ => unreachable!(), - }; - let acton = action_on[1] - b'1'; - (action, acton) - }) - .collect::>() - }) -} - -proptest! { - #[test] - // check that various sequences of operations on mulitple db's do not get the db's into an - // incompatible state. The main concern here is that there might be a sequence of create - // and delete operations that results in a task existing in one DB but not existing in - // another. So, the generated sequences focus on a single task UUID. - fn transform_sequences_of_operations(action_sequence in action_sequence_strategy()) { - let mut server = TestServer::new(); - let mut dbs = [newdb(), newdb(), newdb()]; - - for (action, db) in action_sequence { - println!("{:?} on db {}", action, db); - - let db = &mut dbs[db as usize]; - match action { - Action::Op(op) => { - if let Err(e) = db.apply(op) { - println!(" {:?} (ignored)", e); - } - }, - Action::Sync => db.sync("me", &mut server).unwrap(), - } - } - - assert_eq!(dbs[0].sorted_tasks(), dbs[0].sorted_tasks()); - assert_eq!(dbs[1].sorted_tasks(), dbs[2].sorted_tasks()); - } -} From 2296d0fa3521853a939a256909004243013eccbc Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 23 Nov 2020 16:03:04 -0500 Subject: [PATCH 5/6] rename DB to TaskDB for consistency --- taskchampion/src/replica.rs | 6 +-- taskchampion/src/taskdb.rs | 50 +++++++++++------------ taskchampion/src/taskstorage/mod.rs | 2 +- taskchampion/src/taskstorage/operation.rs | 10 ++--- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/taskchampion/src/replica.rs b/taskchampion/src/replica.rs index 874e74eca..a3df48f7e 100644 --- a/taskchampion/src/replica.rs +++ b/taskchampion/src/replica.rs @@ -1,7 +1,7 @@ use crate::errors::Error; use crate::server::Server; use crate::task::{Status, Task}; -use crate::taskdb::DB; +use crate::taskdb::TaskDB; use crate::taskstorage::{Operation, TaskMap, TaskStorage}; use chrono::Utc; use failure::Fallible; @@ -11,13 +11,13 @@ use uuid::Uuid; /// A replica represents an instance of a user's task data, providing an easy interface /// for querying and modifying that data. pub struct Replica { - taskdb: DB, + taskdb: TaskDB, } impl Replica { pub fn new(storage: Box) -> Replica { return Replica { - taskdb: DB::new(storage), + taskdb: TaskDB::new(storage), }; } diff --git a/taskchampion/src/taskdb.rs b/taskchampion/src/taskdb.rs index 7fe9362fc..2221326c7 100644 --- a/taskchampion/src/taskdb.rs +++ b/taskchampion/src/taskdb.rs @@ -7,7 +7,7 @@ use std::collections::HashSet; use std::str; use uuid::Uuid; -pub struct DB { +pub struct TaskDB { storage: Box, } @@ -17,24 +17,24 @@ struct Version { operations: Vec, } -impl DB { - /// Create a new DB with the given backend storage - pub fn new(storage: Box) -> DB { - DB { storage } +impl TaskDB { + /// Create a new TaskDB with the given backend storage + pub fn new(storage: Box) -> TaskDB { + TaskDB { storage } } #[cfg(test)] - pub fn new_inmemory() -> DB { - DB::new(Box::new(crate::taskstorage::InMemoryStorage::new())) + pub fn new_inmemory() -> TaskDB { + TaskDB::new(Box::new(crate::taskstorage::InMemoryStorage::new())) } - /// Apply an operation to the DB. Aside from synchronization operations, this is the only way - /// to modify the DB. In cases where an operation does not make sense, this function will do - /// nothing and return an error (but leave the DB in a consistent state). + /// Apply an operation to the TaskDB. Aside from synchronization operations, this is the only way + /// to modify the TaskDB. In cases where an operation does not make sense, this function will do + /// nothing and return an error (but leave the TaskDB in a consistent state). pub fn apply(&mut self, op: Operation) -> Fallible<()> { // TODO: differentiate error types here? let mut txn = self.storage.txn()?; - if let err @ Err(_) = DB::apply_op(txn.as_mut(), &op) { + if let err @ Err(_) = TaskDB::apply_op(txn.as_mut(), &op) { return err; } txn.add_operation(op)?; @@ -183,7 +183,7 @@ impl DB { assert_eq!(version.version, txn.base_version()? + 1); println!("applying version {:?} from server", version.version); - DB::apply_version(txn.as_mut(), version)?; + TaskDB::apply_version(txn.as_mut(), version)?; } let operations: Vec = txn.operations()?.iter().map(|o| o.clone()).collect(); @@ -254,7 +254,7 @@ impl DB { } } if let Some(o) = svr_op { - if let Err(e) = DB::apply_op(txn, &o) { + if let Err(e) = TaskDB::apply_op(txn, &o) { println!("Invalid operation when syncing: {} (ignored)", e); } } @@ -309,7 +309,7 @@ mod tests { #[test] fn test_apply_create() { - let mut db = DB::new_inmemory(); + let mut db = TaskDB::new_inmemory(); let uuid = Uuid::new_v4(); let op = Operation::Create { uuid }; db.apply(op.clone()).unwrap(); @@ -320,7 +320,7 @@ mod tests { #[test] fn test_apply_create_exists() { - let mut db = DB::new_inmemory(); + let mut db = TaskDB::new_inmemory(); let uuid = Uuid::new_v4(); let op = Operation::Create { uuid }; db.apply(op.clone()).unwrap(); @@ -335,7 +335,7 @@ mod tests { #[test] fn test_apply_create_update() { - let mut db = DB::new_inmemory(); + let mut db = TaskDB::new_inmemory(); let uuid = Uuid::new_v4(); let op1 = Operation::Create { uuid }; db.apply(op1.clone()).unwrap(); @@ -356,7 +356,7 @@ mod tests { #[test] fn test_apply_create_update_delete_prop() { - let mut db = DB::new_inmemory(); + let mut db = TaskDB::new_inmemory(); let uuid = Uuid::new_v4(); let op1 = Operation::Create { uuid }; db.apply(op1.clone()).unwrap(); @@ -398,7 +398,7 @@ mod tests { #[test] fn test_apply_update_does_not_exist() { - let mut db = DB::new_inmemory(); + let mut db = TaskDB::new_inmemory(); let uuid = Uuid::new_v4(); let op = Operation::Update { uuid, @@ -417,7 +417,7 @@ mod tests { #[test] fn test_apply_create_delete() { - let mut db = DB::new_inmemory(); + let mut db = TaskDB::new_inmemory(); let uuid = Uuid::new_v4(); let op1 = Operation::Create { uuid }; db.apply(op1.clone()).unwrap(); @@ -431,7 +431,7 @@ mod tests { #[test] fn test_apply_delete_not_present() { - let mut db = DB::new_inmemory(); + let mut db = TaskDB::new_inmemory(); let uuid = Uuid::new_v4(); let op1 = Operation::Delete { uuid }; @@ -446,7 +446,7 @@ mod tests { #[test] fn rebuild_working_set() -> Fallible<()> { - let mut db = DB::new_inmemory(); + let mut db = TaskDB::new_inmemory(); let uuids = vec![ Uuid::new_v4(), // 0: pending, not already in working set Uuid::new_v4(), // 1: pending, already in working set @@ -455,7 +455,7 @@ mod tests { Uuid::new_v4(), // 4: pending, already in working set ]; - // add everything to the DB + // add everything to the TaskDB for uuid in &uuids { db.apply(Operation::Create { uuid: uuid.clone() })?; } @@ -513,8 +513,8 @@ mod tests { Ok(()) } - fn newdb() -> DB { - DB::new(Box::new(InMemoryStorage::new())) + fn newdb() -> TaskDB { + TaskDB::new(Box::new(InMemoryStorage::new())) } #[test] @@ -666,7 +666,7 @@ mod tests { #[test] // check that various sequences of operations on mulitple db's do not get the db's into an // incompatible state. The main concern here is that there might be a sequence of create - // and delete operations that results in a task existing in one DB but not existing in + // and delete operations that results in a task existing in one TaskDB but not existing in // another. So, the generated sequences focus on a single task UUID. fn transform_sequences_of_operations(action_sequence in action_sequence_strategy()) { let mut server = TestServer::new(); diff --git a/taskchampion/src/taskstorage/mod.rs b/taskchampion/src/taskstorage/mod.rs index bc0fb30a3..699b59ff2 100644 --- a/taskchampion/src/taskstorage/mod.rs +++ b/taskchampion/src/taskstorage/mod.rs @@ -68,7 +68,7 @@ pub trait TaskStorageTxn { fn operations<'a>(&mut self) -> Fallible>; /// Add an operation to the end of the list of operations in the storage. Note that this - /// merely *stores* the operation; it is up to the DB to apply it. + /// merely *stores* the operation; it is up to the TaskDB to apply it. fn add_operation(&mut self, op: Operation) -> Fallible<()>; /// Replace the current list of operations with a new list. diff --git a/taskchampion/src/taskstorage/operation.rs b/taskchampion/src/taskstorage/operation.rs index f8927a133..52bc55d56 100644 --- a/taskchampion/src/taskstorage/operation.rs +++ b/taskchampion/src/taskstorage/operation.rs @@ -128,7 +128,7 @@ impl Operation { #[cfg(test)] mod test { use super::*; - use crate::taskdb::DB; + use crate::taskdb::TaskDB; use crate::taskstorage::InMemoryStorage; use chrono::{Duration, Utc}; use proptest::prelude::*; @@ -148,7 +148,7 @@ mod test { // check that the two operation sequences have the same effect, enforcing the invariant of // the transform function. - let mut db1 = DB::new_inmemory(); + let mut db1 = TaskDB::new_inmemory(); if let Some(ref o) = setup { db1.apply(o.clone()).unwrap(); } @@ -157,7 +157,7 @@ mod test { db1.apply(o).unwrap(); } - let mut db2 = DB::new_inmemory(); + let mut db2 = TaskDB::new_inmemory(); if let Some(ref o) = setup { db2.apply(o.clone()).unwrap(); } @@ -310,8 +310,8 @@ mod test { fn transform_invariant_holds(o1 in operation_strategy(), o2 in operation_strategy()) { let (o1p, o2p) = Operation::transform(o1.clone(), o2.clone()); - let mut db1 = DB::new(Box::new(InMemoryStorage::new())); - let mut db2 = DB::new(Box::new(InMemoryStorage::new())); + let mut db1 = TaskDB::new(Box::new(InMemoryStorage::new())); + let mut db2 = TaskDB::new(Box::new(InMemoryStorage::new())); // Ensure that any expected tasks already exist if let Operation::Update{ ref uuid, .. } = o1 { From 084c978b31c03693c35321e834a896d2aa48e649 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Mon, 23 Nov 2020 16:07:35 -0500 Subject: [PATCH 6/6] move docs back to top level --- {taskchampion/docs => docs}/.gitignore | 0 {taskchampion/docs => docs}/README.md | 0 {taskchampion/docs => docs}/book.toml | 0 {taskchampion/docs => docs}/src/SUMMARY.md | 0 {taskchampion/docs => docs}/src/data-model.md | 0 {taskchampion/docs => docs}/src/installation.md | 0 {taskchampion/docs => docs}/src/plans.md | 0 {taskchampion/docs => docs}/src/storage.md | 0 {taskchampion/docs => docs}/src/sync.md | 0 {taskchampion/docs => docs}/src/taskdb.md | 0 {taskchampion/docs => docs}/src/tasks.md | 0 {taskchampion/docs => docs}/src/usage.md | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename {taskchampion/docs => docs}/.gitignore (100%) rename {taskchampion/docs => docs}/README.md (100%) rename {taskchampion/docs => docs}/book.toml (100%) rename {taskchampion/docs => docs}/src/SUMMARY.md (100%) rename {taskchampion/docs => docs}/src/data-model.md (100%) rename {taskchampion/docs => docs}/src/installation.md (100%) rename {taskchampion/docs => docs}/src/plans.md (100%) rename {taskchampion/docs => docs}/src/storage.md (100%) rename {taskchampion/docs => docs}/src/sync.md (100%) rename {taskchampion/docs => docs}/src/taskdb.md (100%) rename {taskchampion/docs => docs}/src/tasks.md (100%) rename {taskchampion/docs => docs}/src/usage.md (100%) diff --git a/taskchampion/docs/.gitignore b/docs/.gitignore similarity index 100% rename from taskchampion/docs/.gitignore rename to docs/.gitignore diff --git a/taskchampion/docs/README.md b/docs/README.md similarity index 100% rename from taskchampion/docs/README.md rename to docs/README.md diff --git a/taskchampion/docs/book.toml b/docs/book.toml similarity index 100% rename from taskchampion/docs/book.toml rename to docs/book.toml diff --git a/taskchampion/docs/src/SUMMARY.md b/docs/src/SUMMARY.md similarity index 100% rename from taskchampion/docs/src/SUMMARY.md rename to docs/src/SUMMARY.md diff --git a/taskchampion/docs/src/data-model.md b/docs/src/data-model.md similarity index 100% rename from taskchampion/docs/src/data-model.md rename to docs/src/data-model.md diff --git a/taskchampion/docs/src/installation.md b/docs/src/installation.md similarity index 100% rename from taskchampion/docs/src/installation.md rename to docs/src/installation.md diff --git a/taskchampion/docs/src/plans.md b/docs/src/plans.md similarity index 100% rename from taskchampion/docs/src/plans.md rename to docs/src/plans.md diff --git a/taskchampion/docs/src/storage.md b/docs/src/storage.md similarity index 100% rename from taskchampion/docs/src/storage.md rename to docs/src/storage.md diff --git a/taskchampion/docs/src/sync.md b/docs/src/sync.md similarity index 100% rename from taskchampion/docs/src/sync.md rename to docs/src/sync.md diff --git a/taskchampion/docs/src/taskdb.md b/docs/src/taskdb.md similarity index 100% rename from taskchampion/docs/src/taskdb.md rename to docs/src/taskdb.md diff --git a/taskchampion/docs/src/tasks.md b/docs/src/tasks.md similarity index 100% rename from taskchampion/docs/src/tasks.md rename to docs/src/tasks.md diff --git a/taskchampion/docs/src/usage.md b/docs/src/usage.md similarity index 100% rename from taskchampion/docs/src/usage.md rename to docs/src/usage.md