diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..4f294f36 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,12 @@ +Thank you for using `crev`! + +If you want to ask a question, try crev's gitter channel first: https://gitter.im/dpc/crev . You might get a faster answer. + +If you want to report an issue, please go through the following checklist: + +* If a command is failing try running it with `RUST_BACKTRACE=1` +* Paste the exact command and error and/or outputs (along with the backtrace) +* Please let us know: + * Which version are you using (eg. `cargo crev --version`) + * How did you install `crev` (git?, cargo?, your distribution?) + * What OS/platform are you running on diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..8ba8ed2a --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,4 @@ +Checklist: + +* [ ] `cargo +nightly fmt --all` +* [ ] Modify `CHANGELOG.md` if applicable diff --git a/.gitignore b/.gitignore index c4ba454f..3a2fe796 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ /target target/ **/*.rs.bk + +# Intellij project files +*.iml +.idea diff --git a/.travis.yml b/.travis.yml index 3185fe91..45ac88d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,3 +8,8 @@ rust: - stable - beta - nightly + +script: + - if [ "$TRAVIS_RUST_VERSION" == "stable" ] ; then rustup component add rustfmt && cargo fmt --all -- --check; fi + - cargo build --verbose + - cargo test --verbose diff --git a/Cargo.lock b/Cargo.lock index 83b2d69c..5908d3d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,31 +5,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "aes" -version = "0.1.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aes-soft 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "aesni 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "block-cipher-trait 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "aes-soft" -version = "0.1.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "block-cipher-trait 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "aesni" -version = "0.3.5" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "block-cipher-trait 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -48,17 +48,6 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "app_dirs" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "shell32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "argon2rs" version = "0.2.5" @@ -91,6 +80,11 @@ dependencies = [ "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "arrayref" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "arrayvec" version = "0.4.8" @@ -192,28 +186,20 @@ dependencies = [ ] [[package]] -name = "block-cipher-trait" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-modes" -version = "0.1.0" +name = "block-buffer" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "block-cipher-trait 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "block-padding 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "block-padding" -version = "0.1.2" +name = "block-cipher-trait" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -305,15 +291,15 @@ dependencies = [ [[package]] name = "cargo-crev" -version = "0.3.0" +version = "0.4.0" dependencies = [ "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "cargo 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "common_failures 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "crates_io_api 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crev-common 0.3.0", - "crev-data 0.3.0", - "crev-lib 0.3.0", + "crev-common 0.4.0", + "crev-data 0.4.0", + "crev-lib 0.4.0", "default 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -323,6 +309,7 @@ dependencies = [ "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokei 8.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -410,12 +397,12 @@ dependencies = [ [[package]] name = "cmac" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "block-cipher-trait 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-mac 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dbl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dbl 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -521,32 +508,16 @@ name = "crc32fast" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "crev-bin" -version = "0.1.0" -dependencies = [ - "common_failures 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crev-common 0.3.0", - "crev-data 0.3.0", - "crev-lib 0.3.0", - "default 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "quicli 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rpassword 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rprompt 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "crev-common" -version = "0.3.0" +version = "0.4.0" dependencies = [ "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "rpassword 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rprompt 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", @@ -555,18 +526,17 @@ dependencies = [ [[package]] name = "crev-data" -version = "0.3.0" +version = "0.4.0" dependencies = [ "argonautica 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "common_failures 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crev-common 0.3.0", + "crev-common 0.4.0", "derive_builder 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "ed25519-dalek 1.0.0-pre.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "miscreant 0.4.0-beta2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", @@ -575,23 +545,24 @@ dependencies = [ [[package]] name = "crev-lib" -version = "0.3.0" +version = "0.4.0" dependencies = [ - "app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "argonautica 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "common_failures 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crev-common 0.3.0", - "crev-data 0.3.0", + "crev-common 0.4.0", + "crev-data 0.4.0", "crev-recursive-digest 0.2.1", "default 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "derive_builder 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "git2 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "miscreant 0.4.0-beta2 (registry+https://github.com/rust-lang/crates.io-index)", + "insideout 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "miscreant 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "resiter-dpc-tmp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rprompt 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -599,8 +570,8 @@ dependencies = [ "serde_cbor 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)", + "shell-escape 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -624,7 +595,7 @@ version = "0.3.0" dependencies = [ "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "common_failures 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crev-common 0.3.0", + "crev-common 0.4.0", "crev-recursive-digest 0.2.1", "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -735,20 +706,20 @@ dependencies = [ [[package]] name = "crypto-mac" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "crypto-mac" -version = "0.7.0" +name = "ctr" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -790,7 +761,7 @@ dependencies = [ "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.0.0-pre.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -826,10 +797,10 @@ dependencies = [ [[package]] name = "dbl" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -860,6 +831,14 @@ dependencies = [ "syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "digest" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "digest" version = "0.8.0" @@ -868,6 +847,15 @@ dependencies = [ "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "directories" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "dirs" version = "1.0.4" @@ -907,6 +895,14 @@ dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "encoding_rs_io" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "encoding_rs 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "env_logger" version = "0.5.13" @@ -939,6 +935,11 @@ dependencies = [ "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "filetime" version = "0.2.4" @@ -1104,6 +1105,22 @@ name = "half" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "handlebars" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "heck" version = "0.3.0" @@ -1224,6 +1241,11 @@ name = "indexmap" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "insideout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "iovec" version = "0.1.2" @@ -1360,6 +1382,11 @@ dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "maplit" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "matches" version = "0.1.8" @@ -1485,18 +1512,21 @@ dependencies = [ [[package]] name = "miscreant" -version = "0.4.0-beta2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "block-modes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "cmac 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-mac 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dbl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pmac 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cmac 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dbl 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pmac 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1568,20 +1598,6 @@ dependencies = [ "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "ole32-sys" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "opaque-debug" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "opaque-debug" version = "0.2.1" @@ -1665,6 +1681,45 @@ name = "percent-encoding" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "pest" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ucd-trie 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_generator 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pest_generator" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_meta 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pest_meta" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "phf" version = "0.7.23" @@ -1707,12 +1762,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "pmac" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "block-cipher-trait 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-mac 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dbl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dbl 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2167,18 +2222,20 @@ dependencies = [ ] [[package]] -name = "shell-escape" -version = "0.1.4" +name = "sha-1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] -name = "shell32-sys" -version = "0.1.2" +name = "shell-escape" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "siphasher" @@ -2214,6 +2271,14 @@ name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "stream-cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "string" version = "0.1.2" @@ -2244,11 +2309,6 @@ dependencies = [ "syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "subtle" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "subtle" version = "1.0.0" @@ -2256,7 +2316,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "subtle" -version = "2.0.0-pre.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2321,6 +2381,16 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "term_size" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "termcolor" version = "1.0.4" @@ -2365,6 +2435,23 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tokei" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs_io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", + "handlebars 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ignore 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", + "term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tokio" version = "0.1.13" @@ -2538,6 +2625,11 @@ name = "typenum" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ucd-trie" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ucd-util" version = "0.1.3" @@ -2721,11 +2813,6 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "xdg" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "yaml-rust" version = "0.4.2" @@ -2734,16 +2821,21 @@ dependencies = [ "linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "zeroize" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [metadata] "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" -"checksum aes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0275405eedf13afd19de588add12a3b0d481a50b194eeb826e9dece11e741331" -"checksum aes-soft 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91f401742d8c1b0a3d01f53563f98d8ef0beea460b8d37322faf9fb4c7977cfa" -"checksum aesni 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ca074691b47c3dc585e05e45f6d069c75d0209069ca09b1c49ea37720e7b5f" +"checksum aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9" +"checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" +"checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -"checksum app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e73a24bad9bd6a94d6395382a6c69fe071708ae4409f763c5475e14ee896313d" "checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" "checksum argonautica 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0b56f74d1eb6894f7e3726b704fd6be2c177bf8b8c39a2c33bddd94718d877c7" +"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" "checksum arrayvec 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f405cc4c21cd8b784f6c8fc2adf9bc00f59558f0049b5ec21517f875963040cc" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" @@ -2754,9 +2846,8 @@ dependencies = [ "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91721a6330935673395a0607df4d49a9cb90ae12d259f1b3e0a3f6e1d486872e" "checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" -"checksum block-cipher-trait 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "370424437b9459f3dfd68428ed9376ddfe03d8b70ede29cc533b3557df186ab4" -"checksum block-modes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "862511b40f91a3305dc119fdfdc25b561779f78828495e41b71d360b8c9f56df" -"checksum block-padding 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc4358306e344bf9775d0197fd00d2603e5afb0771bb353538630f022068ea3" +"checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" +"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" "checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" "checksum byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "980479e6fde23246dfb54d47580d66b4e99202e7579c5eaa9fe10ecb5ebd2182" @@ -2773,7 +2864,7 @@ dependencies = [ "checksum clap-verbosity-flag 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26de1a8b4a21f6b05c99680edd48a6fded30afcf7aa13f1fa7e5c79198f5bac7" "checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum cmac 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44f175b5f76aa82ebe4c7e85ef95b23e9293c5618db28461cb10ee929e0f6e2f" +"checksum cmac 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6f4a435124bcc292eba031f1f725d7abacdaf13cbf9f935450e8c45aa9e96cad" "checksum common_failures 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c84afb517ac0988b7e049e06c51511716f80d0f0233972fca666d3f14f80d8fb" "checksum commoncrypto 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d056a8586ba25a1e4d61cb090900e495952c7886786fc55f909ab2f819b69007" "checksum commoncrypto-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1fed34f46747aa73dfaa578069fd8279d2818ade2b55f38f22a9401c7f4083e2" @@ -2796,27 +2887,31 @@ dependencies = [ "checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015" "checksum crossbeam-utils 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c55913cc2799171a550e307918c0a360e8c16004820291bf3b638969b4a01816" "checksum crypto-hash 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "09de9ee0fc255ace04c7fa0763c9395a945c37c8292bb554f8d48361d1dcf1b4" -"checksum crypto-mac 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7afa06d05a046c7a47c3a849907ec303504608c927f4e85f7bfff22b7180d971" "checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" +"checksum ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "044f882973b245404e90c90e7e42e8ee8d7a64edfd7adf83d684fb97e8e2c1b6" "checksum curl 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)" = "c7c9d851c825e0c033979d4516c9173bc19a78a96eb4d6ae51d4045440eafa16" "checksum curl-sys 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)" = "721c204978be2143fab0a84b708c49d79d1f6100b8785610f456043a90708870" "checksum curve25519-dalek 1.0.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)" = "692ddc2371febea60ce125bac83c4289547565bd94d0e0c870bbb86ff0eda768" "checksum darling 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f000e7b03a0083a30e1f10b1428a530849c21e72b338fa76869b5dbc4b045bf" "checksum darling_core 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "86bc5ce438f4b703755d12f59bbf0a16c642766d4534e922db47569dbdd0b998" "checksum darling_macro 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9973050ba46be2a2935a7b316147f41a808ac604b8f0fef6eba77fd47a89daeb" -"checksum dbl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "920e117b69060a961c4164ccf83af573292cb167ccdd918950bcf0f5afc32c1c" +"checksum dbl 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6c40b13b561e11560d7b12785e74113a3163df617e2fbce60ce1764e0b270eaa" "checksum default 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f33853de3c94db27f825215641be74ddce259f5aec7aefae9c760ee1bb3dbaa7" "checksum derive_builder 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15d9e4f0be540b522e95c1de6200be0b12946fdd8408c093a1948de638e16f55" "checksum derive_builder_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1cffc940f53a89045824e676302b840a5a60d447560704d352316e2039125a2" +"checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" "checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" +"checksum directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "72d337a64190607d4fcca2cb78982c5dd57f4916e19696b48a575fa746b6cb0f" "checksum dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "88972de891f6118092b643d85a0b28e0678e0f948d7f879aa32f2d5aafe97d2a" "checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" "checksum ed25519-dalek 1.0.0-pre.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d617dd712105b5985fade607438d91de39c4191f0f1bf56cba3bb60c172e2c72" "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" "checksum encoding_rs 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1a8fa54e6689eb2549c4efed8d00d7f3b2b994a064555b0e8df4ae3764bcc4be" +"checksum encoding_rs_io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "098f6a0ab73a9ba256b71344dc82c6d7e252736ad9db7f4e35345f3a1f8713f5" "checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" "checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7" "checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596" +"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a2df5c1a8c4be27e7707789dc42ae65976e60b394afd293d1419ab915833e646" "checksum flate2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2291c165c8e703ee54ef3055ad6188e3d51108e2ded18e9f2476e774fc5ad3d4" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" @@ -2836,6 +2931,7 @@ dependencies = [ "checksum globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4743617a7464bbda3c8aec8558ff2f9429047e025771037df561d383337ff865" "checksum h2 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "1ac030ae20dee464c5d0f36544d8b914a6bc606da44a57e052d2b0f5dae129e0" "checksum half 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9353c2a89d550b58fa0061d8ed8d002a7d8cdf2494eb0e432859bd3a9e543836" +"checksum handlebars 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d82e5750d8027a97b9640e3fefa66bbaf852a35228e1c90790efd13c4b09c166" "checksum heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea04fa3ead4e05e51a7c806fc07271fdbde4e246a6c6d1efd52e72230b771b82" "checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" "checksum home 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "80dff82fb58cfbbc617fb9a9184b010be0529201553cda50ad04372bc2333aff" @@ -2848,6 +2944,7 @@ dependencies = [ "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum ignore 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "36ecfc5ad80f0b1226df948c562e2cddd446096be3f644c95106400eae8a5e01" "checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" +"checksum insideout 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2d154ccd0b9a143b3d7ef692673ec2af7f81e2b9eb0c5cf616daefa6a7b0967" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" "checksum jobserver 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "dd80e58f77e0cdea53ba96acc5e04479e5ffc5d869626a6beafe50fed867eace" @@ -2864,6 +2961,7 @@ dependencies = [ "checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" +"checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" "checksum memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0a3eb002f0535929f1199681417029ebea04aadc0c7a4224b46be99c7f5d6a16" @@ -2877,7 +2975,7 @@ dependencies = [ "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226" -"checksum miscreant 0.4.0-beta2 (registry+https://github.com/rust-lang/crates.io-index)" = "ed87c1c62b5b8fd24c5148aa5ec073467b6eeddc68470675fac711d8156a5b53" +"checksum miscreant 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94bc83c2681a9c8a2063a9d897446e34472d65acda6146f2d9afcb256f6e1767" "checksum native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8e08de0070bbf4c31f452ea2a70db092f36f6f2e4d897adf5674477d488fb2" "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" @@ -2886,8 +2984,6 @@ dependencies = [ "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" -"checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c" -"checksum opaque-debug 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d620c9c26834b34f039489ac0dfdb12c7ac15ccaf818350a64c9b5334a452ad7" "checksum opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "51ecbcb821e1bd256d456fe858aaa7f380b63863eab2eb86eee1bd9f33dd6682" "checksum opener 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "04b1d6b086d9b3009550f9b6f81b10ad9428cf14f404b8e1a3a06f6f012c8ec9" "checksum openssl 0.10.15 (registry+https://github.com/rust-lang/crates.io-index)" = "5e1309181cdcbdb51bc3b6bedb33dfac2a83b3d585033d3f6d9e22e8c1928613" @@ -2898,12 +2994,16 @@ dependencies = [ "checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" "checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "54f0c72a98d8ab3c99560bfd16df8059cc10e1f9a8e83e6e3b97718dd766e9c3" +"checksum pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +"checksum pest_generator 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "63120576c4efd69615b5537d3d052257328a4ca82876771d6944424ccfd9f646" +"checksum pest_meta 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5a3492a4ed208ffc247adcdcc7ba2a95be3104f58877d0d02f0df39bf3efb5e" "checksum phf 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "cec29da322b242f4c3098852c77a0ca261c9c01b806cae85a5572a1eb94db9a6" "checksum phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "7d187f00cd98d5afbcd8898f6cf181743a449162aeb329dcd2f3849009e605ad" "checksum phf_generator 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "03dc191feb9b08b0dc1330d6549b795b9d81aec19efe6b4a45aec8d4caee0c4b" "checksum phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "b539898d22d4273ded07f64a05737649dc69095d92cb87c7097ec68e3f150b93" "checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" -"checksum pmac 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a82cc12454dc99354a9342c237149aec041ef16f618066d0a682df256b97714" +"checksum pmac 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd76d63e86aa67a4bd063079f0d93bb8730b036eea5697345f651e6874ea61e5" "checksum proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "77997c53ae6edd6d187fec07ec41b207063b5ee6f33680e9fa86d405cdd313d4" "checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" @@ -2953,31 +3053,33 @@ dependencies = [ "checksum serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)" = "c37ccd6be3ed1fdf419ee848f7c758eb31b054d7cd3ae3600e3bae0adf569811" "checksum serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d48f9f99cd749a2de71d29da5f948de7f2764cc5a9d7f3c97e3514d4ee6eabf2" "checksum serde_yaml 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0887a8e097a69559b56aa2526bf7aff7c3048cf627dff781f0b56a6001534593" +"checksum sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9d1f3b5de8a167ab06834a7c883bd197f2191e1dda1a22d9ccfeedbf9aded" "checksum shell-escape 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "170a13e64f2a51b77a45702ba77287f5c6829375b04a69cf2222acd17d0cfab9" -"checksum shell32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9ee04b46101f57121c9da2b151988283b6beb79b34f5bb29a58ee48cb695122c" "checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" "checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d" "checksum smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b73ea3738b47563803ef814925e69be00799a8c07420be8b996f8e98fb2336db" "checksum socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d11a52082057d87cb5caa31ad812f4504b97ab44732cd8359df2e9ff9f48e7" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" +"checksum stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8861bc80f649f5b4c9bd38b696ae9af74499d479dbfb327f0607de6b326a36bc" "checksum string 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98998cced76115b1da46f63388b909d118a37ae0be0f82ad35773d4a4bc9d18d" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "670ad348dc73012fcf78c71f06f9d942232cdd4c859d4b6975e27836c3efc0c3" "checksum structopt-derive 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "ef98172b1a00b0bec738508d3726540edcbd186d50dfd326f2b1febbb3559f04" -"checksum subtle 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7a6bab57c3efd01ebd3d750f4244ae0af4cdd1fc505a7904a41603192b803c5" "checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" -"checksum subtle 2.0.0-pre.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9983aa133c8d2d36c0b2aa8d3e68b6ad2dd28e080593de76fa207744adc96ab4" +"checksum subtle 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "702662512f3ddeb74a64ce2fbbf3707ee1b6bb663d28bb054e0779bbc720d926" "checksum syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)" = "ae8b29eb5210bc5cf63ed6149cbf9adfc82ac0be023d8735c176ee74a2db4da7" "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" "checksum tar 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "a303ba60a099fcd2aaa646b14d2724591a96a75283e4b7ed3d1a1658909d9ae2" "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" "checksum tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7e91405c14320e5c79b3d148e1c86f40749a36e490642202a31689cb1a3452b2" "checksum term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6b677dd1e8214ea1ef4297f85dbcbed8e8cdddb561040cc998ca2551c37561" +"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327" "checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" +"checksum tokei 8.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bfdcbacc1767851b4a330ff74f521e20b746954bac282d3928ae300454fb24e2" "checksum tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "a7817d4c98cc5be21360b3b37d6036fe9b7aefa5b7a201b7b16ff33423822f7d" "checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" "checksum tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "331c8acc267855ec06eb0c94618dcbbfea45bed2d20b77252940095273fb58f6" @@ -2993,6 +3095,7 @@ dependencies = [ "checksum toml 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "19782e145d5abefb03758958f06ea35f7b1d8421b534140e0238fd3d0bfd66e3" "checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" +"checksum ucd-trie 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "71a9c5b1fe77426cf144cc30e49e955270f5086e31a6441dfa8b32efc09b9d77" "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" "checksum unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d3218ea14b4edcaccfa0df0a64a3792a2c32cc706f1b336e48867f9d3147f90" @@ -3020,5 +3123,5 @@ dependencies = [ "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -"checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" "checksum yaml-rust 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "95acf0db5515d07da9965ec0e0ba6cc2d825e2caeb7303b66ca441729801254e" +"checksum zeroize 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8ddfeb6eee2fb3b262ef6e0898a52b7563bb8e0d5955a313b3cf2f808246ea14" diff --git a/Cargo.toml b/Cargo.toml index edb33445..0431d661 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,8 @@ members = [ "crev-common", "crev-data", "crev-lib", - "crev-bin", + # defunct + # "crev-bin", "cargo-crev", "recursive-digest", "rblake2sum", diff --git a/HACKING.md b/HACKING.md index b440110a..0487142b 100644 --- a/HACKING.md +++ b/HACKING.md @@ -26,4 +26,4 @@ and not neccessarily properly done. Seek help on [crev gitter channel](https://gitter.im/dpc/crev) before you start hacking on the code. -The imediate goal is to get `cargo-cargo` binary to be usable. +The imediate goal is to get `cargo-crev` binary to be usable. diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 00000000..16fe87b0 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 00000000..39d4bdb5 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/LICENSE-MPL2 b/LICENSE-MPL2 new file mode 100644 index 00000000..14e2f777 --- /dev/null +++ b/LICENSE-MPL2 @@ -0,0 +1,373 @@ +Mozilla 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. diff --git a/README.md b/README.md index 25f58e63..d04ed4cb 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,9 @@ You're ultimately responsible for vetting your dependencies. But in a world of NPM/PIP/Cargo/RubyGems - how do you do that? Can you keep up with ever-changing ecosystem? -`crev` is an actual *code review* system as opposed to typicaly practiced *code-change review* system. +`crev` is an actual *code review* system as opposed to typically practiced *code-change review* system. -`crev` is scalable, distributed and social. Users publish and circulate results of their reviews: potentially warning about problems, malicious code, or just encuraging high quality by peer review. +`crev` is scalable, distributed and social. Users publish and circulate results of their reviews: potentially warning about problems, malicious code, or just encouraging high quality by peer review. `crev` allows building a personal web of trust in people and code. @@ -42,7 +42,7 @@ you keep up with ever-changing ecosystem? ## Vision We would like Crev to become a general, language and ecosystem agnostic -system for estabilishing trust in Open Source code. We would like to have +system for establishing trust in Open Source code. We would like to have frontends integrated with all major Open Source package managers and ecosystems. Consider joining [crev gitter channel](https://gitter.im/dpc/crev). Thank you! diff --git a/cargo-crev/CHANGELOG.md b/cargo-crev/CHANGELOG.md new file mode 100644 index 00000000..635e099b --- /dev/null +++ b/cargo-crev/CHANGELOG.md @@ -0,0 +1,47 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased](https://github.com/dpc/crev/compare/cargo-crev-v0.4.0...HEAD) + + +## [0.4.0](https://github.com/dpc/crev/compare/cargo-crev-v0.3.0...cargo-crev-v0.4.0) - 2019-01-12 +### Added +- This `CHANGELOG.md` file. +- `LICENSE` files +- Ability to work without an Id for most commands. +- `open` command to help IDE users. +- Tracking effective trust level in WoT. +- Distrust calculation when calculating WoT. +- `review` command options: `--print-[un]signed` and `-no-store`. +- `export` and `import` commands for Ids. +- New column in `verify deps`: `lines` - line counts using `tokei`. +- New column in `verify dpes`: `flags` - Custom Build. +- `verify deps` option: `--for-id`. +- Exit status on `verify deps` to make it usable in CI pipelines. +- Counts of new proofs on `fetch` commands. +- Effecttive trust level column in `query id trusted` output. +- `update` command. + +### Changed +- Windows cache folder changed from `%AppData%\Local\Dawid Ci,281,,380,arkiewicz\crev` to `%AppData%\Local\crev`. +- Windows config folder changed from `%AppData%\Roaming\Dawid Ci,281,,380,arkiewicz\crev` to `%AppData%\Roaming\crev`. +- MacOS config folder changed from `$HOME/Library/Application Support/crev` to `$HOME/Library/Preferences/crev`. +- Improve `verify deps` names and format. +- Handle error messages better in many places. +- Use host-specific salt in paths of proof files, to prevent dealing with git conflicts when sharing Id between many machines +- Make newer reviews (for the same package and version) effectively overwrite older ones. +- Change `push`, `pull`, `publish` to be more ID-sharing (between hosts) friendly +- Rename `--independent` to `--unrelated` and add `-u` as a short version. +- Avoid fetching things during normal work (helps offline use). +- Hardcode dpc's proof-repo url on `fetch all` to help bootstrap the ecosystem. + +### Fixed +- Fix `$EDITOR`/`$VISUAL` handling, especially on Windows +- Save `lanes` in `LockedId`. Old Ids need to be fixed manually. + +## [0.3.0] - 2018-12-28 + +Changelog was not maintained for this and earlier releases diff --git a/cargo-crev/Cargo.toml b/cargo-crev/Cargo.toml index 2f689c85..f30a1d83 100644 --- a/cargo-crev/Cargo.toml +++ b/cargo-crev/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = '2018' name = "cargo-crev" -version = "0.3.0" +version = "0.4.0" description = "Scalable, social, Code REView system that we desperately need - Rust/cargo frontend" authors = ["Dawid Ciężarkiewicz "] documentation = "https://docs.rs/crev" @@ -21,9 +21,9 @@ quicli = "0.3" structopt = "0.2" common_failures = "0.1" cargo = "0.32" -crev-lib = { path = "../crev-lib", version = "0.3" } -crev-data = { path = "../crev-data", version = "0.3" } -crev-common = { path = "../crev-common", version = "0.3" } +crev-lib = { path = "../crev-lib", version = "0.4" } +crev-data = { path = "../crev-data", version = "0.4" } +crev-common = { path = "../crev-common", version = "0.4" } semver = "0.9" default = "0.1" crates_io_api = "0.3" @@ -32,3 +32,4 @@ serde_json = "1" dirs = "1" atty = "0.2" term = "0.5" +tokei = "8" diff --git a/cargo-crev/README.md b/cargo-crev/README.md index f8bd565b..eb1a8ea0 100644 --- a/cargo-crev/README.md +++ b/cargo-crev/README.md @@ -1,3 +1,16 @@ +

+ + Travis CI Build Status + + + crates.io + + + Gitter Chat + +
+

+ # `cargo-crev` - Cargo Code REView! @@ -17,7 +30,7 @@ All of this neatly integrated with the `cargo` itself! * People download your reviews, you download reviews of others. * Build a web of trust veting whole Rust ecosystem. * Gain reputation and trust. Maybe even monetize it, by reving code for money. -* Implement it in your company and/or team to stay ahead! +* Implement it in your company and/or team to stay ahead! * Never again get bitten by unreviewed and untrusted code. ## More info @@ -32,6 +45,10 @@ See it in action: [![asciicast](https://asciinema.org/a/216695.png)](https://asciinema.org/a/216695?speed=3) +## Changelog + +Changelog can be found here: https://github.com/dpc/crev/blob/master/cargo-crev/CHANGELOG.md + ## Getting started `cargo-crev` is a work in progress, but it should be usable at all times. @@ -59,20 +76,21 @@ path. ### Installing from crates.io + + +If you wish to use latest release: + ``` cargo install cargo-crev ``` -and you're all set. - ### Installing from github -If you want to live on the edge, you can install `cargo-crev` directly from github, too: +We try to release often, but new features are added at fast pace. If +you want to try the git version: ``` -git clone https://github.com/dpc/crev -cd crev -cargo install -f --path cargo-crev +cargo install --git https://github.com/dpc/crev/ cargo-crev ``` ## Usage @@ -89,7 +107,10 @@ cargo crev query id all # show all known ids cargo crev query review # show all reviews cargo crev query review # show all reviews of a package cargo crev trust # trust someone +cargo crev goto # jump to crate to review it +cargo crev review # review a crate (after goto) cargo crev review # review a dependency +cargo crev review --independent # review a crate that is not a dependency cargo crev commit # commit new proofs (reviews, trust) cargo crev push # push proofs to your public github repository cargo crev help # see what other things you can do diff --git a/cargo-crev/rustfmt.toml b/cargo-crev/rustfmt.toml index 7d2cf549..807b07b8 100644 --- a/cargo-crev/rustfmt.toml +++ b/cargo-crev/rustfmt.toml @@ -1 +1,2 @@ +edition = "2018" merge_imports = true diff --git a/cargo-crev/src/crates_io.rs b/cargo-crev/src/crates_io.rs index 1ab25b3b..ba39aee5 100644 --- a/cargo-crev/src/crates_io.rs +++ b/cargo-crev/src/crates_io.rs @@ -1,10 +1,11 @@ use crate::prelude::*; -use serde::de::DeserializeOwned; -use serde::Serialize; -use std::fs; -use std::io::Read; -use std::path::{Path, PathBuf}; -use std::time::Duration; +use serde::{de::DeserializeOwned, Serialize}; +use std::{ + fs, + io::Read, + path::{Path, PathBuf}, + time::Duration, +}; pub struct Client { client: crates_io_api::SyncClient, @@ -15,7 +16,7 @@ fn is_fresh(path: &Path) -> Result { let metadata = fs::metadata(path)?; let created = metadata.created().or_else(|_e| metadata.modified())?; let now = std::time::SystemTime::now(); - Ok(((now - Duration::from_secs(60 * 60 * 24)) < created) && (created < now)) + Ok(((now - Duration::from_secs(60 * 60 * 72)) < created) && (created < now)) } trait Cacheable: Sized { diff --git a/cargo-crev/src/main.rs b/cargo-crev/src/main.rs index d29fcf78..66fc50bf 100644 --- a/cargo-crev/src/main.rs +++ b/cargo-crev/src/main.rs @@ -2,21 +2,18 @@ extern crate structopt; use self::prelude::*; -use ::term::color; use cargo::{ - core::dependency::Dependency, - core::source::SourceMap, - core::{package_id::PackageId, SourceId}, + core::{dependency::Dependency, source::SourceMap, Package, SourceId}, util::important_paths::find_root_manifest_for_wd, }; -use crev_lib::ProofStore; -use crev_lib::{self, local::Local}; -use default::default; +use crev_common::convert::OptionDeref; +use crev_lib::{self, local::Local, ProofStore}; use semver; use serde::Deserialize; use std::{ collections::HashSet, - env, fmt, + env, + io::BufRead, path::{Path, PathBuf}, process, }; @@ -26,14 +23,10 @@ mod crates_io; mod opts; mod prelude; mod term; +mod tokei; use crev_data::proof; -use crev_lib::{TrustOrDistrust, TrustOrDistrust::*}; - -struct Repo { - manifest_path: PathBuf, - config: cargo::util::config::Config, -} +use crev_lib::TrustOrDistrust::{self, *}; /// Name of ENV with original location `crev goto` was called from const GOTO_ORIGINAL_DIR_ENV: &str = "CARGO_CREV_GOTO_ORIGINAL_DIR"; @@ -48,6 +41,7 @@ const KNOWN_CARGO_OWNERS_FILE: &str = "known_cargo_owners.txt"; /// Constant we use for `source` in the review proof const PROJECT_SOURCE_CRATES_IO: &str = "https://crates.io"; +/// The file added to crates containing vcs revision const VCS_INFO_JSON_FILE: &str = ".cargo_vcs_info.json"; /// Data from `.cargo_vcs_info.json` @@ -79,23 +73,11 @@ impl VcsInfoJson { Some(s.to_string()) } } -#[derive(Debug)] -struct KnownOwnersColored(usize); -impl fmt::Display for KnownOwnersColored { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_fmt(format_args!("{}", self.0)) - } -} - -impl crev_lib::Colored for KnownOwnersColored { - fn color(&self) -> Option { - if self.0 > 0 { - Some(color::GREEN) - } else { - None - } - } +/// A handle to the current Rust project +struct Repo { + manifest_path: PathBuf, + config: cargo::util::config::Config, } impl Repo { @@ -111,9 +93,35 @@ impl Repo { }) } - fn for_every_non_local_dependency_dir( + fn update_source(&self) -> Result<()> { + let mut source = self.load_source()?; + source.update()?; + Ok(()) + } + + fn update_counts(&self) -> Result<()> { + let local = crev_lib::Local::auto_create_or_open()?; + let crates_io = crates_io::Client::new(&local)?; + + self.for_every_non_local_dep_crate(|crate_| { + let _ = crates_io.get_downloads_count(&crate_.name(), &crate_.version().to_string()); + Ok(()) + })?; + + Ok(()) + } + + fn load_source<'a>(&'a self) -> Result> { + let source_id = SourceId::crates_io(&self.config)?; + let map = cargo::sources::SourceConfigMap::new(&self.config)?; + let source = map.load(&source_id)?; + Ok(source) + } + + /// Run `f` for every non-local dependency crate + fn for_every_non_local_dep_crate( &self, - mut f: impl FnMut(&PackageId, &Path) -> Result<()>, + mut f: impl FnMut(&Package) -> Result<()>, ) -> Result<()> { let workspace = cargo::core::Workspace::new(&self.manifest_path, &self.config)?; let specs = cargo::ops::Packages::All.to_package_id_specs(&workspace)?; @@ -125,9 +133,7 @@ impl Repo { false, // no_default_features &specs, )?; - let source_id = SourceId::crates_io(&self.config)?; - let map = cargo::sources::SourceConfigMap::new(&self.config)?; - let mut source = map.load(&source_id)?; + let mut source = self.load_source()?; let pkgs = package_set.get_many(package_set.package_ids())?; @@ -140,7 +146,7 @@ impl Repo { source.download(pkg.package_id())?; } - f(&pkg.package_id(), &pkg.root())?; + f(&pkg)?; } Ok(()) @@ -150,10 +156,8 @@ impl Repo { &self, name: &str, version: Option<&str>, - ) -> Result> { - let map = cargo::sources::SourceConfigMap::new(&self.config)?; - let source_id = SourceId::crates_io(&self.config)?; - let mut source = map.load(&source_id)?; + ) -> Result> { + let mut source = self.load_source()?; let mut summaries = vec![]; let dependency_request = Dependency::parse_no_deprecated(name, version, source.source_id())?; @@ -182,23 +186,19 @@ impl Repo { &self.config, )?; let pkg_id = summary.package_id(); - let pkg = package_set.get_one(pkg_id)?; - Ok(Some((pkg.root().to_owned(), pkg_id.version().to_owned()))) + Ok(Some(package_set.get_one(pkg_id)?.to_owned())) } - fn find_dependency_dir( - &self, - name: &str, - version: Option<&str>, - ) -> Result> { + fn find_dependency(&self, name: &str, version: Option<&str>) -> Result> { let mut ret = vec![]; - self.for_every_non_local_dependency_dir(|pkg_id, path| { + self.for_every_non_local_dep_crate(|pkg| { + let pkg_id = pkg.package_id(); if name == pkg_id.name().as_str() && (version.is_none() || version == Some(&pkg_id.version().to_string())) { - ret.push((path.to_owned(), pkg_id.version().to_owned())); + ret.push(pkg.to_owned()); } Ok(()) })?; @@ -210,21 +210,17 @@ impl Repo { } } - fn find_crate( - &self, - name: &str, - version: Option<&str>, - independent: bool, - ) -> Result<(PathBuf, semver::Version)> { - if independent { + fn find_crate(&self, name: &str, version: Option<&str>, unrelated: bool) -> Result { + if unrelated { self.find_idependent_crate_dir(name, version)? } else { - self.find_dependency_dir(name, version)? + self.find_dependency(name, version)? } .ok_or_else(|| format_err!("Could not find requested crate")) } } +/// Ignore things that are commonly added during the review (eg. by RLS) fn cargo_full_ignore_list() -> HashSet { let mut ignore_list = HashSet::new(); ignore_list.insert(PathBuf::from(".cargo-ok")); @@ -233,13 +229,18 @@ fn cargo_full_ignore_list() -> HashSet { ignore_list } +/// Ignore only the marker added by `cargo` after fully downloading and extracting crate fn cargo_min_ignore_list() -> HashSet { let mut ignore_list = HashSet::new(); ignore_list.insert(PathBuf::from(".cargo-ok")); ignore_list } -fn goto_crate_src(selector: &opts::CrateSelector, independent: bool) -> Result<()> { +/// `cd` into crate source code and start shell +/// +/// Set some `envs` to help other commands work +/// from inside such a "review-shell". +fn goto_crate_src(selector: &opts::CrateSelector, unrelated: bool) -> Result<()> { if env::var(GOTO_ORIGINAL_DIR_ENV).is_ok() { bail!("You're already in a `cargo crev goto` shell"); }; @@ -248,17 +249,18 @@ fn goto_crate_src(selector: &opts::CrateSelector, independent: bool) -> Result<( .name .clone() .ok_or_else(|| format_err!("Crate name argument required"))?; - let (pkg_dir, crate_version) = - repo.find_crate(&name, selector.version.as_deref(), independent)?; + let crate_ = repo.find_crate(&name, selector.version.as_deref(), unrelated)?; + let crate_dir = crate_.root(); + let crate_version = crate_.version(); let shell = env::var_os("SHELL").ok_or_else(|| format_err!("$SHELL not set"))?; let cwd = env::current_dir()?; - eprintln!("Opening shell in: {}", pkg_dir.display()); + eprintln!("Opening shell in: {}", crate_dir.display()); eprintln!("Use `exit` or Ctrl-D to return to the original project.",); eprintln!("Use `review` and `flag` without any arguments to review this crate."); let status = process::Command::new(shell) - .current_dir(pkg_dir) + .current_dir(crate_dir) .env(GOTO_ORIGINAL_DIR_ENV, cwd) .env(GOTO_CRATE_NAME_ENV, name) .env(GOTO_CRATE_VERSION_ENV, &crate_version.to_string()) @@ -271,7 +273,7 @@ fn goto_crate_src(selector: &opts::CrateSelector, independent: bool) -> Result<( Ok(()) } -fn ensure_known_owners_exists(local: &crev_lib::Local) -> Result<()> { +fn ensure_known_owners_list_exists(local: &crev_lib::Local) -> Result<()> { let path = local.get_proofs_dir_path()?.join(KNOWN_CARGO_OWNERS_FILE); if !path.exists() { crev_common::store_str_to_file(&path, include_str!("known_cargo_owners_defaults.txt"))?; @@ -281,11 +283,15 @@ fn ensure_known_owners_exists(local: &crev_lib::Local) -> Result<()> { Ok(()) } -fn read_known_owners() -> Result> { +fn read_known_owners_list() -> Result> { let local = Local::auto_create_or_open()?; - let path = local.get_proofs_dir_path()?.join(KNOWN_CARGO_OWNERS_FILE); - - Ok(crev_common::read_file_to_string(&path)? + let content = if let Some(path) = local.get_proofs_dir_path_opt()? { + let path = path.join(KNOWN_CARGO_OWNERS_FILE); + crev_common::read_file_to_string(&path)? + } else { + include_str!("known_cargo_owners_defaults.txt").to_string() + }; + Ok(content .lines() .map(|s| s.trim()) .filter(|s| !s.starts_with('#')) @@ -293,40 +299,74 @@ fn read_known_owners() -> Result> { .collect()) } -fn edit_known_owners() -> Result<()> { +fn edit_known_owners_list() -> Result<()> { let local = Local::auto_create_or_open()?; let path = local.get_proofs_dir_path()?.join(KNOWN_CARGO_OWNERS_FILE); - ensure_known_owners_exists(&local)?; + ensure_known_owners_list_exists(&local)?; crev_lib::util::edit_file(&path)?; Ok(()) } -fn clean_crate(name: &str, version: Option<&str>, independent: bool) -> Result<()> { +/// Wipe the crate source, then re-download it +fn clean_crate(name: &str, version: Option<&str>, unrelated: bool) -> Result<()> { let repo = Repo::auto_open_cwd()?; - let (pkg_dir, _crate_version) = repo.find_crate(name, version, independent)?; + let crate_ = repo.find_crate(name, version, unrelated)?; + let crate_root = crate_.root(); - assert!(!pkg_dir.starts_with(std::env::current_dir()?)); + assert!(!crate_root.starts_with(std::env::current_dir()?)); - if pkg_dir.is_dir() { - std::fs::remove_dir_all(&pkg_dir)?; + if crate_root.is_dir() { + std::fs::remove_dir_all(&crate_root)?; } - let (_pkg_dir, _crate_version) = repo.find_crate(name, version, independent)?; + let _crate = repo.find_crate(name, version, unrelated)?; + Ok(()) +} + +/// Open a crate +/// +/// * `unrelated` - the crate might not actually be a dependency +fn crate_open(name: &str, version: Option<&str>, unrelated: bool) -> Result<()> { + let repo = Repo::auto_open_cwd()?; + let crate_ = repo.find_crate(name, version, unrelated)?; + + let crate_root = crate_.root(); + + let status = if cfg!(target_os = "windows") { + process::Command::new("start") + } else if cfg!(target_os = "macos") { + process::Command::new("open") + } else if cfg!(target_os = "linux") { + process::Command::new("xdg-open") + } else { + eprintln!("Unsupported platform. Please submit a PR!"); + process::Command::new("xdg-open") + } + .arg(crate_root) + .status()?; + + if !status.success() { + bail!("Shell returned {}", status); + } + Ok(()) } /// Review a crate /// -/// * `independent` - the crate might not actually be a dependency -fn review_crate( +/// * `unrelated` - the crate might not actually be a dependency +fn create_review_proof( name: &str, version: Option<&str>, - independent: bool, + unrelated: bool, trust: TrustOrDistrust, + proof_create_opt: &opts::CommonProofCreate, ) -> Result<()> { let repo = Repo::auto_open_cwd()?; - let (pkg_dir, crate_version) = repo.find_crate(name, version, independent)?; + let crate_ = repo.find_crate(name, version, unrelated)?; + let crate_root = crate_.root(); + let crate_version = crate_.version(); - assert!(!pkg_dir.starts_with(std::env::current_dir()?)); + assert!(!crate_root.starts_with(std::env::current_dir()?)); let local = Local::auto_open()?; // to protect from creating a digest from a crate in unclean state @@ -334,7 +374,7 @@ fn review_crate( // check if the digest was the same // BUG: TODO: https://users.rust-lang.org/t/append-an-additional-extension/23586 let reviewed_pkg_dir: PathBuf = - crev_common::fs::append_to_path(pkg_dir.clone(), ".crev.reviewed"); + crev_common::fs::append_to_path(crate_root.to_owned(), ".crev.reviewed"); if reviewed_pkg_dir.is_dir() { std::fs::remove_dir_all(&reviewed_pkg_dir)?; } @@ -343,12 +383,16 @@ fn review_crate( // having the cwd pulled from under them and confusing their // shells, we move all the entries in a dir, instead of the whole // dir. this is not a perfect solution, but better than nothing. - crev_common::fs::move_dir_content(&pkg_dir, &reviewed_pkg_dir)?; - let (pkg_dir_second, crate_version_second) = repo.find_crate(name, version, independent)?; - assert_eq!(pkg_dir, pkg_dir_second); + crev_common::fs::move_dir_content(&crate_root, &reviewed_pkg_dir)?; + let crate_second = repo.find_crate(name, version, unrelated)?; + let crate_root_second = crate_second.root(); + let crate_version_second = crate_second.version(); + + assert_eq!(crate_root, crate_root_second); assert_eq!(crate_version, crate_version_second); - let digest_clean = crev_lib::get_recursive_digest_for_dir(&pkg_dir, &cargo_min_ignore_list())?; + let digest_clean = + crev_lib::get_recursive_digest_for_dir(&crate_root, &cargo_min_ignore_list())?; let digest_reviewed = crev_lib::get_recursive_digest_for_dir(&reviewed_pkg_dir, &cargo_full_ignore_list())?; @@ -357,16 +401,14 @@ fn review_crate( "The digest of the reviewed and freshly downloaded crate were different; {} != {}; {} != {}", digest_clean, digest_reviewed, - pkg_dir.display(), + crate_root.display(), reviewed_pkg_dir.display(), ); } std::fs::remove_dir_all(&reviewed_pkg_dir)?; - let vcs = VcsInfoJson::read_from_crate_dir(&pkg_dir)?; - - let passphrase = crev_common::read_passphrase()?; - let id = local.read_current_unlocked_id(&passphrase)?; + let vcs = VcsInfoJson::read_from_crate_dir(&crate_root)?; + let id = local.read_current_unlocked_id(&crev_common::read_passphrase)?; let review = proof::review::PackageBuilder::default() .from(id.id.to_owned()) @@ -379,7 +421,7 @@ fn review_crate( digest_type: proof::default_digest_type(), revision: vcs .and_then(|vcs| vcs.get_git_revision()) - .unwrap_or("".into()), + .unwrap_or_else(|| "".into()), revision_type: proof::default_revision_type(), }) .review(trust.to_review()) @@ -388,18 +430,39 @@ fn review_crate( let review = crev_lib::util::edit_proof_content_iteractively(&review.into())?; + if proof_create_opt.print_unsigned { + print!("{}", review); + } + let proof = review.sign_by(&id)?; - local.insert(&proof)?; + if proof_create_opt.print_signed { + print!("{}", proof); + } + + if !proof_create_opt.no_store { + local.insert(&proof)?; + + if !proof_create_opt.no_commit { + let commit_msg = format!( + "Add review for {crate} v{version}", + crate = name, + version = crate_version + ); + local + .proof_dir_commit(&commit_msg) + .with_context(|_| format_err!("Could not not automatically commit"))?; + } + } + Ok(()) } fn find_reviews( crate_: &opts::CrateSelector, - trust_params: &crev_lib::trustdb::TrustDistanceParams, ) -> Result> { let local = crev_lib::Local::auto_open()?; - let (db, _trust_set) = local.load_db(&trust_params)?; + let db = local.load_db()?; Ok(db.get_package_reviews_for_package( PROJECT_SOURCE_CRATES_IO, crate_.name.as_ref().map(|s| s.as_str()), @@ -408,15 +471,19 @@ fn find_reviews( } fn list_reviews(crate_: &opts::CrateSelector) -> Result<()> { - // TODO: take trust params? - for review in find_reviews(crate_, &default())? { + for review in find_reviews(crate_)? { println!("{}", review); } Ok(()) } -fn handle_goto_mode_command(args: &opts::ReviewOrGoto, f: F) -> Result<()> +/// Handle the `goto mode` commands +/// +/// After jumping to a crate with `goto`, the crate is selected +/// already, and commands like `review` must not be given any arguments +/// like that. +fn handle_goto_mode_command(args: &opts::ReviewOrGotoCommon, f: F) -> Result<()> where F: FnOnce(&str, Option<&str>, bool) -> Result<()>, { @@ -427,7 +494,7 @@ where let name = env::var(GOTO_CRATE_NAME_ENV) .map_err(|_| format_err!("crate name env var not found"))?; let version = env::var(GOTO_CRATE_VERSION_ENV) - .map_err(|_| format_err!("crate versoin env var not found"))?; + .map_err(|_| format_err!("crate version env var not found"))?; env::set_current_dir(org_dir)?; f(&name, Some(&version), true)?; @@ -439,29 +506,80 @@ where .clone() .ok_or_else(|| format_err!("Crate name required"))?; - f(&name, args.crate_.version.as_deref(), args.independent)?; + f(&name, args.crate_.version.as_deref(), args.unrelated)?; } Ok(()) } -fn main() -> Result<()> { - let opts = opts::Opts::from_args(); - let opts::MainCommand::Crev(command) = opts.command; +fn create_trust_proof( + ids: Vec, + trust_or_distrust: TrustOrDistrust, + proof_create_opt: &opts::CommonProofCreate, +) -> Result<()> { + let local = Local::auto_open()?; + + let own_id = local.read_current_unlocked_id(&crev_common::read_passphrase)?; + + let trust = local.build_trust_proof(own_id.as_pubid(), ids.clone(), trust_or_distrust)?; + + if proof_create_opt.print_unsigned { + print!("{}", trust); + } + + let proof = trust.sign_by(&own_id)?; + + if proof_create_opt.print_signed { + print!("{}", proof); + } + + if !proof_create_opt.no_store { + local.insert(&proof)?; + + if !proof_create_opt.no_commit { + let commit_msg = format!( + "Add {t_or_d} for {ids}", + t_or_d = trust_or_distrust, + ids = ids.join(", ") + ); + local + .proof_dir_commit(&commit_msg) + .with_context(|_| format_err!("Could not not automatically commit"))?; + } + } + + Ok(()) +} + +/// Result of `run_command` +/// +/// This is to distinguish expeced non-success results, +/// from errors: unexpected failures. +enum CommandExitStatus { + // `verify deps` failed + VerificationFailed, + // Success, exit code 0 + Successs, +} + +fn run_command(command: opts::Command) -> Result { match command { opts::Command::New(cmd) => match cmd { opts::New::Id(args) => { - let res = - crev_lib::generate_id(args.url, args.github_username, args.use_https_push); + let local = Local::auto_create_or_open()?; + let res = local.generate_id(args.url, args.github_username, args.use_https_push); if res.is_err() { eprintln!("Visit https://github.com/dpc/crev/wiki/Proof-Repository for help."); } let local = crev_lib::Local::auto_open()?; - let _ = ensure_known_owners_exists(&local); + let _ = ensure_known_owners_list_exists(&local); res?; } }, opts::Command::Switch(cmd) => match cmd { - opts::Switch::Id(args) => crev_lib::switch_id(&args.id)?, + opts::Switch::Id(args) => { + let local = Local::auto_open()?; + local.switch_id(&args.id)? + } }, opts::Command::Edit(cmd) => match cmd { opts::Edit::Readme => { @@ -469,55 +587,73 @@ fn main() -> Result<()> { local.edit_readme()?; } opts::Edit::Known => { - edit_known_owners()?; + edit_known_owners_list()?; } }, opts::Command::Verify(cmd) => match cmd { + // TODO: This is waaay too long; refactor opts::Verify::Deps(args) => { let mut term = term::Term::new(); - let local = crev_lib::Local::auto_open()?; - let (db, trust_set) = local.load_db(&args.trust_params.clone().into())?; + let local = crev_lib::Local::auto_create_or_open()?; + let db = local.load_db()?; + + let trust_set = + if let Some(for_id) = local.get_for_id_from_str_opt(args.for_id.as_deref())? { + db.calculate_trust_set(&for_id, &args.trust_params.clone().into()) + } else { + crev_lib::proofdb::TrustSet::default() + }; let repo = Repo::auto_open_cwd()?; let ignore_list = cargo_min_ignore_list(); - let cratesio = crates_io::Client::new(&local)?; + let crates_io = crates_io::Client::new(&local)?; if term.stderr_is_tty && term.stdout_is_tty { if args.verbose { eprint!("{:43} ", "digest"); } eprint!( - "{:8} {:8} {:^13} {:6}", - "status", "reviews", "downloads", "owners" + "{:8} {:8} {:^15} {:4} {:6} {:4}", + "trust", "reviews", "downloads", "own.", "lines", "flgs" ); eprintln!(" {:<19} {:<15}", "crate", "version"); } - let known_owners = read_known_owners().unwrap_or_else(|_| HashSet::new()); - repo.for_every_non_local_dependency_dir(|pkg_id, path| { - let pkg_name = pkg_id.name().as_str(); - let pkg_version = pkg_id.version().to_string(); - - let digest = crev_lib::get_dir_digest(&path, &ignore_list)?; - let result = db.verify_digest(&digest, &trust_set); + let known_owners = read_known_owners_list().unwrap_or_else(|_| HashSet::new()); + let mut total_verification_successful = true; + repo.for_every_non_local_dep_crate(|crate_| { + let crate_id = crate_.package_id(); + let crate_name = crate_id.name().as_str(); + let crate_version = crate_id.version().to_string(); + let crate_root = crate_.root(); + + let digest = crev_lib::get_dir_digest(&crate_root, &ignore_list)?; + let result = db.verify_package_digest(&digest, &trust_set); + + if !result.is_verified() { + total_verification_successful = false; + } - if result == crev_lib::VerificationStatus::Verified && args.skip_verified { + if result.is_verified() && args.skip_verified { return Ok(()); } - let pkg_review_count = - db.get_package_review_count(PROJECT_SOURCE_CRATES_IO, Some(pkg_name), None); + let pkg_review_count = db.get_package_review_count( + PROJECT_SOURCE_CRATES_IO, + Some(crate_name), + None, + ); let pkg_version_review_count = db.get_package_review_count( PROJECT_SOURCE_CRATES_IO, - Some(pkg_name), - Some(&pkg_version), + Some(crate_name), + Some(&crate_version), ); - let (version_downloads, total_downloads) = cratesio - .get_downloads_count(&pkg_name, &pkg_version) + let (version_downloads, total_downloads) = crates_io + .get_downloads_count(&crate_name, &crate_version) .map(|(a, b)| (a.to_string(), b.to_string())) .unwrap_or_else(|_e| ("err".into(), "err".into())); - let owners = cratesio.get_owners(&pkg_name).ok(); + let owners = crates_io.get_owners(&crate_name).ok(); let (known_owners_count, total_owners_count) = if let Some(owners) = owners { let total_owners_count = owners.len(); let known_owners_count = owners @@ -536,7 +672,10 @@ fn main() -> Result<()> { if args.verbose { print!("{:43} ", digest); } - term.stdout(format_args!("{:8}", result), &result)?; + term.print( + format_args!("{:8}", result), + term::verification_status_color(&result), + )?; print!( " {:2} {:2} {:>8} {:>9}", pkg_version_review_count, @@ -544,48 +683,78 @@ fn main() -> Result<()> { version_downloads, total_downloads, ); - let colored_count_color = KnownOwnersColored(known_owners_count.unwrap_or(0)); - term.stdout( + term.print( format_args!( " {}", &known_owners_count .map(|c| c.to_string()) .unwrap_or_else(|| "?".into()) ), - &colored_count_color, + term::known_owners_count_color(known_owners_count.unwrap_or(0)), )?; print!( - "/{}", + "/{} ", total_owners_count .map(|c| c.to_string()) .unwrap_or_else(|| "?".into()) ); - println!(" {:<20} {:<15}", pkg_name, pkg_version); + print!( + "{:>6} ", + tokei::get_rust_line_count(crate_root) + .ok() + .map(|n| n.to_string()) + .unwrap_or_else(|| "err".into()) + ); + term.print( + format_args!(" {:4}", if crate_.has_custom_build() { "CB" } else { "" }), + ::term::color::YELLOW, + )?; + println!(" {:<20} {:<15}", crate_name, crate_version); Ok(()) })?; + + return Ok(if total_verification_successful { + CommandExitStatus::Successs + } else { + CommandExitStatus::VerificationFailed + }); } }, opts::Command::Query(cmd) => match cmd { opts::Query::Id(cmd) => match cmd { - opts::QueryId::Current => crev_lib::show_current_id()?, - opts::QueryId::Own => crev_lib::list_own_ids()?, - // TODO: move to crev-lib - opts::QueryId::Trusted { trust_params } => { + opts::QueryId::Current => { + let local = Local::auto_open()?; + local.show_current_id()? + } + opts::QueryId::Own => { + let local = Local::auto_open()?; + local.list_own_ids()? + } + opts::QueryId::Trusted { + for_id, + trust_params, + } => { let local = crev_lib::Local::auto_open()?; - let (db, trust_set) = local.load_db(&trust_params.into())?; - for id in &trust_set { + let db = local.load_db()?; + let for_id = local.get_for_id_from_str(for_id.as_deref())?; + let trust_set = db.calculate_trust_set(&for_id, &trust_params.into()); + + for id in trust_set.trusted_ids() { println!( - "{} {}", + "{} {:6} {}", id, + trust_set + .get_effective_trust_level(id) + .expect("Some trust level"), db.lookup_url(id).map(|url| url.url.as_str()).unwrap_or("") ); } } // TODO: move to crev-lib opts::QueryId::All => { - let local = crev_lib::Local::auto_open()?; - let (db, _trust_set) = local.load_db(&default())?; + let local = crev_lib::Local::auto_create_or_open()?; + let db = local.load_db()?; for id in &db.all_known_ids() { println!( @@ -599,46 +768,41 @@ fn main() -> Result<()> { opts::Query::Review(args) => list_reviews(&args.crate_)?, }, opts::Command::Review(args) => { - handle_goto_mode_command(&args, |c, v, i| { - review_crate(c, v, i, TrustOrDistrust::Trust) + handle_goto_mode_command(&args.common, |c, v, i| { + create_review_proof(c, v, i, TrustOrDistrust::Trust, &args.common_proof_create) })?; } opts::Command::Goto(args) => { - goto_crate_src(&args.crate_, args.independent)?; + goto_crate_src(&args.crate_, args.unrelated)?; + } + opts::Command::Open(args) => { + handle_goto_mode_command(&args, |c, v, i| crate_open(c, v, i))?; } opts::Command::Flag(args) => { - handle_goto_mode_command(&args, |c, v, i| { - review_crate(c, v, i, TrustOrDistrust::Distrust) + handle_goto_mode_command(&args.common, |c, v, i| { + create_review_proof( + c, + v, + i, + TrustOrDistrust::Distrust, + &args.common_proof_create, + ) })?; } opts::Command::Clean(args) => { handle_goto_mode_command(&args, |c, v, i| clean_crate(c, v, i))?; } opts::Command::Trust(args) => { - let local = Local::auto_open()?; - let passphrase = crev_common::read_passphrase()?; - local.build_trust_proof(args.pub_ids, &passphrase, Trust)?; + create_trust_proof(args.pub_ids, Trust, &args.common_proof_create)?; } opts::Command::Distrust(args) => { - let local = Local::auto_open()?; - let passphrase = crev_common::read_passphrase()?; - local.build_trust_proof(args.pub_ids, &passphrase, Distrust)?; + create_trust_proof(args.pub_ids, Distrust, &args.common_proof_create)?; } opts::Command::Git(git) => { let local = Local::auto_open()?; let status = local.run_git(git.args)?; std::process::exit(status.code().unwrap_or(-159)); } - opts::Command::Diff => { - let local = Local::auto_open()?; - let status = local.run_git(vec!["diff".into(), "HEAD".into()])?; - std::process::exit(status.code().unwrap_or(-159)); - } - opts::Command::Commit => { - let local = Local::auto_open()?; - let status = local.run_git(vec!["commit".into(), "-a".into()])?; - std::process::exit(status.code().unwrap_or(-159)); - } opts::Command::Push => { let local = Local::auto_open()?; let status = local.run_git(vec!["push".into()])?; @@ -646,7 +810,20 @@ fn main() -> Result<()> { } opts::Command::Publish => { let local = Local::auto_open()?; - let mut status = local.run_git(vec!["commit".into(), "-a".into()])?; + let mut status = local.run_git(vec!["diff".into(), "--exit-code".into()])?; + + if status.code().unwrap_or(-2) == 1 { + status = local.run_git(vec![ + "commit".into(), + "-a".into(), + "-m".into(), + "auto-commit on `crev publish`".into(), + ])?; + } + + if status.code().unwrap_or(-1) == 0 { + status = local.run_git(vec!["pull".into(), "--rebase".into()])?; + } if status.code().unwrap_or(-1) == 0 { status = local.run_git(vec!["push".into()])?; } @@ -654,24 +831,69 @@ fn main() -> Result<()> { } opts::Command::Pull => { let local = Local::auto_open()?; - let status = local.run_git(vec!["pull".into()])?; + let status = local.run_git(vec!["pull".into(), "--rebase".into()])?; std::process::exit(status.code().unwrap_or(-159)); } opts::Command::Fetch(cmd) => match cmd { opts::Fetch::Trusted(params) => { - let local = Local::auto_open()?; + let local = Local::auto_create_or_open()?; local.fetch_trusted(params.into())?; } opts::Fetch::Url(params) => { - let local = Local::auto_open()?; + let local = Local::auto_create_or_open()?; local.fetch_url(¶ms.url)?; } opts::Fetch::All => { - let local = Local::auto_open()?; + let local = Local::auto_create_or_open()?; local.fetch_all()?; } }, + opts::Command::Update => { + let repo = Repo::auto_open_cwd()?; + repo.update_source()?; + repo.update_counts()?; + } + opts::Command::Export(cmd) => match cmd { + opts::Export::Id(params) => { + let local = Local::auto_open()?; + println!("{}", local.export_locked_id(params.id)?); + } + }, + opts::Command::Import(cmd) => match cmd { + opts::Import::Id => { + let local = Local::auto_create_or_open()?; + let term = term::Term::new(); + if term.stdin_is_tty { + eprintln!("Paste in the text and press Ctrl+D.") + } + let mut s = vec![]; + + std::io::stdin().lock().read_until(0, &mut s)?; + let id = local.import_locked_id(&String::from_utf8(s)?)?; + // Note: It's unclear how much of this should be done by + // the library + local.save_current_id(&id.id)?; + + let proof_dir_path = local.get_proofs_dir_path_for_url(&id.url)?; + if !proof_dir_path.exists() { + local.clone_proof_dir_from_git(&id.url.url, false)?; + } + } + }, } - Ok(()) + Ok(CommandExitStatus::Successs) +} + +fn main() { + let opts = opts::Opts::from_args(); + let opts::MainCommand::Crev(command) = opts.command; + match run_command(command) { + Ok(CommandExitStatus::Successs) => {} + Ok(CommandExitStatus::VerificationFailed) => std::process::exit(-1), + Err(e) => { + eprintln!("{}", e.display_causes_and_backtrace()); + std::process::exit(-2) + } + } } diff --git a/cargo-crev/src/opts.rs b/cargo-crev/src/opts.rs index 7047f5a6..af048f0e 100644 --- a/cargo-crev/src/opts.rs +++ b/cargo-crev/src/opts.rs @@ -45,9 +45,9 @@ pub struct TrustParams { pub low_cost: u64, } -impl From for crev_lib::trustdb::TrustDistanceParams { +impl From for crev_lib::TrustDistanceParams { fn from(params: TrustParams) -> Self { - crev_lib::trustdb::TrustDistanceParams { + crev_lib::TrustDistanceParams { max_distance: params.depth, high_trust_distance: params.high_cost, medium_trust_distance: params.medium_cost, @@ -69,12 +69,28 @@ pub struct VerifyDeps { #[structopt(long = "skip-known-owners")] pub skip_known_owners: bool, + + #[structopt(long = "for-id")] + pub for_id: Option, } #[derive(Debug, StructOpt, Clone)] pub enum Verify { /// Verify dependencies - #[structopt(name = "deps")] + #[structopt( + name = "deps", + after_help = r"This will show the following information: + +- trust - Effective trust level trusted reviewers or `none`, `flagged` +- reviews - Number of reviews for the specific version and for all versions +- downloads - Download counts from crates.io for the specific version and all versions +- own. - Owner counts from crates.io (known/all) +- lines - Lines of Rust code +- flgs - Flags for specific types of packages + - CB - Custom Build +- name - Crate name +- version - Crate version" + )] Deps(VerifyDeps), } @@ -82,6 +98,9 @@ pub enum Verify { pub struct Trust { /// Public IDs to create Trust Proof for pub pub_ids: Vec, + + #[structopt(flatten)] + pub common_proof_create: CommonProofCreate, } #[derive(Debug, StructOpt, Clone)] @@ -124,6 +143,9 @@ pub enum QueryId { Trusted { #[structopt(flatten)] trust_params: TrustParams, + + #[structopt(long = "for-id")] + for_id: Option, }, } @@ -170,15 +192,59 @@ pub struct Git { } #[derive(Debug, StructOpt, Clone)] -pub struct ReviewOrGoto { +pub struct ReviewOrGotoCommon { #[structopt(flatten)] pub crate_: CrateSelector, /// This crate is not neccesarily a dependency of the current cargo project - #[structopt(long = "independent")] - pub independent: bool, + #[structopt(long = "unrelated", short = "u")] + pub unrelated: bool, } +#[derive(Debug, StructOpt, Clone)] +pub struct CommonProofCreate { + /// Don't auto-commit local Proof Repository + #[structopt(long = "no-commit")] + pub no_commit: bool, + + /// Print unsigned proof content on stdout + #[structopt(long = "print-unsigned")] + pub print_unsigned: bool, + + /// Print signed proof content on stdout + #[structopt(long = "print-signed")] + pub print_signed: bool, + + /// Print signed proof content on stdout + #[structopt(long = "no-store")] + pub no_store: bool, +} + +#[derive(Debug, StructOpt, Clone)] +pub struct Review { + #[structopt(flatten)] + pub common: ReviewOrGotoCommon, + + #[structopt(flatten)] + pub common_proof_create: CommonProofCreate, +} + +#[derive(Debug, StructOpt, Clone)] +pub struct ExportId { + pub id: Option, +} + +#[derive(Debug, StructOpt, Clone)] +pub enum Export { + #[structopt(name = "id")] + Id(ExportId), +} + +#[derive(Debug, StructOpt, Clone)] +pub enum Import { + #[structopt(name = "id")] + Id, +} #[derive(Debug, StructOpt, Clone)] pub enum Command { /// Create an Id, ... @@ -199,11 +265,11 @@ pub enum Command { /// Review a crate #[structopt(name = "review")] - Review(ReviewOrGoto), + Review(Review), /// Flag a crate as buggy/low-quality/dangerous #[structopt(name = "flag")] - Flag(ReviewOrGoto), + Flag(Review), /// Query Ids, packages, reviews... #[structopt(name = "query")] @@ -226,14 +292,6 @@ pub enum Command { #[structopt(raw(setting = "structopt::clap::AppSettings::TrailingVarArg"))] Git(Git), - /// See changes in the local proof repository (alias to `git diff`) - #[structopt(name = "diff")] - Diff, - - /// Commit changes to the local proof repository (alias to `git commit -a`) - #[structopt(name = "commit")] - Commit, - /// Push local changes to the public proof repository (alias to `git push HEAD`) #[structopt(name = "push")] Push, @@ -248,11 +306,27 @@ pub enum Command { /// Start a shell in source directory of a crate under review #[structopt(name = "goto")] - Goto(ReviewOrGoto), + Goto(ReviewOrGotoCommon), + + /// Open source code of a crate + #[structopt(name = "open")] + Open(ReviewOrGotoCommon), /// Clean a crate source code (eg. after review) #[structopt(name = "clean")] - Clean(ReviewOrGoto), + Clean(ReviewOrGotoCommon), + + /// Export an id, ... + #[structopt(name = "export")] + Export(Export), + + /// Import an Id, ... + #[structopt(name = "import")] + Import(Import), + + /// Update data from online sources (crates.io) + #[structopt(name = "update")] + Update, } /// Cargo will pass the name of the `cargo-` diff --git a/cargo-crev/src/prelude.rs b/cargo-crev/src/prelude.rs index 62a33885..3644ae38 100644 --- a/cargo-crev/src/prelude.rs +++ b/cargo-crev/src/prelude.rs @@ -1,14 +1,2 @@ -use std::ops::Deref; - pub use common_failures::prelude::*; pub use failure::{bail, format_err}; - -pub trait OptionDeref { - fn as_deref(&self) -> Option<&T::Target>; -} - -impl OptionDeref for Option { - fn as_deref(&self) -> Option<&T::Target> { - self.as_ref().map(Deref::deref) - } -} diff --git a/cargo-crev/src/term.rs b/cargo-crev/src/term.rs index b9aca09b..f86219c4 100644 --- a/cargo-crev/src/term.rs +++ b/cargo-crev/src/term.rs @@ -1,57 +1,85 @@ -use crev_lib::Colored; -use std::fmt::Arguments; -use std::io::{self, Write}; -use term::{self, StderrTerminal, StdoutTerminal}; +use crev_lib::VerificationStatus; +use std::{ + fmt::Arguments, + io::{self, Write}, +}; +use term::{ + self, + color::{self, Color}, + StderrTerminal, StdoutTerminal, +}; + +pub fn verification_status_color(s: &VerificationStatus) -> Option { + match s { + VerificationStatus::Verified(_) => Some(term::color::GREEN), + VerificationStatus::Flagged => Some(term::color::YELLOW), + VerificationStatus::Dangerous => Some(term::color::RED), + _ => None, + } +} + +pub fn known_owners_count_color(count: usize) -> Option { + if count > 0 { + Some(color::GREEN) + } else { + None + } +} pub struct Term { pub stdout_is_tty: bool, pub stderr_is_tty: bool, + pub stdin_is_tty: bool, stdout: Option>, + #[allow(unused)] stderr: Option>, } -fn output_to( +fn output_to( args: std::fmt::Arguments, - t: &T, + color: Option, term: &mut dyn term::Terminal, is_tty: bool, ) -> io::Result<()> where - T: Colored, O: Write, { let use_color = is_tty && term.supports_color(); if use_color { - if let Some(color) = t.color() { + if let Some(color) = color { term.fg(color)? } } term.get_mut().write_fmt(args)?; - if use_color { + if use_color && color.is_some() { term.reset()?; } Ok(()) } + impl Term { pub fn new() -> Term { Term { stdout: term::stdout(), stderr: term::stderr(), + stdin_is_tty: atty::is(atty::Stream::Stdin), stdout_is_tty: atty::is(atty::Stream::Stdout), stderr_is_tty: atty::is(atty::Stream::Stderr), } } - pub fn stdout(&mut self, fmt: Arguments, t: &T) -> io::Result<()> + pub fn print(&mut self, fmt: Arguments, color: C) -> io::Result<()> where - T: Colored, + C: Into>, { + let color = color.into(); + if let Some(ref mut term) = self.stdout { output_to( fmt, - t, + color, (&mut **term) as &mut term::Terminal, self.stdout_is_tty, )?; @@ -60,51 +88,20 @@ impl Term { } #[allow(unused)] - pub fn stderr(&mut self, fmt: Arguments, t: &T) -> io::Result<()> + pub fn eprint(&mut self, fmt: Arguments, color: C) -> io::Result<()> where - T: Colored, + C: Into>, { + let color = color.into(); + if let Some(ref mut term) = self.stderr { output_to( fmt, - t, + color, (&mut **term) as &mut term::Terminal, - self.stderr_is_tty, + self.stdout_is_tty, )?; } Ok(()) } - - /* - fn set_term_color(&self, t: &mut Box) -> Result<()> { - if !t.supports_color() { - return Ok(()); - } - - match *self { - VerificationStatus::Verified => { - t.fg(term::color::GREEN)?; - }, - VerificationStatus::Flagged => { - t.fg(term::color::RED)?; - }, - _ => {} - } - Ok(()) - } - - pub fn write_colored_to_stdout(&self) -> Result<()> { - match term::stdout() { - Some(ref mut t) => { - self.set_term_color(t)?; - write!(t, "{:8}", *self)?; - t.reset()?; - } - None => { - print!("{:8}", *self); - } - } - Ok(()) - } - */ } diff --git a/cargo-crev/src/tokei.rs b/cargo-crev/src/tokei.rs new file mode 100644 index 00000000..b3fa54ac --- /dev/null +++ b/cargo-crev/src/tokei.rs @@ -0,0 +1,16 @@ +use crate::prelude::*; +use std::path::Path; +use tokei::{LanguageType, Languages}; + +pub fn get_rust_line_count(path: &Path) -> Result { + let path = format!("{}", path.display()); + let paths = &[path.as_str()]; + let excluded = vec![]; + let mut languages = Languages::new(); + languages.get_statistics(paths, excluded, None); + let language_map = languages.remove_empty(); + let rust = language_map + .get(&LanguageType::Rust) + .ok_or_else(|| format_err!("Rust should work"))?; + Ok(rust.code) +} diff --git a/crev-bin/rustfmt.toml b/crev-bin/rustfmt.toml new file mode 100644 index 00000000..807b07b8 --- /dev/null +++ b/crev-bin/rustfmt.toml @@ -0,0 +1,2 @@ +edition = "2018" +merge_imports = true diff --git a/crev-bin/src/main.rs b/crev-bin/src/main.rs index 96ac436b..afd9cc6d 100644 --- a/crev-bin/src/main.rs +++ b/crev-bin/src/main.rs @@ -8,7 +8,6 @@ use rprompt; #[macro_use] extern crate structopt; -use crev_lib::TrustOrDistrust::*; use crev_lib::{local::Local, repo::Repo}; use default::default; use hex; @@ -20,15 +19,11 @@ mod util; main!(|opts: opts::Opts| match opts.command { opts::Command::Id(id) => match id.id_command { - opts::IdCommand::Show => crev_lib::show_current_id()?, + opts::IdCommand::Show => unimplemented!(), opts::IdCommand::New => unimplemented!(), }, opts::Command::Trust(trust) => match trust { - opts::Trust::Add(trust) => { - let local = Local::auto_open()?; - let passphrase = crev_common::read_passphrase()?; - local.build_trust_proof(trust.pub_ids, &passphrase, Trust)?; - } + opts::Trust::Add(_trust) => unimplemented!(), }, opts::Command::Add(add) => { let mut repo = Repo::auto_open()?; @@ -36,10 +31,9 @@ main!(|opts: opts::Opts| match opts.command { } opts::Command::Commit(opts) => { let mut repo = Repo::auto_open()?; - let passphrase = crev_common::read_passphrase()?; if opts.all { } else { - repo.commit(&passphrase, opts.allow_dirty)?; + repo.commit(&crev_common::read_passphrase, opts.allow_dirty)?; } } opts::Command::Package(package) => match package { @@ -50,12 +44,15 @@ main!(|opts: opts::Opts| match opts.command { } opts::Package::Trust(package_trust) => { let mut repo = Repo::auto_open()?; - let passphrase = crev_common::read_passphrase()?; - repo.trust_package(&passphrase, package_trust.allow_dirty)?; + repo.trust_package(&crev_common::read_passphrase, package_trust.allow_dirty)?; } - opts::Package::Verify(verify) => { + opts::Package::Verify(args) => { let mut repo = Repo::auto_open()?; - println!("{}", repo.package_verify(verify.allow_dirty)?); + let local = Local::auto_create_or_open()?; + println!( + "{}", + repo.package_verify(&local, args.allow_dirty, args.for_id, &args.trust_params)? + ); } opts::Package::Digest(digest) => { let mut repo = Repo::auto_open()?; diff --git a/crev-common/Cargo.toml b/crev-common/Cargo.toml index 68a25772..c87e421a 100644 --- a/crev-common/Cargo.toml +++ b/crev-common/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = "2018" name = "crev-common" -version = "0.3.0" +version = "0.4.0" description = "Scalable, social, Code REView system that we desperately need - common code" authors = ["Dawid Ciężarkiewicz "] documentation = "https://docs.rs/crev" @@ -21,3 +21,4 @@ rprompt = "1" rpassword = "2" serde = "1" serde_yaml = "0.8" +rand = "0.5" diff --git a/crev-common/rustfmt.toml b/crev-common/rustfmt.toml new file mode 100644 index 00000000..807b07b8 --- /dev/null +++ b/crev-common/rustfmt.toml @@ -0,0 +1,2 @@ +edition = "2018" +merge_imports = true diff --git a/crev-common/src/blake2b256.rs b/crev-common/src/blake2b256.rs index b935c43a..af5ec65c 100644 --- a/crev-common/src/blake2b256.rs +++ b/crev-common/src/blake2b256.rs @@ -1,5 +1,4 @@ -use digest; -use digest::VariableOutput; +use digest::{self, VariableOutput}; #[derive(Debug, Clone)] pub struct Blake2b256(blake2::VarBlake2b); diff --git a/crev-common/src/convert.rs b/crev-common/src/convert.rs new file mode 100644 index 00000000..63d988a3 --- /dev/null +++ b/crev-common/src/convert.rs @@ -0,0 +1,11 @@ +use std::ops::Deref; + +pub trait OptionDeref { + fn as_deref(&self) -> Option<&T::Target>; +} + +impl OptionDeref for Option { + fn as_deref(&self) -> Option<&T::Target> { + self.as_ref().map(Deref::deref) + } +} diff --git a/crev-common/src/lib.rs b/crev-common/src/lib.rs index e04890e7..60e70ecb 100644 --- a/crev-common/src/lib.rs +++ b/crev-common/src/lib.rs @@ -1,7 +1,9 @@ //! Bunch of code that is auxiliary and common for all `crev` pub mod blake2b256; +pub mod convert; pub mod fs; +pub mod rand; pub mod serde; pub use crate::blake2b256::Blake2b256; @@ -12,10 +14,9 @@ use chrono; use blake2::{digest::FixedOutput, Digest}; use rpassword; use rprompt; -use std::io::{Read, Write}; use std::{ env, - io::{self, BufRead}, + io::{self, BufRead, Read, Write}, path::Path, }; diff --git a/crev-common/src/rand.rs b/crev-common/src/rand.rs new file mode 100644 index 00000000..b95c80c4 --- /dev/null +++ b/crev-common/src/rand.rs @@ -0,0 +1,8 @@ +use rand::{self, Rng}; + +pub fn random_vec(len: usize) -> Vec { + rand::thread_rng() + .sample_iter(&rand::distributions::Standard) + .take(len) + .collect() +} diff --git a/crev-data/Cargo.toml b/crev-data/Cargo.toml index ba95c446..5cfa9abf 100644 --- a/crev-data/Cargo.toml +++ b/crev-data/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = '2018' name = "crev-data" -version = "0.3.0" +version = "0.4.0" description = "Scalable, social, Code REView system that we desperately need - data types library" authors = ["Dawid Ciężarkiewicz "] documentation = "https://docs.rs/crev" @@ -12,11 +12,10 @@ license = "MPL-2.0 OR MIT OR Apache-2.0" readme = "../README.md" [dependencies] -crev-common = { path = "../crev-common", version = "0.3" } +crev-common = { path = "../crev-common", version = "0.4" } blake2 = "0.8" chrono = "0.4" common_failures = "0.1" -miscreant = "0.4.0-beta2" ed25519-dalek = "1.0.0-pre.0" failure = "0.1" serde = "1" diff --git a/crev-data/rustfmt.toml b/crev-data/rustfmt.toml deleted file mode 120000 index 39f97b04..00000000 --- a/crev-data/rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -../rustfmt.toml \ No newline at end of file diff --git a/crev-data/rustfmt.toml b/crev-data/rustfmt.toml new file mode 100644 index 00000000..807b07b8 --- /dev/null +++ b/crev-data/rustfmt.toml @@ -0,0 +1,2 @@ +edition = "2018" +merge_imports = true diff --git a/crev-data/src/digest.rs b/crev-data/src/digest.rs index b522884e..cfdfc581 100644 --- a/crev-data/src/digest.rs +++ b/crev-data/src/digest.rs @@ -1,6 +1,5 @@ use std::fmt; - #[derive(Eq, PartialEq)] pub struct Digest(Vec); diff --git a/crev-data/src/id.rs b/crev-data/src/id.rs index 047c084b..d01d3506 100644 --- a/crev-data/src/id.rs +++ b/crev-data/src/id.rs @@ -1,8 +1,9 @@ -use crate::proof; -use crate::{Result, Url}; +use crate::{proof, Result, Url}; use blake2; -use crev_common; -use crev_common::serde::{as_base64, from_base64}; +use crev_common::{ + self, + serde::{as_base64, from_base64}, +}; use ed25519_dalek::{self, PublicKey, SecretKey}; use rand::OsRng; use std::fmt; @@ -57,6 +58,12 @@ impl Id { Ok(()) } + + pub fn to_bytes(&self) -> Vec { + match self { + Id::Crev { id } => id.clone(), + } + } } impl fmt::Display for Id { @@ -93,28 +100,41 @@ impl PubId { url, }) } -} -/// A `PubId` with the corresponding secret key -#[derive(Debug)] -pub struct OwnId { - pub id: PubId, - pub keypair: ed25519_dalek::Keypair, -} - -impl OwnId { pub fn create_trust_proof( &self, ids: Vec, trust_level: proof::trust::TrustLevel, ) -> Result { Ok(proof::TrustBuilder::default() - .from(self.id.clone()) + .from(self.clone()) .trust(trust_level) .ids(ids) .build() .map_err(|e| format_err!("{}", e))?) } + + pub fn create_package_review_proof( + &self, + package: proof::PackageInfo, + review: proof::review::Review, + comment: String, + ) -> Result { + Ok(proof::review::PackageBuilder::default() + .from(self.clone()) + .package(package) + .review(review) + .comment(comment) + .build() + .map_err(|e| format_err!("{}", e))?) + } +} + +/// A `PubId` with the corresponding secret key +#[derive(Debug)] +pub struct OwnId { + pub id: PubId, + pub keypair: ed25519_dalek::Keypair, } impl AsRef for OwnId { diff --git a/crev-data/src/proof/review/package.rs b/crev-data/src/proof/review/package.rs index 5817ca54..f4ebdf9e 100644 --- a/crev-data/src/proof/review/package.rs +++ b/crev-data/src/proof/review/package.rs @@ -33,10 +33,10 @@ pub struct Package { #[serde(rename = "package")] pub package: proof::PackageInfo, #[builder(default = "Default::default()")] - review: super::Review, + pub review: super::Review, #[serde(skip_serializing_if = "String::is_empty", default = "Default::default")] #[builder(default = "Default::default()")] - comment: String, + pub comment: String, } impl Package { diff --git a/crev-data/src/proof/trust.rs b/crev-data/src/proof/trust.rs index 5b5b32f4..88977546 100644 --- a/crev-data/src/proof/trust.rs +++ b/crev-data/src/proof/trust.rs @@ -20,11 +20,11 @@ fn cur_version() -> i64 { #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialOrd, Ord, PartialEq, Eq)] #[serde(rename_all = "lowercase")] pub enum TrustLevel { - High, - Medium, - Low, - None, Distrust, + None, + Low, + Medium, + High, } impl Default for TrustLevel { @@ -36,7 +36,7 @@ impl Default for TrustLevel { impl fmt::Display for TrustLevel { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use self::TrustLevel::*; - f.write_str(match self { + f.pad(match self { Distrust => "distrust", None => "none", Low => "low", diff --git a/crev-data/src/tests.rs b/crev-data/src/tests.rs index 610f90d8..3faa5cfc 100644 --- a/crev-data/src/tests.rs +++ b/crev-data/src/tests.rs @@ -1,8 +1,7 @@ -use crate::Url; use crate::{ id::OwnId, proof::{self, Proof, Serialized}, - Result, + Result, Url, }; use std::path::PathBuf; diff --git a/crev-lib/Cargo.toml b/crev-lib/Cargo.toml index 9284907e..ba76818b 100644 --- a/crev-lib/Cargo.toml +++ b/crev-lib/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = "2018" name = "crev-lib" -version = "0.3.0" +version = "0.4.0" description = "Scalable, social, Code REView system that we desperately need - core library" authors = ["Dawid Ciężarkiewicz "] documentation = "https://docs.rs/crev" @@ -12,17 +12,17 @@ license = "MPL-2.0 OR MIT OR Apache-2.0" readme = "../README.md" [dependencies] -app_dirs = "1" blake2 = "0.8" chrono = "0.4" common_failures = "0.1" derive_builder = "0.7" digest = "0.8" +directories = "1.0.2" failure = "0.1" git2 = "0.7" hex = "0.3" -miscreant = "0.4.0-beta2" -rand = "0.5.5" +miscreant = { version = "0.4", features = ["soft-aes"] } +rand = "0.5" serde = "1" serde_cbor = "0.9" serde_derive = "1" @@ -32,7 +32,8 @@ walkdir = "2" resiter-dpc-tmp = "0.3" rprompt = "1" default = "0.1" -term = "0.5.1" +insideout = "0.2" +shell-escape = "0.1" [dependencies.argonautica] features = ["serde"] @@ -40,11 +41,11 @@ version = "0.1" [dependencies.crev-common] path = "../crev-common" -version = "0.3" +version = "0.4" [dependencies.crev-data] path = "../crev-data" -version = "0.3" +version = "0.4" [dependencies.crev-recursive-digest] path = "../recursive-digest" diff --git a/crev-lib/rc/doc/README.md b/crev-lib/rc/doc/README.md new file mode 100644 index 00000000..ec3b2683 --- /dev/null +++ b/crev-lib/rc/doc/README.md @@ -0,0 +1,8 @@ + + +# Proof Repository + +This git repository is used a [Crev Proof +Repository](https://github.com/dpc/crev/wiki/Proof-Repository). + + diff --git a/crev-lib/rc/doc/README_USING_THIS_REPO.md b/crev-lib/rc/doc/README_USING_THIS_REPO.md deleted file mode 100644 index 9cbd52d0..00000000 --- a/crev-lib/rc/doc/README_USING_THIS_REPO.md +++ /dev/null @@ -1,6 +0,0 @@ - - -# What is in this repository? - -This git repository is used a [Crev Proof Repository](https://github.com/dpc/crev/wiki/Proof-Repository). - diff --git a/crev-lib/rustfmt.toml b/crev-lib/rustfmt.toml index d4e3c676..807b07b8 100644 --- a/crev-lib/rustfmt.toml +++ b/crev-lib/rustfmt.toml @@ -1,3 +1,2 @@ - +edition = "2018" merge_imports = true - diff --git a/crev-lib/src/id.rs b/crev-lib/src/id.rs index 4ada14ba..b0334ba8 100644 --- a/crev-lib/src/id.rs +++ b/crev-lib/src/id.rs @@ -1,5 +1,7 @@ +use crate::prelude::*; use argonautica::{self, Hasher}; use crev_common::serde::{as_base64, from_base64}; +use crev_data::id::{OwnId, PubId}; use miscreant; use rand::{self, Rng}; use serde_yaml; @@ -9,24 +11,23 @@ use std::{ path::Path, }; -use crate::Result; -use crev_data::id::{OwnId, PubId}; - const CURRENT_LOCKED_ID_SERIALIZATION_VERSION: i64 = -1; +pub type PassphraseFn<'a> = &'a Fn() -> std::io::Result; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct PassConfig { version: u32, variant: String, iterations: u32, #[serde(rename = "memory-size")] memory_size: u32, + lanes: Option, #[serde(serialize_with = "as_base64", deserialize_with = "from_base64")] salt: Vec, } /// Serialized, stored on disk -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct LockedId { version: i64, #[serde(flatten)] @@ -53,7 +54,7 @@ impl fmt::Display for LockedId { impl LockedId { pub fn from_own_id(own_id: &OwnId, passphrase: &str) -> Result { - use miscreant::aead::Algorithm; + use miscreant::aead::Aead; let mut hasher = Hasher::default(); hasher @@ -63,7 +64,7 @@ impl LockedId { let pwhash = hasher.with_password(passphrase).hash_raw()?; - let mut siv = miscreant::aead::Aes256Siv::new(pwhash.raw_hash_bytes()); + let mut siv = miscreant::aead::Aes256SivAead::new(pwhash.raw_hash_bytes()); let seal_nonce: Vec = rand::thread_rng() .sample_iter(&rand::distributions::Standard) @@ -84,6 +85,7 @@ impl LockedId { iterations: hasher_config.iterations(), memory_size: hasher_config.memory_size(), version: 0x13, + lanes: Some(hasher_config.lanes()), variant: hasher_config.variant().as_str().to_string(), }, }) @@ -115,6 +117,10 @@ impl LockedId { Ok(serde_yaml::from_str::(&content)?) } + pub fn from_str(yaml_s: &str) -> Result { + Ok(serde_yaml::from_str::(&yaml_s)?) + } + pub fn to_unlocked(&self, passphrase: &str) -> Result { let LockedId { ref version, @@ -128,10 +134,9 @@ impl LockedId { if *version > CURRENT_LOCKED_ID_SERIALIZATION_VERSION { bail!("Unsupported version: {}", *version); } - use miscreant::aead::Algorithm; + use miscreant::aead::Aead; let mut hasher = Hasher::default(); - hasher .configure_memory_size(pass.memory_size) .configure_version(argonautica::config::Version::from_u32(pass.version)?) @@ -141,19 +146,29 @@ impl LockedId { .configure_hash_len(64) .opt_out_of_secret_key(true); - let pwhash = hasher.with_password(passphrase).hash_raw()?; + if let Some(lanes) = pass.lanes { + hasher.configure_lanes(lanes); + } else { + eprintln!( + "`lanes` not configured. Old bug. See: https://github.com/dpc/crev/issues/151" + ); + eprintln!("Using `lanes: {}`", hasher.config().lanes()); + } - let mut siv = miscreant::aead::Aes256Siv::new(pwhash.raw_hash_bytes()); + let passphrase_hash = hasher.with_password(passphrase).hash_raw()?; + let mut siv = miscreant::aead::Aes256SivAead::new(passphrase_hash.raw_hash_bytes()); - let sec_key = siv.open(&seal_nonce, &[], &sealed_secret_key)?; + let secret_key = siv + .open(&seal_nonce, &[], &sealed_secret_key) + .map_err(|_| format_err!("incorrect passphrase"))?; - let res = OwnId::new(url.to_owned(), sec_key)?; + assert!(!secret_key.is_empty()); - if public_key != &res.keypair.public.to_bytes() { + let result = OwnId::new(url.to_owned(), secret_key)?; + if public_key != &result.keypair.public.to_bytes() { bail!("PubKey mismatch"); } - - Ok(res) + Ok(result) } } } diff --git a/crev-lib/src/lib.rs b/crev-lib/src/lib.rs index 3d9eea2b..bd4be9c8 100644 --- a/crev-lib/src/lib.rs +++ b/crev-lib/src/lib.rs @@ -1,30 +1,29 @@ #[macro_use] extern crate serde_derive; -extern crate term; - -use common_failures::prelude::*; #[macro_use] extern crate failure; pub mod id; pub mod local; +pub(crate) mod prelude; pub mod proof; +pub mod proofdb; pub mod repo; pub mod staging; -pub mod trustdb; pub mod util; -pub use self::local::Local; +use crate::{prelude::*, proofdb::TrustSet}; use crev_data::Digest; -use crev_data::Id; -use std::convert::AsRef; use std::{ collections::HashSet, fmt, path::{Path, PathBuf}, }; +pub use self::local::Local; +pub use crate::proofdb::{ProofDB, TrustDistanceParams}; + /// Trait representing a place that can keep proofs /// /// Typically serialized and persisted. @@ -39,6 +38,15 @@ pub enum TrustOrDistrust { Distrust, } +impl fmt::Display for TrustOrDistrust { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + TrustOrDistrust::Trust => f.write_str("trust"), + TrustOrDistrust::Distrust => f.write_str("distrust"), + } + } +} + impl TrustOrDistrust { pub fn is_trust(self) -> bool { if let TrustOrDistrust::Trust = self { @@ -61,24 +69,17 @@ impl TrustOrDistrust { /// Not named `Result` to avoid confusion with `Result` type. #[derive(PartialEq, Eq, Debug)] pub enum VerificationStatus { - Verified, - Unknown, + Verified(crev_data::proof::TrustLevel), + None, Flagged, + Dangerous, } -/// Trait for stuff that has a coresponding color somewhere in the "UI" -// -// TODO: This has to find some better place than here. -pub trait Colored { - fn color(&self) -> Option; -} - -impl Colored for VerificationStatus { - fn color(&self) -> Option { - match *self { - VerificationStatus::Verified => Some(term::color::GREEN), - VerificationStatus::Flagged => Some(term::color::RED), - _ => None, +impl VerificationStatus { + pub fn is_verified(&self) -> bool { + match self { + VerificationStatus::Verified(_) => true, + _ => false, } } } @@ -86,22 +87,22 @@ impl Colored for VerificationStatus { impl fmt::Display for VerificationStatus { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - VerificationStatus::Verified => f.pad("verified"), - VerificationStatus::Unknown => f.pad("unknown"), + VerificationStatus::Verified(level) => f.pad(&level.to_string()), + VerificationStatus::None => f.pad("none"), VerificationStatus::Flagged => f.pad("flagged"), + VerificationStatus::Dangerous => f.pad("danger"), } } } -pub fn dir_or_git_repo_verify( +pub fn dir_or_git_repo_verify

( path: &Path, ignore_list: &HashSet, - db: &trustdb::TrustDB, - trusted_set: &HashSet, + db: &ProofDB, + trusted_set: &TrustSet, ) -> Result where H1: std::hash::BuildHasher + std::default::Default, - H2: std::hash::BuildHasher + std::default::Default, { let digest = if path.join(".git").exists() { get_recursive_digest_for_git_dir(path, ignore_list)? @@ -112,24 +113,23 @@ where >(path, ignore_list)?) }; - Ok(db.verify_digest(&digest, trusted_set)) + Ok(db.verify_package_digest(&digest, trusted_set)) } -pub fn dir_verify( +pub fn dir_verify

( path: &Path, ignore_list: &HashSet, - db: &trustdb::TrustDB, - trusted_set: &HashSet, + db: &ProofDB, + trusted_set: &TrustSet, ) -> Result where H1: std::hash::BuildHasher + std::default::Default, - H2: std::hash::BuildHasher + std::default::Default, { let digest = Digest::from_vec(crev_recursive_digest::get_recursive_digest_for_dir::< crev_common::Blake2b256, H1, >(path, ignore_list)?); - Ok(db.verify_digest(&digest, trusted_set)) + Ok(db.verify_package_digest(&digest, trusted_set)) } pub fn get_dir_digest

(path: &Path, ignore_list: &HashSet) -> Result @@ -144,14 +144,6 @@ where )) } -pub fn show_current_id() -> Result<()> { - let local = Local::auto_open()?; - let id = local.read_current_locked_id()?; - let id = id.to_pubid(); - println!("{} {}", id.id, id.url.url); - Ok(()) -} - pub fn get_recursive_digest_for_git_dir( root_path: &Path, ignore_list: &HashSet, @@ -214,61 +206,5 @@ where )) } -pub fn generate_id( - url: Option, - github_username: Option, - use_https_push: bool, -) -> Result<()> { - let url = match (url, github_username) { - (Some(url), None) => url, - (None, Some(username)) => format!("https://github.com/{}/crev-proofs", username), - (Some(_), Some(_)) => bail!("Can't provide both username and url"), - (None, None) => bail!("Must provide github username or url"), - }; - - if !url.starts_with("https://") { - bail!("URL must start with 'https://"); - } - - let local = Local::auto_create_or_open()?; - local.clone_proof_dir_from_git(&url, use_https_push)?; - - let id = crev_data::id::OwnId::generate(crev_data::Url::new_git(url.clone())); - eprintln!("CrevID will be protected by a passphrase."); - eprintln!("There's no way to recover your CrevID if you forget your passphrase."); - let passphrase = crev_common::read_new_passphrase()?; - let locked = id::LockedId::from_own_id(&id, &passphrase)?; - - local.save_locked_id(&locked)?; - local.save_current_id(id.as_ref())?; - - eprintln!(""); - eprintln!("Your CrevID was created and will be printed below in an encrypted form."); - eprintln!("Make sure to back it up on another device, to prevent loosing it."); - - eprintln!(""); - println!("{}", locked); - - local.init_readme_using_this_repo_file()?; - - Ok(()) -} - -pub fn switch_id(id_str: &str) -> Result<()> { - let id: Id = Id::crevid_from_str(id_str)?; - let local = Local::auto_open()?; - local.save_current_id(&id)?; - - Ok(()) -} - -pub fn list_own_ids() -> Result<()> { - let local = Local::auto_open()?; - for id in local.list_ids()? { - println!("{} {}", id.id, id.url.url); - } - Ok(()) -} - #[cfg(test)] mod tests; diff --git a/crev-lib/src/local.rs b/crev-lib/src/local.rs index c0d544eb..058d1e90 100644 --- a/crev-lib/src/local.rs +++ b/crev-lib/src/local.rs @@ -1,34 +1,62 @@ -use crate::ProofStore; use crate::{ - id::{self, LockedId}, - trustdb, - util::{self, APP_INFO}, - Result, + id::{self, LockedId, PassphraseFn}, + prelude::*, + util, ProofDB, ProofStore, +}; +use crev_common::{ + self, + serde::{as_base64, from_base64}, +}; +use crev_data::{ + id::OwnId, + proof::{self, trust::TrustLevel}, + Id, PubId, Url, }; -use app_dirs::{app_root, AppDataType}; -use crev_common; -use crev_data::{id::OwnId, proof, proof::trust::TrustLevel, Id, PubId, Url}; use default::default; +use directories::ProjectDirs; use failure::ResultExt; use git2; +use insideout::InsideOut; use resiter_dpc_tmp::*; use serde_yaml; -use std::cell::RefCell; use std::{ + cell::RefCell, collections::HashSet, ffi::OsString, fs, - io::Write, + io::{BufRead, Write}, path::{Path, PathBuf}, }; const CURRENT_USER_CONFIG_SERIALIZATION_VERSION: i64 = -1; +fn generete_salt() -> Vec { + crev_common::rand::random_vec(32) +} + +/// Backfill the host salt +/// +/// For people that have configs generated when +/// `host_salt` was not a thing - generate some +/// form of stable id +/// +/// TODO: at some point this should no longer be neccessary +fn backfill_salt() -> Vec { + crev_common::blake2b256sum(b"BACKFILLED_SUM") +} + #[derive(Serialize, Deserialize, Debug, Clone)] pub struct UserConfig { pub version: i64, #[serde(rename = "current-id")] pub current_id: Option, + #[serde( + rename = "host-salt", + serialize_with = "as_base64", + deserialize_with = "from_base64", + default = "backfill_salt" + )] + host_salt: Vec, } impl Default for UserConfig { @@ -36,145 +64,43 @@ impl Default for UserConfig { Self { version: CURRENT_USER_CONFIG_SERIALIZATION_VERSION, current_id: None, + host_salt: generete_salt(), } } } impl UserConfig { pub fn get_current_userid(&self) -> Result<&Id> { - self.current_id - .as_ref() + self.get_current_userid_opt() .ok_or_else(|| format_err!("Current Id not set")) } -} - -#[derive(PartialEq, Debug, Default)] -pub struct GitUrlComponents { - pub domain: String, - pub username: String, - pub repo: String, - pub suffix: String, -} - -pub fn parse_git_url_https(http_url: &str) -> Option { - let mut split: Vec<_> = http_url.split('/').collect(); - - while let Some(&"") = split.last() { - split.pop(); - } - if split.len() != 5 { - return None; + pub fn get_current_userid_opt(&self) -> Option<&Id> { + self.current_id.as_ref() } - if split[0] != "https:" && split[0] != "http:" { - return None; - } - let domain = split[2]; - let username = split[3]; - let repo = split[4]; - let suffix = if repo.ends_with(".git") { "" } else { ".git" }; - - Some(GitUrlComponents { - domain: domain.to_string(), - username: username.to_string(), - repo: repo.to_string(), - suffix: suffix.to_string(), - }) -} - -fn fetch_and_checkout_git_repo(repo: &git2::Repository) -> Result<()> { - repo.find_remote("origin")?.fetch(&["master"], None, None)?; - repo.set_head("FETCH_HEAD")?; - let mut opts = git2::build::CheckoutBuilder::new(); - opts.force(); - repo.checkout_head(Some(&mut opts))?; - Ok(()) -} - -#[test] -fn parse_git_url_https_test() { - assert_eq!( - parse_git_url_https("https://github.com/dpc/trust"), - Some(GitUrlComponents { - domain: "github.com".to_string(), - username: "dpc".to_string(), - repo: "trust".to_string(), - suffix: ".git".to_string() - }) - ); - assert_eq!( - parse_git_url_https("https://gitlab.com/hackeraudit/web.git"), - Some(GitUrlComponents { - domain: "gitlab.com".to_string(), - username: "hackeraudit".to_string(), - repo: "web.git".to_string(), - suffix: "".to_string() - }) - ); - assert_eq!( - parse_git_url_https("https://gitlab.com/hackeraudit/web.git/"), - Some(GitUrlComponents { - domain: "gitlab.com".to_string(), - username: "hackeraudit".to_string(), - repo: "web.git".to_string(), - suffix: "".to_string() - }) - ); - assert_eq!( - parse_git_url_https("https://gitlab.com/hackeraudit/web.git/////////"), - Some(GitUrlComponents { - domain: "gitlab.com".to_string(), - username: "hackeraudit".to_string(), - repo: "web.git".to_string(), - suffix: "".to_string() - }) - ); -} - -fn https_to_git_url(http_url: &str) -> Option { - parse_git_url_https(http_url).map(|components| { - format!( - "git@{}:{}/{}{}", - components.domain, components.username, components.repo, components.suffix - ) - }) -} - -#[test] -fn https_to_git_url_test() { - assert_eq!( - https_to_git_url("https://github.com/dpc/trust"), - Some("git@github.com:dpc/trust.git".into()) - ); - assert_eq!( - https_to_git_url("https://gitlab.com/hackeraudit/web.git"), - Some("git@gitlab.com:hackeraudit/web.git".into()) - ); - assert_eq!( - https_to_git_url("https://gitlab.com/hackeraudit/web.git/"), - Some("git@gitlab.com:hackeraudit/web.git".into()) - ); - assert_eq!( - https_to_git_url("https://gitlab.com/hackeraudit/web.git/////////"), - Some("git@gitlab.com:hackeraudit/web.git".into()) - ); } /// Local config stored in `~/.config/crev` +/// +/// This managed IDs, local proof repository, etc. pub struct Local { root_path: PathBuf, cache_path: PathBuf, cur_url: RefCell>, + user_config: RefCell>, } impl Local { #[allow(clippy::new_ret_no_self)] fn new() -> Result { - let root_path = app_root(AppDataType::UserConfig, &APP_INFO)?; - let cache_path = app_root(AppDataType::UserCache, &APP_INFO)?; + let proj_dir = ProjectDirs::from("", "", "crev") + .expect("no valid home directory path could be retrieved from the operating system"); + let root_path = proj_dir.config_dir().into(); + let cache_path = proj_dir.cache_dir().into(); Ok(Self { root_path, cache_path, cur_url: RefCell::new(None), + user_config: RefCell::new(None), }) } @@ -189,12 +115,14 @@ impl Local { bail!("User config not-initialized. Use `crev new id` to generate CrevID."); } + *repo.user_config.borrow_mut() = Some(repo.load_user_config()?); Ok(repo) } pub fn auto_create() -> Result { let repo = Self::new()?; fs::create_dir_all(&repo.root_path)?; + fs::create_dir_all(&repo.cache_remotes_path())?; let config_path = repo.user_config_path(); if config_path.exists() { @@ -202,6 +130,7 @@ impl Local { } let config: UserConfig = default(); repo.store_user_config(&config)?; + *repo.user_config.borrow_mut() = Some(config); Ok(repo) } @@ -219,6 +148,26 @@ impl Local { Ok(self.load_user_config()?.get_current_userid()?.to_owned()) } + pub fn read_current_id_opt(&self) -> Result> { + Ok(self.load_user_config()?.get_current_userid_opt().cloned()) + } + + /// Calculate `for_id` that is used in a lot of operations + /// + /// * if `id_str` is given - convert to Id + /// * otherwise return current id + pub fn get_for_id_from_str_opt(&self, id_str: Option<&str>) -> Result> { + id_str + .map(crev_data::id::Id::crevid_from_str) + .or_else(|| self.read_current_id_opt().inside_out()) + .inside_out() + } + + pub fn get_for_id_from_str(&self, id_str: Option<&str>) -> Result { + self.get_for_id_from_str_opt(id_str)? + .ok_or_else(|| format_err!("Id not specified and current id not set")) + } + pub fn save_current_id(&self, id: &Id) -> Result<()> { let path = self.id_path(id); if !path.exists() { @@ -229,6 +178,11 @@ impl Local { let mut config = self.load_user_config()?; config.current_id = Some(id.clone()); + // Change the old, backfilled `host_salt` the first time + // the id is being switched + if config.host_salt == backfill_salt() { + config.host_salt = generete_salt(); + } self.store_user_config(&config)?; Ok(()) @@ -286,14 +240,15 @@ impl Local { let config_str = serde_yaml::to_string(&config)?; - Ok(util::store_str_to_file(&path, &config_str)?) + util::store_str_to_file(&path, &config_str)?; + + *self.user_config.borrow_mut() = Some(config.clone()); + Ok(()) } - pub fn get_current_userid(&self) -> Result { + pub fn get_current_userid(&self) -> Result> { let config = self.load_user_config()?; - Ok(config - .current_id - .ok_or_else(|| format_err!("Current id not set"))?) + Ok(config.current_id) } pub fn read_locked_id(&self, id: &Id) -> Result { @@ -301,19 +256,44 @@ impl Local { LockedId::read_from_yaml_file(&path) } + pub fn read_current_locked_id_opt(&self) -> Result> { + self.get_current_userid()? + .map(|current_id| self.read_locked_id(¤t_id)) + .inside_out() + } + pub fn read_current_locked_id(&self) -> Result { - let current_id = self.get_current_userid()?; - self.read_locked_id(¤t_id) + self.read_current_locked_id_opt()? + .ok_or_else(|| format_err!("Current Id not set")) } - pub fn read_current_unlocked_id(&self, passphrase: &str) -> Result { - let current_id = self.get_current_userid()?; - self.read_unlocked_id(¤t_id, passphrase) + pub fn read_current_unlocked_id_opt( + &self, + passphrase_callback: PassphraseFn, + ) -> Result> { + self.get_current_userid()? + .map(|current_id| self.read_unlocked_id(¤t_id, passphrase_callback)) + .inside_out() } - pub fn read_unlocked_id(&self, id: &Id, passphrase: &str) -> Result { + pub fn read_current_unlocked_id(&self, passphrase_callback: PassphraseFn) -> Result { + self.read_current_unlocked_id_opt(passphrase_callback)? + .ok_or_else(|| format_err!("Current Id not set")) + } + + pub fn read_unlocked_id(&self, id: &Id, passphrase_callback: PassphraseFn) -> Result { let locked = self.read_locked_id(id)?; - locked.to_unlocked(passphrase) + let mut i = 0; + loop { + let passphrase = passphrase_callback()?; + let res = locked.to_unlocked(&passphrase); + if let Ok(id) = res { + return Ok(id); + } else if i == 5 { + return res; + } + i += 1; + } } pub fn save_locked_id(&self, id: &id::LockedId) -> Result<()> { @@ -331,7 +311,7 @@ impl Local { let push_url = if use_https_push { git_https_url.to_string() } else { - match https_to_git_url(git_https_url) { + match util::git::https_to_git_url(git_https_url) { Some(git_url) => git_url, None => { eprintln!("Could not deduce `ssh` push url. Call:"); @@ -370,27 +350,51 @@ impl Local { } pub fn init_readme_using_this_repo_file(&self) -> Result<()> { + const README_MARKER_V0: &str = "CREV_README_MARKER_V0"; + let proof_dir = self.get_proofs_dir_path()?; - let mut file = std::fs::File::create(proof_dir.join("README_USING_THIS_REPO.md"))?; - file.write_all(include_bytes!("../rc/doc/README_USING_THIS_REPO.md"))?; - file.flush()?; - self.proof_dir_git_add_path(&PathBuf::from("README_USING_THIS_REPO.md"))?; + let path = proof_dir.join("README.md"); + if path.exists() { + if let Some(line) = std::io::BufReader::new(std::fs::File::open(&path)?) + .lines() + .into_iter() + .filter(|line| { + if let Ok(ref line) = line { + line.trim() != "" + } else { + true + } + }) + .next() + { + if line?.contains(README_MARKER_V0) { + return Ok(()); + } + } + } + + std::fs::write( + proof_dir.join("README.md"), + &include_bytes!("../rc/doc/README.md")[..], + )?; + self.proof_dir_git_add_path(Path::new("README.md"))?; Ok(()) } // Get path relative to `get_proofs_dir_path` to store the `proof` - fn get_proof_rel_store_path(&self, proof: &proof::Proof) -> PathBuf { - crate::proof::rel_store_path(&proof.content) + fn get_proof_rel_store_path(&self, proof: &proof::Proof, host_salt: &[u8]) -> PathBuf { + crate::proof::rel_store_path(&proof.content, host_salt) } - fn get_cur_url(&self) -> Result { + fn get_cur_url(&self) -> Result> { let url = self.cur_url.borrow().clone(); Ok(if let Some(url) = url { - url - } else { - let locked_id = self.read_current_locked_id()?; + Some(url) + } else if let Some(locked_id) = self.read_current_locked_id_opt()? { *self.cur_url.borrow_mut() = Some(locked_id.url.clone()); - locked_id.url + Some(locked_id.url) + } else { + None }) } @@ -404,32 +408,36 @@ impl Local { } // Path where the `proofs` are stored under `git` repository - pub fn get_proofs_dir_path(&self) -> Result { + pub fn get_proofs_dir_path_opt(&self) -> Result> { Ok(self - .root_path - .join("proofs") - .join(self.get_cur_url()?.digest().to_string())) + .get_cur_url()? + .map(|url| self.root_path.join("proofs").join(url.digest().to_string()))) + } + + pub fn get_proofs_dir_path(&self) -> Result { + self.get_proofs_dir_path_opt()? + .ok_or_else(|| format_err!("Current Id not set")) } pub fn build_trust_proof( &self, + from_id: &PubId, id_strings: Vec, - passphrase: &str, trust_or_distrust: crate::TrustOrDistrust, - ) -> Result<()> { + ) -> Result { if id_strings.is_empty() { bail!("No ids given."); } - let mut trustdb = trustdb::TrustDB::new(); - trustdb.import_from_iter(self.proofs_iter()?); - trustdb.import_from_iter(proofs_iter_for_path(self.cache_remotes_path())); + let mut db = crate::ProofDB::new(); + db.import_from_iter(self.proofs_iter()?); + db.import_from_iter(proofs_iter_for_path(self.cache_remotes_path())); let mut pub_ids = vec![]; for id_string in id_strings { let id = Id::crevid_from_str(&id_string)?; - if let Some(url) = trustdb.lookup_url(&id) { + if let Some(url) = db.lookup_url(&id) { pub_ids.push(PubId::new(id, url.to_owned())); } else { bail!( @@ -439,9 +447,7 @@ impl Local { } } - let own_id = self.read_current_unlocked_id(&passphrase)?; - - let trust = own_id.create_trust_proof( + let trust = from_id.create_trust_proof( pub_ids, if trust_or_distrust.is_trust() { TrustLevel::Medium @@ -450,22 +456,25 @@ impl Local { }, )?; - let trust = util::edit_proof_content_iteractively(&trust.into())?; - - let proof = trust.sign_by(&own_id)?; - - self.insert(&proof)?; - Ok(()) + Ok(util::edit_proof_content_iteractively(&trust.into())?) } pub fn fetch_url(&self, url: &str) -> Result<()> { - let _success = util::err_eprint_and_ignore(self.fetch_remote_git(url).compat()); + let mut db = self.load_db()?; + if let Some(dir) = self.fetch_proof_repo_import_and_print_counts(url, &mut db) { + let mut db = ProofDB::new(); + db.import_from_iter(proofs_iter_for_path(dir)); + eprintln!("Found proofs from:"); + for (id, count) in db.all_author_ids() { + println!("{:>8} {}", count, id); + } + } Ok(()) } - pub fn fetch_trusted(&self, trust_params: trustdb::TrustDistanceParams) -> Result<()> { + pub fn fetch_trusted(&self, trust_params: crate::TrustDistanceParams) -> Result<()> { let mut already_fetched = HashSet::new(); - let mut db = trustdb::TrustDB::new(); + let mut db = crate::ProofDB::new(); db.import_from_iter(self.proofs_iter()?); db.import_from_iter(proofs_iter_for_path(self.cache_remotes_path())); let user_config = self.load_user_config()?; @@ -477,7 +486,7 @@ impl Local { let trust_set = db.calculate_trust_set(user_config.get_current_userid()?, &trust_params); - for id in &trust_set { + for id in trust_set.trusted_ids() { if already_fetched.contains(id) { continue; } else { @@ -485,15 +494,8 @@ impl Local { } if user_id == id { continue; - } else if let Some(url) = db.lookup_url(id) { - let success = - util::err_eprint_and_ignore(self.fetch_remote_git(&url.url).compat()); - if success { - something_was_fetched = true; - db.import_from_iter(proofs_iter_for_path( - self.get_remote_git_cache_path(&url.url), - )); - } + } else if let Some(url) = db.lookup_url(id).cloned() { + self.fetch_proof_repo_import_and_print_counts(&url.url, &mut db); } else { eprintln!("No URL for {}", id); } @@ -502,13 +504,14 @@ impl Local { Ok(()) } - fn fetch_all_ids_recursively(&self, mut already_fetched_urls: HashSet) -> Result<()> { + fn fetch_all_ids_recursively( + &self, + mut already_fetched_urls: HashSet, + db: &mut ProofDB, + ) -> Result<()> { let mut already_fetched = HashSet::new(); - let mut db = trustdb::TrustDB::new(); - db.import_from_iter(self.proofs_iter()?); - db.import_from_iter(proofs_iter_for_path(self.cache_remotes_path())); let user_config = self.load_user_config()?; - let user_id = user_config.get_current_userid()?; + let user_id = user_config.get_current_userid_opt(); let mut something_was_fetched = true; while something_was_fetched { @@ -520,24 +523,17 @@ impl Local { } else { already_fetched.insert(id.to_owned()); } - if user_id == id { + if user_id == Some(id) { continue; - } else if let Some(url) = db.lookup_url(id) { - let url = url.url.to_string(); + } else if let Some(url) = db.lookup_url(id).cloned() { + let url = url.url; if already_fetched_urls.contains(&url) { continue; } else { already_fetched_urls.insert(url.clone()); } - - let success = util::err_eprint_and_ignore(self.fetch_remote_git(&url).compat()); - if success { - something_was_fetched = true; - db.import_from_iter(proofs_iter_for_path( - self.get_remote_git_cache_path(&url), - )); - } + self.fetch_proof_repo_import_and_print_counts(&url, db); } else { eprintln!("No URL for {}", id); } @@ -552,23 +548,66 @@ impl Local { self.cache_remotes_path().join(digest.to_string()) } - pub fn fetch_remote_git(&self, url: &str) -> Result<()> { + /// Fetch a git proof repository + /// + /// Returns url where it was cloned/fetched + pub fn fetch_remote_git(&self, url: &str) -> Result { let dir = self.get_remote_git_cache_path(url); if dir.exists() { - eprintln!("Fetching {} to {}", url, dir.display()); - let repo = git2::Repository::open(dir)?; - fetch_and_checkout_git_repo(&repo)? + let repo = git2::Repository::open(&dir)?; + util::git::fetch_and_checkout_git_repo(&repo)? } else { - eprintln!("Cloning {} to {}", url, dir.display()); - git2::Repository::clone(url, dir)?; + git2::Repository::clone(url, &dir)?; } - Ok(()) + Ok(dir) + } + + pub fn fetch_proof_repo_import_and_print_counts( + &self, + url: &str, + db: &mut ProofDB, + ) -> Option { + let prev_pkg_review_count = db.unique_package_review_proof_count(); + let prev_trust_count = db.unique_trust_proof_count(); + + eprint!("Fetching {}... ", url); + match self.fetch_remote_git(url) { + Ok(dir) => { + db.import_from_iter(proofs_iter_for_path(dir.clone())); + + eprint!("OK"); + + let new_pkg_review_count = + db.unique_package_review_proof_count() - prev_pkg_review_count; + let new_trust_count = db.unique_trust_proof_count() - prev_trust_count; + + if new_trust_count > 0 { + eprint!("; {} new trust proofs", new_pkg_review_count); + } + if new_pkg_review_count > 0 { + eprint!("; {} new package reviews", new_pkg_review_count); + } + eprintln!(""); + Some(dir) + } + Err(e) => { + eprintln!("Error: {}", e); + None + } + } } pub fn fetch_all(&self) -> Result<()> { let mut fetched_urls = HashSet::new(); + let mut db = self.load_db()?; + + // Temporarily hardcode `dpc`'s proof-repo url + let dpc_url = "https://github.com/dpc/crev-proofs"; + self.fetch_proof_repo_import_and_print_counts(dpc_url, &mut db); + fetched_urls.insert(dpc_url.to_owned()); + for entry in fs::read_dir(self.cache_remotes_path())? { let path = entry?.path(); if !path.is_dir() { @@ -593,9 +632,10 @@ impl Local { match url { Ok(url) => { - fetched_urls.insert(url.clone()); - let _success = - util::err_eprint_and_ignore(self.fetch_remote_git(&url).compat()); + if !fetched_urls.contains(&url) { + fetched_urls.insert(url.clone()); + self.fetch_proof_repo_import_and_print_counts(&url, &mut db); + } } Err(e) => { eprintln!("ERR: {} {}", path.display(), e); @@ -603,14 +643,21 @@ impl Local { } } - self.fetch_all_ids_recursively(fetched_urls)?; + self.fetch_all_ids_recursively(fetched_urls, &mut db)?; Ok(()) } pub fn run_git(&self, args: Vec) -> Result { let orig_dir = std::env::current_dir()?; - std::env::set_current_dir(self.get_proofs_dir_path()?)?; + let proof_dir_path = self.get_proofs_dir_path()?; + if !proof_dir_path.exists() { + let id = self.read_current_locked_id()?; + self.clone_proof_dir_from_git(&id.url.url, false)?; + } + + std::env::set_current_dir(proof_dir_path) + .with_context(|_| "Trying to change dir to the current local proof repo")?; use std::process::Command; @@ -630,33 +677,139 @@ impl Local { Ok(()) } - pub fn load_db( - &self, - params: &trustdb::TrustDistanceParams, - ) -> Result<(trustdb::TrustDB, HashSet)> { - let user_config = self.load_user_config()?; - let mut db = trustdb::TrustDB::new(); + /// Create a new proofdb, and populate it with local repo + /// and cache content. + pub fn load_db(&self) -> Result { + let mut db = crate::ProofDB::new(); db.import_from_iter(self.proofs_iter()?); db.import_from_iter(proofs_iter_for_path(self.cache_remotes_path())); - let trusted_set = db.calculate_trust_set(user_config.get_current_userid()?, ¶ms); - Ok((db, trusted_set)) + Ok(db) } pub fn proof_dir_git_add_path(&self, rel_path: &Path) -> Result<()> { let proof_dir = self.get_proofs_dir_path()?; - let repo = git2::Repository::init(&proof_dir)?; + let repo = git2::Repository::open(&proof_dir)?; let mut index = repo.index()?; index.add_path(rel_path)?; index.write()?; Ok(()) } + + pub fn proof_dir_commit(&self, commit_msg: &str) -> Result<()> { + let proof_dir = self.get_proofs_dir_path()?; + let repo = git2::Repository::open(&proof_dir)?; + let mut index = repo.index()?; + let tree_id = index.write_tree()?; + let tree = repo.find_tree(tree_id)?; + let head = repo.head()?.peel_to_commit()?; + + let signature = repo.signature()?; + + repo.commit( + Some("HEAD"), + &signature, + &signature, + commit_msg, + &tree, + &[&head], + )?; + + Ok(()) + } + + pub fn show_current_id(&self) -> Result<()> { + if let Some(id) = self.read_current_locked_id_opt()? { + let id = id.to_pubid(); + println!("{} {}", id.id, id.url.url); + } + Ok(()) + } + + pub fn generate_id( + &self, + url: Option, + github_username: Option, + use_https_push: bool, + ) -> Result<()> { + let url = match (url, github_username) { + (Some(url), None) => url, + (None, Some(username)) => format!("https://github.com/{}/crev-proofs", username), + (Some(_), Some(_)) => bail!("Can't provide both username and url"), + (None, None) => bail!("Must provide github username or url"), + }; + + if !url.starts_with("https://") { + bail!("URL must start with 'https://"); + } + + self.clone_proof_dir_from_git(&url, use_https_push)?; + + let id = crev_data::id::OwnId::generate(crev_data::Url::new_git(url.clone())); + eprintln!("CrevID will be protected by a passphrase."); + eprintln!("There's no way to recover your CrevID if you forget your passphrase."); + let passphrase = crev_common::read_new_passphrase()?; + let locked = id::LockedId::from_own_id(&id, &passphrase)?; + + self.save_locked_id(&locked)?; + self.save_current_id(id.as_ref())?; + + eprintln!(""); + eprintln!("Your CrevID was created and will be printed below in an encrypted form."); + eprintln!("Make sure to back it up on another device, to prevent loosing it."); + + eprintln!(""); + println!("{}", locked); + + self.init_readme_using_this_repo_file()?; + + Ok(()) + } + + pub fn switch_id(&self, id_str: &str) -> Result<()> { + let id: Id = Id::crevid_from_str(id_str)?; + self.save_current_id(&id)?; + + Ok(()) + } + + pub fn list_own_ids(&self) -> Result<()> { + for id in self.list_ids()? { + println!("{} {}", id.id, id.url.url); + } + Ok(()) + } + + pub fn export_locked_id(&self, id_str: Option) -> Result { + let id = if let Some(id_str) = id_str { + let id = Id::crevid_from_str(&id_str)?; + self.read_locked_id(&id)? + } else { + self.read_current_locked_id()? + }; + + Ok(id.to_string()) + } + + pub fn import_locked_id(&self, locked_id_serialized: &str) -> Result { + let id = LockedId::from_str(locked_id_serialized)?; + self.save_locked_id(&id)?; + Ok(id.to_pubid()) + } } impl ProofStore for Local { fn insert(&self, proof: &proof::Proof) -> Result<()> { - let rel_store_path = self.get_proof_rel_store_path(proof); + let rel_store_path = self.get_proof_rel_store_path( + proof, + &self + .user_config + .borrow() + .as_ref() + .expect("User config loaded") + .host_salt, + ); let path = self.get_proofs_dir_path()?.join(&rel_store_path); fs::create_dir_all(path.parent().expect("Not a root dir"))?; @@ -677,11 +830,15 @@ impl ProofStore for Local { } fn proofs_iter(&self) -> Result>> { - Ok(proofs_iter_for_path(self.get_proofs_dir_path()?)) + Ok(Box::new( + self.get_proofs_dir_path_opt()? + .into_iter() + .flat_map(proofs_iter_for_path), + )) } } -fn proofs_iter_for_path(path: PathBuf) -> Box> { +fn proofs_iter_for_path(path: PathBuf) -> impl Iterator { use std::ffi::OsStr; let file_iter = walkdir::WalkDir::new(path) .into_iter() @@ -710,5 +867,5 @@ fn proofs_iter_for_path(path: PathBuf) -> Box> { eprintln!("Failed processing a proof: {}", e); }); - Box::new(proofs_iter.oks()) + proofs_iter.oks() } diff --git a/crev-lib/src/prelude.rs b/crev-lib/src/prelude.rs new file mode 100644 index 00000000..07bcc3b6 --- /dev/null +++ b/crev-lib/src/prelude.rs @@ -0,0 +1 @@ +pub use common_failures::prelude::*; diff --git a/crev-lib/src/proof.rs b/crev-lib/src/proof.rs index b6625730..d5f9081d 100644 --- a/crev-lib/src/proof.rs +++ b/crev-lib/src/proof.rs @@ -11,12 +11,12 @@ fn type_name(content: &Content) -> (&str, Option<&str>) { } /// The path to use under package `.crev/` -pub(crate) fn rel_package_path(content: &Content) -> PathBuf { - rel_store_path(content) +pub(crate) fn rel_package_path(content: &Content, host_salt: &[u8]) -> PathBuf { + rel_store_path(content, host_salt) } /// The path to use under user store -pub(crate) fn rel_store_path(content: &Content) -> PathBuf { +pub(crate) fn rel_store_path(content: &Content, host_salt: &[u8]) -> PathBuf { let (type_name, type_subname) = type_name(content); let date = content .date() @@ -24,9 +24,17 @@ pub(crate) fn rel_store_path(content: &Content) -> PathBuf { .format("%Y-%m") .to_string(); let path = PathBuf::from(content.author_id().to_string()).join(type_name); + let mut host_full_id = host_salt.to_vec(); + host_full_id.append(&mut content.author_id().to_bytes()); + let host_plus_id_digest = crev_common::blake2b256sum(&host_full_id); path.join(if let Some(type_subname) = type_subname { - format!("{}-{}", date, type_subname) + format!( + "{}-{}-{}", + date, + type_subname, + crev_common::base64_encode(&host_plus_id_digest[..4]) + ) } else { date }) diff --git a/crev-lib/src/proofdb.rs b/crev-lib/src/proofdb.rs new file mode 100644 index 00000000..05c1a00e --- /dev/null +++ b/crev-lib/src/proofdb.rs @@ -0,0 +1,632 @@ +use crate::VerificationStatus; +use chrono::{self, offset::Utc, DateTime}; +use crev_data::{ + self, + proof::{ + self, + review::{self, Rating}, + trust::TrustLevel, + Content, ContentCommon, + }, + Digest, Id, Url, +}; +use default::default; +use std::collections::{hash_map, BTreeMap, BTreeSet, HashMap, HashSet}; + +/// A `T` with a timestamp +/// +/// This allows easily keeping track of a most recent version +/// of `T`. Typically `T` is a *proof* of some kind. +#[derive(Clone, Debug)] +pub struct Timestamped { + pub date: chrono::DateTime, + value: T, +} + +impl Timestamped { + fn update_to_more_recent(&mut self, date: &chrono::DateTime, value: T) { + if self.date < *date { + self.value = value; + } + } + + fn insert_into_or_update_to_more_recent(self, entry: hash_map::Entry>) { + match entry { + hash_map::Entry::Occupied(mut entry) => entry + .get_mut() + .update_to_more_recent(&self.date, self.value), + hash_map::Entry::Vacant(entry) => { + entry.insert(self); + } + } + } +} + +type TimestampedUrl = Timestamped; +type TimestampedTrustLevel = Timestamped; +type TimestampedReview = Timestamped; + +impl From for TimestampedTrustLevel { + fn from(trust: proof::Trust) -> Self { + TimestampedTrustLevel { + date: trust.date().with_timezone(&Utc), + value: trust.trust, + } + } +} + +impl<'a, T: review::Common> From<&'a T> for TimestampedReview { + fn from(review: &T) -> Self { + TimestampedReview { + value: review.review().to_owned(), + date: review.date().with_timezone(&Utc), + } + } +} + +/// Unique package review +/// +/// Since package review can be overwritten, it's useful +/// to refer to a review by an unique combination of +/// +/// * author's ID +/// * source +/// * crate +/// * version +#[derive(Hash, Debug, Clone, PartialEq, Eq)] +pub struct UniquePackageReview { + from: Id, + source: String, + name: String, + version: String, +} + +type TimestampedSignature = Timestamped; + +impl From for UniquePackageReview { + fn from(review: review::Package) -> Self { + Self { + from: review.from.id, + source: review.package.source, + name: review.package.name, + version: review.package.version, + } + } +} + +impl From<(&DateTime, String)> for TimestampedSignature +where + Tz: chrono::TimeZone, +{ + fn from(args: (&DateTime, String)) -> Self { + Self { + date: args.0.with_timezone(&Utc), + value: args.1, + } + } +} +/// In memory database tracking information from proofs +/// +/// After population, used for calculating the effcttive trust set, etc. +/// +/// Right now, for every invocation of crev, we just load it up with +/// all known proofs, and then query. If it ever becomes too slow, +/// all the logic here will have to be moved to a real embedded db +/// of some kind. +pub struct ProofDB { + trust_id_to_id: HashMap>, // who -(trusts)-> whom + url_by_id: HashMap, + url_by_id_secondary: HashMap, + + package_review_by_signature: HashMap, + + package_review_signatures_by_package_digest: + HashMap, HashMap>, + package_review_signatures_by_unique_package_review: + HashMap, + + package_reviews_by_source: BTreeMap>, + package_reviews_by_name: BTreeMap<(String, String), HashSet>, + package_reviews_by_version: BTreeMap<(String, String, String), HashSet>, +} + +impl Default for ProofDB { + fn default() -> Self { + ProofDB { + trust_id_to_id: default(), + url_by_id: default(), + url_by_id_secondary: default(), + package_review_signatures_by_package_digest: default(), + package_review_signatures_by_unique_package_review: default(), + package_review_by_signature: default(), + package_reviews_by_source: default(), + package_reviews_by_name: default(), + package_reviews_by_version: default(), + } + } +} + +impl ProofDB { + pub fn new() -> Self { + default() + } + + pub fn unique_package_review_proof_count(&self) -> usize { + self.package_review_signatures_by_unique_package_review + .len() + } + + pub fn unique_trust_proof_count(&self) -> usize { + self.trust_id_to_id + .iter() + .fold(0, |count, (_id, set)| count + set.len()) + } + + fn add_code_review(&mut self, review: &review::Code) { + let from = &review.from; + self.record_url_from_from_field(&review.date_utc(), &from); + for _file in &review.files { + // not implemented right now; just ignore + } + } + + fn add_package_review(&mut self, review: &review::Package, signature: &str) { + let from = &review.from; + self.record_url_from_from_field(&review.date_utc(), &from); + + self.package_review_by_signature + .entry(signature.to_owned()) + .or_insert_with(|| review.to_owned()); + + let unique = UniquePackageReview::from(review.clone()); + let timestamp_signature = TimestampedSignature::from((review.date(), signature.to_owned())); + + timestamp_signature + .clone() + .insert_into_or_update_to_more_recent( + self.package_review_signatures_by_package_digest + .entry(review.package.digest.to_owned()) + .or_insert_with(|| default()) + .entry(unique.clone()), + ); + + timestamp_signature.insert_into_or_update_to_more_recent( + self.package_review_signatures_by_unique_package_review + .entry(unique.clone()), + ); + + self.package_reviews_by_source + .entry(review.package.source.to_owned()) + .or_default() + .insert(unique.clone()); + self.package_reviews_by_name + .entry(( + review.package.source.to_owned(), + review.package.name.to_owned(), + )) + .or_default() + .insert(unique.clone()); + self.package_reviews_by_version + .entry(( + review.package.source.to_owned(), + review.package.name.to_owned(), + review.package.version.to_owned(), + )) + .or_default() + .insert(unique); + } + + pub fn get_package_review_count( + &self, + source: &str, + name: Option<&str>, + version: Option<&str>, + ) -> usize { + self.get_package_reviews_for_package(source, name, version) + .count() + } + + pub fn get_package_reviews_for_package( + &self, + source: &str, + name: Option<&str>, + version: Option<&str>, + ) -> impl Iterator { + let mut proofs: Vec<_> = match (name, version) { + (Some(name), Some(version)) => self.package_reviews_by_version.get(&( + source.to_owned(), + name.to_owned(), + version.to_owned(), + )), + + (Some(name), None) => self + .package_reviews_by_name + .get(&(source.to_owned(), name.to_owned())), + + (None, None) => self.package_reviews_by_source.get(source), + + (None, Some(_)) => panic!("Wrong usage"), + } + .into_iter() + .flat_map(|s| s) + .map(|unique_package_review| { + self.package_review_by_signature[&self + .package_review_signatures_by_unique_package_review[unique_package_review] + .value] + .clone() + }) + .collect(); + + proofs.sort_by(|a, b| a.date().cmp(&b.date())); + + proofs.into_iter() + } + + fn add_trust_raw(&mut self, from: &Id, to: &Id, date: DateTime, trust: TrustLevel) { + TimestampedTrustLevel { value: trust, date }.insert_into_or_update_to_more_recent( + self.trust_id_to_id + .entry(from.to_owned()) + .or_insert_with(HashMap::new) + .entry(to.to_owned()), + ); + } + + fn add_trust(&mut self, trust: &proof::Trust) { + let from = &trust.from; + self.record_url_from_from_field(&trust.date_utc(), &from); + for to in &trust.ids { + self.add_trust_raw(&from.id, &to.id, trust.date_utc(), trust.trust); + } + for to in &trust.ids { + self.record_url_from_to_field(&trust.date_utc(), &to) + } + } + + pub fn all_known_ids(&self) -> BTreeSet { + self.url_by_id + .keys() + .chain(self.url_by_id_secondary.keys()) + .cloned() + .collect() + } + + /// Get all Ids that authored a proof (with total count) + pub fn all_author_ids(&self) -> BTreeMap { + let mut res = BTreeMap::new(); + for (id, set) in &self.trust_id_to_id { + *res.entry(id.to_owned()).or_default() += set.len(); + } + + for uniq_rev in self + .package_review_signatures_by_unique_package_review + .keys() + { + *res.entry(uniq_rev.from.clone()).or_default() += 1; + } + + res + } + + pub fn get_package_reviews_by_digest<'a>( + &'a self, + digest: &Digest, + ) -> impl Iterator + 'a { + self.package_review_signatures_by_package_digest + .get(digest.as_slice()) + .into_iter() + .flat_map(move |unique_reviews| { + unique_reviews + .iter() + .map(move |(_unique_review, signature)| { + self.package_review_by_signature[&signature.value].clone() + }) + }) + } + + pub fn verify_package_digest( + &self, + digest: &Digest, + trust_set: &TrustSet, + ) -> VerificationStatus { + let reviews: HashMap = self + .get_package_reviews_by_digest(digest) + .map(|review| (review.from.id.clone(), review)) + .collect(); + // Faster somehow maybe? + let reviews_by: HashSet = reviews.keys().map(|s| s.to_owned()).collect(); + let trusted_ids: HashSet<_> = trust_set.trusted_ids().cloned().collect(); + let matching_reviewers = trusted_ids.intersection(&reviews_by); + let mut trust_count = 0; + let mut trust_level = TrustLevel::None; + let mut flagged_count = 0; + let mut dangerous_count = 0; + for matching_reviewer in matching_reviewers { + let rating = &reviews[matching_reviewer].review.rating; + if Rating::Neutral <= *rating { + trust_count += 1; + trust_level = std::cmp::max( + trust_level, + trust_set + .get_effective_trust_level(matching_reviewer) + .expect("Id should have been there"), + ); + } else if *rating <= Rating::Dangerous { + dangerous_count += 1; + } else if *rating < Rating::Neutral { + flagged_count += 1; + } + } + + if dangerous_count > 0 { + VerificationStatus::Dangerous + } else if flagged_count > 0 { + VerificationStatus::Flagged + } else if trust_count > 0 { + VerificationStatus::Verified(trust_level) + } else { + VerificationStatus::None + } + } + + fn record_url_from_to_field(&mut self, date: &DateTime, to: &crev_data::PubId) { + self.url_by_id_secondary + .entry(to.id.clone()) + .or_insert_with(|| TimestampedUrl { + value: to.url.clone(), + date: *date, + }); + } + + fn record_url_from_from_field(&mut self, date: &DateTime, from: &crev_data::PubId) { + TimestampedUrl { + value: from.url.clone(), + date: date.to_owned(), + } + .insert_into_or_update_to_more_recent(self.url_by_id.entry(from.id.clone())); + } + fn add_proof(&mut self, proof: &proof::Proof) { + proof + .verify() + .expect("All proofs were supposed to be valid here"); + match proof.content { + Content::Code(ref review) => self.add_code_review(&review), + Content::Package(ref review) => self.add_package_review(&review, &proof.signature), + Content::Trust(ref trust) => self.add_trust(&trust), + } + } + + pub fn import_from_iter(&mut self, i: impl Iterator) { + for proof in i { + self.add_proof(&proof); + } + } + + fn get_trust_list_of_id(&self, id: &Id) -> impl Iterator { + if let Some(map) = self.trust_id_to_id.get(id) { + Some(map.iter().map(|(id, trust)| (trust.value, id))) + } else { + None + } + .into_iter() + .flatten() + } + + pub fn calculate_trust_set(&self, for_id: &Id, params: &TrustDistanceParams) -> TrustSet { + let mut distrusted = HashMap::new(); + + // We keep retrying the whole thing, with more and more + // distrusted Ids + loop { + let prev_distrusted_len = distrusted.len(); + let trust_set = self.calculate_trust_set_internal(for_id, params, distrusted); + if trust_set.distrusted.len() <= prev_distrusted_len { + return trust_set; + } + distrusted = trust_set.distrusted; + } + } + + /// Calculate the effective trust levels for IDs inside a WoT. + /// + /// This is one of the most important functions in `crev-lib`. + fn calculate_trust_set_internal( + &self, + for_id: &Id, + params: &TrustDistanceParams, + distrusted: HashMap>, + ) -> TrustSet { + #[derive(PartialOrd, Ord, Eq, PartialEq, Clone, Debug)] + struct Visit { + distance: u64, + id: Id, + } + + let mut pending = BTreeSet::new(); + let mut visited = TrustSet::default(); + visited.distrusted = distrusted; + + pending.insert(Visit { + distance: 0, + id: for_id.clone(), + }); + visited.record_trusted_id(for_id.clone(), for_id.clone(), 0, TrustLevel::High); + + while let Some(current) = pending.iter().next().cloned() { + pending.remove(¤t); + + for (level, candidate_id) in self.get_trust_list_of_id(&¤t.id) { + if level == TrustLevel::Distrust { + visited + .distrusted + .entry(candidate_id.clone()) + .or_default() + .insert(current.id.clone()); + continue; + } + + let candidate_distance_from_current = + if let Some(v) = params.distance_by_level(level) { + v + } else { + continue; + }; + + if visited.distrusted.contains_key(candidate_id) { + continue; + } + let candidate_total_distance = current.distance + candidate_distance_from_current; + + if candidate_total_distance > params.max_distance { + continue; + } + + let candidate_effective_trust = std::cmp::min( + level, + visited + .get_effective_trust_level(¤t.id) + .expect("Id should have been inserted to `visited` beforehand"), + ); + + if candidate_effective_trust < TrustLevel::None { + // can this even happen? + debug_assert!(false); + continue; + } + + if visited.record_trusted_id( + candidate_id.clone(), + current.id.clone(), + candidate_total_distance, + candidate_effective_trust, + ) { + pending.insert(Visit { + distance: candidate_total_distance, + id: candidate_id.to_owned(), + }); + } + } + } + + visited + } + + pub fn lookup_url(&self, id: &Id) -> Option<&Url> { + self.url_by_id + .get(id) + .or_else(|| self.url_by_id_secondary.get(id)) + .map(|url| &url.value) + } +} + +/// Details of a one Id that is +struct TrustedIdDetails { + distance: u64, + // effective, global trust from the root of the WoT + effective_trust: TrustLevel, + referers: HashMap, +} + +#[derive(Default)] +pub struct TrustSet { + trusted: HashMap, + distrusted: HashMap>, +} + +impl TrustSet { + pub fn trusted_ids(&self) -> impl Iterator { + self.trusted.keys() + } + + /// Record that an Id is considered trusted + /// + /// Returns `true` if this actually added or changed the `subject` details, + /// which requires revising it's own downstream trusted Id details in the graph algorithm for it. + fn record_trusted_id( + &mut self, + subject: Id, + referer: Id, + distance: u64, + effective_trust: TrustLevel, + ) -> bool { + // TODO: turn into log or something + // eprintln!( + // "{} -> {} {} ({})", + // referer, subject, distance, effective_trust + // ); + use std::collections::hash_map::Entry; + + match self.trusted.entry(subject) { + Entry::Vacant(entry) => { + let mut referers = HashMap::default(); + referers.insert(referer, effective_trust); + entry.insert(TrustedIdDetails { + distance, + effective_trust, + referers, + }); + true + } + Entry::Occupied(mut entry) => { + let mut changed = false; + let details = entry.get_mut(); + if details.distance > distance { + details.distance = distance; + changed = true; + } + if details.effective_trust < effective_trust { + details.effective_trust = effective_trust; + changed = true; + } + match details.referers.entry(referer.clone()) { + Entry::Vacant(entry) => { + entry.insert(effective_trust); + changed = true; + } + Entry::Occupied(mut entry) => { + let level = entry.get_mut(); + if *level < effective_trust { + *level = effective_trust; + changed = true; + } + } + } + changed + } + } + } + + pub fn get_effective_trust_level(&self, id: &Id) -> Option { + self.trusted.get(id).map(|details| details.effective_trust) + } +} + +pub struct TrustDistanceParams { + pub max_distance: u64, + pub high_trust_distance: u64, + pub medium_trust_distance: u64, + pub low_trust_distance: u64, +} + +impl TrustDistanceParams { + fn distance_by_level(&self, level: TrustLevel) -> Option { + use crev_data::proof::trust::TrustLevel::*; + Some(match level { + Distrust => return Option::None, + None => return Option::None, + Low => self.low_trust_distance, + Medium => self.medium_trust_distance, + High => self.high_trust_distance, + }) + } +} + +impl Default for TrustDistanceParams { + fn default() -> Self { + Self { + max_distance: 10, + high_trust_distance: 0, + medium_trust_distance: 1, + low_trust_distance: 5, + } + } +} diff --git a/crev-lib/src/repo/mod.rs b/crev-lib/src/repo/mod.rs index e6a463c9..bbbd2f12 100644 --- a/crev-lib/src/repo/mod.rs +++ b/crev-lib/src/repo/mod.rs @@ -1,7 +1,6 @@ -use crate::ProofStore; -use crate::{local::Local, util, Result}; -use crev_data::proof; -use crev_data::Digest; +use crate::{id::PassphraseFn, local::Local, prelude::*, proofdb::TrustSet, util, ProofStore}; +use crev_common::convert::OptionDeref; +use crev_data::{proof, Digest}; use git2; use serde_yaml; use std::{ @@ -149,20 +148,32 @@ impl Repo { } pub fn get_proof_rel_store_path(&self, proof: &proof::Proof) -> PathBuf { - PathBuf::from("proofs").join(crate::proof::rel_package_path(&proof.content)) + // TODO: What about the merge conflicts, etc? https://github.com/dpc/crev/issues/153 + PathBuf::from("proofs").join(crate::proof::rel_package_path(&proof.content, &[])); + unimplemented!(); } - pub fn package_verify(&mut self, allow_dirty: bool) -> Result { + pub fn package_verify( + &mut self, + local: &Local, + allow_dirty: bool, + for_id: Option, + params: &crate::TrustDistanceParams, + ) -> Result { if !allow_dirty && self.is_unclean()? { bail!("Git repository is not in a clean state"); } - let local = Local::auto_open()?; - let params = Default::default(); - let (db, trusted_set) = local.load_db(¶ms)?; + let db = local.load_db()?; + + let trust_set = if let Some(id) = local.get_for_id_from_str_opt(for_id.as_deref())? { + db.calculate_trust_set(&id, ¶ms) + } else { + TrustSet::default() + }; let ignore_list = HashSet::new(); let digest = crate::get_recursive_digest_for_git_dir(&self.root_dir, &ignore_list)?; - Ok(db.verify_digest(&digest, &trusted_set)) + Ok(db.verify_package_digest(&digest, &trust_set)) } pub fn package_digest(&mut self, allow_dirty: bool) -> Result { @@ -221,7 +232,11 @@ impl Repo { bail!("Couldn't identify revision info"); } - pub fn trust_package(&mut self, passphrase: &str, allow_dirty: bool) -> Result<()> { + pub fn trust_package( + &mut self, + passphrase_callback: PassphraseFn, + allow_dirty: bool, + ) -> Result<()> { if !self.staging()?.is_empty() { bail!("Can't review with uncommitted staged files."); } @@ -235,7 +250,7 @@ impl Repo { let ignore_list = HashSet::new(); let _digest = crate::get_recursive_digest_for_git_dir(&self.root_dir, &ignore_list)?; - let id = local.read_current_unlocked_id(&passphrase)?; + let id = local.read_current_unlocked_id(passphrase_callback)?; let review = proof::review::PackageBuilder::default() .from(id.id.to_owned()) @@ -250,7 +265,7 @@ impl Repo { Ok(()) } - pub fn commit(&mut self, passphrase: &str, allow_dirty: bool) -> Result<()> { + pub fn commit(&mut self, passphrase_callback: PassphraseFn, allow_dirty: bool) -> Result<()> { if self.staging()?.is_empty() && !allow_dirty { bail!("No reviews to commit. Use `add` first or use `-a` for the whole package."); } @@ -259,7 +274,7 @@ impl Repo { let _revision = self.read_revision()?; self.staging()?.enforce_current()?; let files = self.staging()?.to_review_files(); - let id = local.read_current_unlocked_id(&passphrase)?; + let id = local.read_current_unlocked_id(passphrase_callback)?; let review = proof::review::CodeBuilder::default() .from(id.id.to_owned()) diff --git a/crev-lib/src/tests.rs b/crev-lib/src/tests.rs index 548eba77..9bd26799 100644 --- a/crev-lib/src/tests.rs +++ b/crev-lib/src/tests.rs @@ -1,9 +1,14 @@ use super::*; -use crate::trustdb::{self, TrustDB}; -use crev_data::proof::trust::TrustLevel; -use crev_data::OwnId; - +use crev_data::{proof::trust::TrustLevel, Digest, OwnId}; +use default::default; + +// Basic liftime of an `LockedId`: +// +// * generate +// * lock with a passphrase +// * unlock +// * compare #[test] fn lock_and_unlock() -> Result<()> { let id = OwnId::generate_for_git_url("https://example.com/crev-proofs"); @@ -25,15 +30,17 @@ fn lock_and_unlock() -> Result<()> { Ok(()) } +// Exact distance of flooding the web of trust graph is configurable, +// with the edges distance corresponding to the trust level. #[test] -fn trustdb_distance() -> Result<()> { +fn proofdb_distance() -> Result<()> { let a = OwnId::generate_for_git_url("https://a"); let b = OwnId::generate_for_git_url("https://b"); let c = OwnId::generate_for_git_url("https://c"); let d = OwnId::generate_for_git_url("https://d"); let e = OwnId::generate_for_git_url("https://e"); - let distance_params = trustdb::TrustDistanceParams { + let distance_params = TrustDistanceParams { high_trust_distance: 1, medium_trust_distance: 10, low_trust_distance: 100, @@ -41,23 +48,31 @@ fn trustdb_distance() -> Result<()> { }; let a_to_b = a + .as_pubid() .create_trust_proof(vec![b.as_pubid().to_owned()], TrustLevel::High)? .sign_by(&a)?; let b_to_c = b + .as_pubid() .create_trust_proof(vec![c.as_pubid().to_owned()], TrustLevel::Medium)? .sign_by(&b)?; let c_to_d = c + .as_pubid() .create_trust_proof(vec![d.as_pubid().to_owned()], TrustLevel::Low)? .sign_by(&c)?; let d_to_e = d + .as_pubid() .create_trust_proof(vec![e.as_pubid().to_owned()], TrustLevel::High)? .sign_by(&d)?; - let mut trustdb = TrustDB::new(); + let mut trustdb = ProofDB::new(); trustdb.import_from_iter(vec![a_to_b, b_to_c, c_to_d, d_to_e].into_iter()); - let trust_set = trustdb.calculate_trust_set(a.as_ref(), &distance_params); + let trust_set: HashSet = trustdb + .calculate_trust_set(a.as_ref(), &distance_params) + .trusted_ids() + .cloned() + .collect(); assert!(trust_set.contains(a.as_ref())); assert!(trust_set.contains(b.as_ref())); @@ -66,11 +81,17 @@ fn trustdb_distance() -> Result<()> { assert!(!trust_set.contains(e.as_ref())); let b_to_d = b + .as_pubid() .create_trust_proof(vec![d.as_pubid().to_owned()], TrustLevel::Medium)? .sign_by(&b)?; trustdb.import_from_iter(vec![b_to_d].into_iter()); - let trust_set = trustdb.calculate_trust_set(a.as_ref(), &distance_params); + + let trust_set: HashSet<_> = trustdb + .calculate_trust_set(a.as_ref(), &distance_params) + .trusted_ids() + .cloned() + .collect(); assert!(trust_set.contains(a.as_ref())); assert!(trust_set.contains(b.as_ref())); @@ -79,3 +100,148 @@ fn trustdb_distance() -> Result<()> { assert!(trust_set.contains(e.as_ref())); Ok(()) } + +// A subsequent review of exactly same package version +// is supposed to overwrite the previous one, and it +// should be visible in all the user-facing stats, listings +// and counts. +#[test] +fn overwritting_reviews() -> Result<()> { + let a = OwnId::generate_for_git_url("https://a"); + let digest = vec![0; 32]; + let package = crev_data::proof::PackageInfo { + id: None, + source: "source".into(), + name: "name".into(), + version: "version".into(), + digest: digest.clone(), + digest_type: crev_data::proof::default_digest_type(), + revision: "".into(), + revision_type: crev_data::proof::default_revision_type(), + }; + + let proof1 = a + .as_pubid() + .create_package_review_proof(package.clone(), default(), "a".into())? + .sign_by(&a)?; + // it's lame, but oh well... ; we need to make sure there's a time delay between + // the two proofs + #[allow(deprecated)] + std::thread::sleep_ms(1); + let proof2 = a + .as_pubid() + .create_package_review_proof(package.clone(), default(), "b".into())? + .sign_by(&a)?; + + for order in vec![ + vec![proof1.clone(), proof2.clone()], + vec![proof2.clone(), proof1.clone()], + ] { + let mut trustdb = ProofDB::new(); + trustdb.import_from_iter(order.into_iter()); + assert_eq!( + trustdb + .get_package_reviews_by_digest(&Digest::from_vec(digest.clone())) + .map(|r| r.comment) + .collect::>(), + vec!["b".to_string()] + ); + assert_eq!( + trustdb + .get_package_reviews_for_package( + &package.source, + Some(&package.name), + Some(&package.version) + ) + .count(), + 1 + ); + assert_eq!( + trustdb + .get_package_reviews_for_package(&package.source, Some(&package.name), None) + .count(), + 1 + ); + assert_eq!( + trustdb + .get_package_reviews_for_package(&package.source, None, None) + .count(), + 1 + ); + } + + Ok(()) +} + +#[test] +fn proofdb_distrust() -> Result<()> { + let a = OwnId::generate_for_git_url("https://a"); + let b = OwnId::generate_for_git_url("https://b"); + let c = OwnId::generate_for_git_url("https://c"); + let d = OwnId::generate_for_git_url("https://d"); + let e = OwnId::generate_for_git_url("https://e"); + + let distance_params = TrustDistanceParams { + high_trust_distance: 1, + medium_trust_distance: 10, + low_trust_distance: 100, + max_distance: 10000, + }; + + let a_to_bc = a + .as_pubid() + .create_trust_proof( + vec![b.as_pubid().to_owned(), c.as_pubid().to_owned()], + TrustLevel::High, + )? + .sign_by(&a)?; + let b_to_d = b + .as_pubid() + .create_trust_proof(vec![d.as_pubid().to_owned()], TrustLevel::Low)? + .sign_by(&b)?; + let d_to_c = d + .as_pubid() + .create_trust_proof(vec![c.as_pubid().to_owned()], TrustLevel::Distrust)? + .sign_by(&d)?; + let c_to_e = c + .as_pubid() + .create_trust_proof(vec![e.as_pubid().to_owned()], TrustLevel::High)? + .sign_by(&c)?; + + let mut trustdb = ProofDB::new(); + + trustdb.import_from_iter(vec![a_to_bc, b_to_d, d_to_c, c_to_e].into_iter()); + + let trust_set: HashSet<_> = trustdb + .calculate_trust_set(a.as_ref(), &distance_params) + .trusted_ids() + .cloned() + .collect(); + + assert!(trust_set.contains(a.as_ref())); + assert!(trust_set.contains(b.as_ref())); + assert!(!trust_set.contains(c.as_ref())); + assert!(trust_set.contains(d.as_ref())); + assert!(!trust_set.contains(e.as_ref())); + + let e_to_d = e + .as_pubid() + .create_trust_proof(vec![d.as_pubid().to_owned()], TrustLevel::Distrust)? + .sign_by(&e)?; + + trustdb.import_from_iter(vec![e_to_d].into_iter()); + + let trust_set: HashSet<_> = trustdb + .calculate_trust_set(a.as_ref(), &distance_params) + .trusted_ids() + .cloned() + .collect(); + + assert!(trust_set.contains(a.as_ref())); + assert!(trust_set.contains(b.as_ref())); + assert!(!trust_set.contains(c.as_ref())); + assert!(!trust_set.contains(d.as_ref())); + assert!(!trust_set.contains(e.as_ref())); + + Ok(()) +} diff --git a/crev-lib/src/trustdb.rs b/crev-lib/src/trustdb.rs deleted file mode 100644 index e648f7f4..00000000 --- a/crev-lib/src/trustdb.rs +++ /dev/null @@ -1,417 +0,0 @@ -use crate::VerificationStatus; -use chrono::{self, offset::Utc, DateTime}; -use crev_data::{ - self, - proof::review::Rating, - proof::trust::TrustLevel, - proof::{self, review, Content, ContentCommon}, - Digest, Id, Url, -}; -use default::default; -use std::collections::BTreeMap; -use std::collections::{hash_map, BTreeSet, HashMap, HashSet}; - -pub struct Timestamped { - pub date: chrono::DateTime, - value: T, -} - -impl Timestamped { - fn update_to_more_recent(&mut self, date: &chrono::DateTime, value: T) { - if self.date < *date { - self.value = value; - } - } - - fn insert_into_or_update_to_more_recent(self, entry: hash_map::Entry>) { - match entry { - hash_map::Entry::Occupied(mut entry) => entry - .get_mut() - .update_to_more_recent(&self.date, self.value), - hash_map::Entry::Vacant(entry) => { - entry.insert(self); - } - } - } -} - -type TimestampedUrl = Timestamped; -type TimestampedTrustLevel = Timestamped; -type TimestampedReview = Timestamped; - -impl From for TimestampedTrustLevel { - fn from(trust: proof::Trust) -> Self { - TimestampedTrustLevel { - date: trust.date().with_timezone(&Utc), - value: trust.trust, - } - } -} - -impl<'a, T: review::Common> From<&'a T> for TimestampedReview { - fn from(review: &T) -> Self { - TimestampedReview { - value: review.review().to_owned(), - date: review.date().with_timezone(&Utc), - } - } -} - -/// In memory database tracking information from proofs -/// -/// After population, used for calculating the effcttive trust set, etc. -pub struct TrustDB { - trust_id_to_id: HashMap>, // who -(trusts)-> whom - digest_to_reviews: HashMap, HashMap>, // what (digest) -(reviewed)-> by whom - url_by_id: HashMap, - url_by_id_secondary: HashMap, - - package_review_by_signature: HashMap, - package_reviews_by_source: BTreeMap>, - package_reviews_by_name: BTreeMap<(String, String), BTreeSet>, - package_reviews_by_version: BTreeMap<(String, String, String), BTreeSet>, -} - -impl Default for TrustDB { - fn default() -> Self { - Self { - trust_id_to_id: Default::default(), - url_by_id: Default::default(), - url_by_id_secondary: Default::default(), - digest_to_reviews: Default::default(), - package_review_by_signature: default(), - package_reviews_by_source: default(), - package_reviews_by_name: default(), - package_reviews_by_version: default(), - } - } -} - -impl TrustDB { - pub fn new() -> Self { - default() - } - - fn add_code_review(&mut self, review: &review::Code) { - let from = &review.from; - self.record_url_from_from_field(&review.date_utc(), &from); - for file in &review.files { - TimestampedReview::from(review).insert_into_or_update_to_more_recent( - self.digest_to_reviews - .entry(file.digest.to_owned()) - .or_insert_with(HashMap::new) - .entry(from.id.clone()), - ) - } - } - - fn add_package_review(&mut self, review: &review::Package, signature: &str) { - let from = &review.from; - self.record_url_from_from_field(&review.date_utc(), &from); - - TimestampedReview::from(review).insert_into_or_update_to_more_recent( - self.digest_to_reviews - .entry(review.package.digest.to_owned()) - .or_insert_with(HashMap::new) - .entry(from.id.clone()), - ); - - self.package_review_by_signature - .entry(signature.to_owned()) - .or_insert_with(|| review.to_owned()); - - self.package_reviews_by_source - .entry(review.package.source.to_owned()) - .or_default() - .insert(signature.to_owned()); - self.package_reviews_by_name - .entry(( - review.package.source.to_owned(), - review.package.name.to_owned(), - )) - .or_default() - .insert(signature.to_owned()); - self.package_reviews_by_version - .entry(( - review.package.source.to_owned(), - review.package.name.to_owned(), - review.package.version.to_owned(), - )) - .or_default() - .insert(signature.to_owned()); - } - - pub fn get_package_review_count( - &self, - source: &str, - name: Option<&str>, - version: Option<&str>, - ) -> usize { - match (name, version) { - (Some(name), Some(version)) => self - .package_reviews_by_version - .get(&(source.to_owned(), name.to_owned(), version.to_owned())) - .map(|set| set.len()) - .unwrap_or(0), - (Some(name), None) => self - .package_reviews_by_name - .get(&(source.to_owned(), name.to_owned())) - .map(|set| set.len()) - .unwrap_or(0), - (None, None) => self - .package_reviews_by_source - .get(source) - .map(|set| set.len()) - .unwrap_or(0), - (None, Some(_)) => panic!("Wrong usage"), - } - } - - pub fn get_package_reviews_for_package( - &self, - source: &str, - name: Option<&str>, - version: Option<&str>, - ) -> impl Iterator { - let mut proofs: Vec<_> = match (name, version) { - (Some(name), Some(version)) => self - .package_reviews_by_version - .get(&(source.to_owned(), name.to_owned(), version.to_owned())) - .map(|set| { - set.iter() - .map(|signature| self.package_review_by_signature[signature].clone()) - .collect() - }) - .unwrap_or_else(|| vec![]), - - (Some(name), None) => self - .package_reviews_by_name - .get(&(source.to_owned(), name.to_owned())) - .map(|set| { - set.iter() - .map(|signature| self.package_review_by_signature[signature].clone()) - .collect() - }) - .unwrap_or_else(|| vec![]), - (None, None) => self - .package_reviews_by_source - .get(source) - .map(|set| { - set.iter() - .map(|signature| self.package_review_by_signature[signature].clone()) - .collect() - }) - .unwrap_or_else(|| vec![]), - (None, Some(_)) => panic!("Wrong usage"), - }; - - proofs.sort_by(|a, b| a.date().cmp(&b.date())); - - proofs.into_iter() - } - - fn add_trust_raw(&mut self, from: &Id, to: &Id, date: DateTime, trust: TrustLevel) { - TimestampedTrustLevel { value: trust, date }.insert_into_or_update_to_more_recent( - self.trust_id_to_id - .entry(from.to_owned()) - .or_insert_with(HashMap::new) - .entry(to.to_owned()), - ); - } - - fn add_trust(&mut self, trust: &proof::Trust) { - let from = &trust.from; - self.record_url_from_from_field(&trust.date_utc(), &from); - for to in &trust.ids { - self.add_trust_raw(&from.id, &to.id, trust.date_utc(), trust.trust); - } - for to in &trust.ids { - self.record_url_from_to_field(&trust.date_utc(), &to) - } - } - - pub fn all_known_ids(&self) -> BTreeSet { - self.url_by_id - .keys() - .chain(self.url_by_id_secondary.keys()) - .cloned() - .collect() - } - - fn get_reviews_of(&self, digest: &Digest) -> Option<&HashMap> { - self.digest_to_reviews.get(digest.as_slice()) - } - - pub fn verify_digest( - &self, - digest: &Digest, - trust_set: &HashSet, - ) -> VerificationStatus - where - H: std::hash::BuildHasher + std::default::Default, - { - if let Some(reviews) = self.get_reviews_of(digest) { - // Faster somehow maybe? - let reviews_by: HashSet = reviews.keys().map(|s| s.to_owned()).collect(); - let matching_reviewers = trust_set.intersection(&reviews_by); - let mut trust_count = 0; - let mut distrust_count = 0; - for matching_reviewer in matching_reviewers { - if Rating::Neutral <= reviews[matching_reviewer].value.rating { - trust_count += 1; - } - if reviews[matching_reviewer].value.rating < Rating::Neutral { - distrust_count += 1; - } - } - - if distrust_count > 0 { - VerificationStatus::Flagged - } else if trust_count > 0 { - VerificationStatus::Verified - } else { - VerificationStatus::Unknown - } - } else { - VerificationStatus::Unknown - } - } - - fn record_url_from_to_field(&mut self, date: &DateTime, to: &crev_data::PubId) { - self.url_by_id_secondary - .entry(to.id.clone()) - .or_insert_with(|| TimestampedUrl { - value: to.url.clone(), - date: *date, - }); - } - - fn record_url_from_from_field(&mut self, date: &DateTime, from: &crev_data::PubId) { - TimestampedUrl { - value: from.url.clone(), - date: date.to_owned(), - } - .insert_into_or_update_to_more_recent(self.url_by_id.entry(from.id.clone())); - } - fn add_proof(&mut self, proof: &proof::Proof) { - proof - .verify() - .expect("All proofs were supposed to be valid here"); - match proof.content { - Content::Code(ref review) => self.add_code_review(&review), - Content::Package(ref review) => self.add_package_review(&review, &proof.signature), - Content::Trust(ref trust) => self.add_trust(&trust), - } - } - - pub fn import_from_iter(&mut self, i: impl Iterator) { - for proof in i { - self.add_proof(&proof); - } - } - - fn get_ids_trusted_by(&self, id: &Id) -> impl Iterator { - if let Some(map) = self.trust_id_to_id.get(id) { - Some(map.iter().map(|(id, trust)| (trust.value, id))) - } else { - None - } - .into_iter() - .flatten() - } - - // Oh god, please someone verify this :D - pub fn calculate_trust_set(&self, for_id: &Id, params: &TrustDistanceParams) -> HashSet { - #[derive(PartialOrd, Ord, Eq, PartialEq, Clone, Debug)] - struct Visit { - distance: u64, - id: Id, - } - let mut pending = BTreeSet::new(); - pending.insert(Visit { - distance: 0, - id: for_id.clone(), - }); - - let mut visited = HashMap::<&Id, _>::new(); - visited.insert(&for_id, 0); - while let Some(current) = pending.iter().next().cloned() { - pending.remove(¤t); - - if let Some(visited_distance) = visited.get(¤t.id) { - if *visited_distance < current.distance { - continue; - } - } - - for (level, candidate_id) in self.get_ids_trusted_by(&¤t.id) { - let candidate_distance_from_current = - if let Some(v) = params.distance_by_level(level) { - v - } else { - continue; - }; - let candidate_total_distance = current.distance + candidate_distance_from_current; - if candidate_total_distance > params.max_distance { - continue; - } - - if let Some(prev_candidate_distance) = visited.get(candidate_id).cloned() { - if prev_candidate_distance > candidate_total_distance { - visited.insert(candidate_id, candidate_total_distance); - pending.insert(Visit { - distance: candidate_total_distance, - id: candidate_id.to_owned(), - }); - } - } else { - visited.insert(candidate_id, candidate_total_distance); - pending.insert(Visit { - distance: candidate_total_distance, - id: candidate_id.to_owned(), - }); - } - } - } - - visited.keys().map(|id| (*id).clone()).collect() - } - - pub fn lookup_url(&self, id: &Id) -> Option<&Url> { - self.url_by_id - .get(id) - .or_else(|| self.url_by_id_secondary.get(id)) - .map(|url| &url.value) - } -} - -pub struct TrustDistanceParams { - pub max_distance: u64, - pub high_trust_distance: u64, - pub medium_trust_distance: u64, - pub low_trust_distance: u64, -} - -impl TrustDistanceParams { - fn distance_by_level(&self, level: TrustLevel) -> Option { - use crev_data::proof::trust::TrustLevel::*; - Some(match level { - Distrust => return Option::None, - None => return Option::None, - Low => self.low_trust_distance, - Medium => self.medium_trust_distance, - High => self.high_trust_distance, - }) - } -} - -impl Default for TrustDistanceParams { - fn default() -> Self { - Self { - max_distance: 10, - high_trust_distance: 0, - medium_trust_distance: 1, - low_trust_distance: 5, - } - } -} diff --git a/crev-lib/src/util/git.rs b/crev-lib/src/util/git.rs new file mode 100644 index 00000000..da242095 --- /dev/null +++ b/crev-lib/src/util/git.rs @@ -0,0 +1,112 @@ +use crate::prelude::*; + +#[derive(PartialEq, Debug, Default)] +pub struct GitUrlComponents { + pub domain: String, + pub username: String, + pub repo: String, + pub suffix: String, +} + +pub fn parse_git_url_https(http_url: &str) -> Option { + let mut split: Vec<_> = http_url.split('/').collect(); + + while let Some(&"") = split.last() { + split.pop(); + } + if split.len() != 5 { + return None; + } + if split[0] != "https:" && split[0] != "http:" { + return None; + } + let domain = split[2]; + let username = split[3]; + let repo = split[4]; + let suffix = if repo.ends_with(".git") { "" } else { ".git" }; + + Some(GitUrlComponents { + domain: domain.to_string(), + username: username.to_string(), + repo: repo.to_string(), + suffix: suffix.to_string(), + }) +} + +pub fn fetch_and_checkout_git_repo(repo: &git2::Repository) -> Result<()> { + repo.find_remote("origin")?.fetch(&["master"], None, None)?; + repo.set_head("FETCH_HEAD")?; + let mut opts = git2::build::CheckoutBuilder::new(); + opts.force(); + repo.checkout_head(Some(&mut opts))?; + Ok(()) +} + +#[test] +fn parse_git_url_https_test() { + assert_eq!( + parse_git_url_https("https://github.com/dpc/trust"), + Some(GitUrlComponents { + domain: "github.com".to_string(), + username: "dpc".to_string(), + repo: "trust".to_string(), + suffix: ".git".to_string() + }) + ); + assert_eq!( + parse_git_url_https("https://gitlab.com/hackeraudit/web.git"), + Some(GitUrlComponents { + domain: "gitlab.com".to_string(), + username: "hackeraudit".to_string(), + repo: "web.git".to_string(), + suffix: "".to_string() + }) + ); + assert_eq!( + parse_git_url_https("https://gitlab.com/hackeraudit/web.git/"), + Some(GitUrlComponents { + domain: "gitlab.com".to_string(), + username: "hackeraudit".to_string(), + repo: "web.git".to_string(), + suffix: "".to_string() + }) + ); + assert_eq!( + parse_git_url_https("https://gitlab.com/hackeraudit/web.git/////////"), + Some(GitUrlComponents { + domain: "gitlab.com".to_string(), + username: "hackeraudit".to_string(), + repo: "web.git".to_string(), + suffix: "".to_string() + }) + ); +} + +pub fn https_to_git_url(http_url: &str) -> Option { + parse_git_url_https(http_url).map(|components| { + format!( + "git@{}:{}/{}{}", + components.domain, components.username, components.repo, components.suffix + ) + }) +} + +#[test] +fn https_to_git_url_test() { + assert_eq!( + https_to_git_url("https://github.com/dpc/trust"), + Some("git@github.com:dpc/trust.git".into()) + ); + assert_eq!( + https_to_git_url("https://gitlab.com/hackeraudit/web.git"), + Some("git@gitlab.com:hackeraudit/web.git".into()) + ); + assert_eq!( + https_to_git_url("https://gitlab.com/hackeraudit/web.git/"), + Some("git@gitlab.com:hackeraudit/web.git".into()) + ); + assert_eq!( + https_to_git_url("https://gitlab.com/hackeraudit/web.git/////////"), + Some("git@gitlab.com:hackeraudit/web.git".into()) + ); +} diff --git a/crev-lib/src/util/mod.rs b/crev-lib/src/util/mod.rs index 7e6d37a0..ed81271e 100644 --- a/crev-lib/src/util/mod.rs +++ b/crev-lib/src/util/mod.rs @@ -1,29 +1,32 @@ -use crate::Result; -use app_dirs; +pub mod git; + +use crate::prelude::*; use crev_common; use crev_data::proof; -use std::fmt::Write as FmtWrite; -use std::{self, env, ffi, fs, io::Write, path::Path, process}; +use git2; +use std::{self, env, ffi, fmt::Write as FmtWrite, fs, io::Write, path::Path, process}; use tempdir; pub use crev_common::{read_file_to_string, store_str_to_file, store_to_file_with}; -pub const APP_INFO: app_dirs::AppInfo = app_dirs::AppInfo { - name: "crev", - author: "Dawid Ciężarkiewicz", -}; -fn get_editor_to_use() -> ffi::OsString { - if let Some(v) = env::var_os("VISUAL") { - return v; +fn get_git_default_editor() -> Result { + let cfg = git2::Config::open_default()?; + Ok(cfg.get_string("core.editor")?) +} + +fn get_editor_to_use() -> Result { + Ok(if let Some(v) = env::var_os("VISUAL") { + v } else if let Some(v) = env::var_os("EDITOR") { - return v; + v + } else if let Ok(v) = get_git_default_editor() { + v.into() } else { - return "vi".into(); - } + "vi".into() + }) } fn edit_text_iteractively(text: &str) -> Result { - let editor = get_editor_to_use(); let dir = tempdir::TempDir::new("crev")?; let file_path = dir.path().join("crev.review"); let mut file = fs::File::create(&file_path)?; @@ -31,18 +34,34 @@ fn edit_text_iteractively(text: &str) -> Result { file.flush()?; drop(file); - let status = process::Command::new(editor).arg(&file_path).status()?; - - if !status.success() { - bail!("Editor returned {}", status); - } + edit_file(&file_path)?; Ok(read_file_to_string(&file_path)?) } pub fn edit_file(path: &Path) -> Result<()> { - let editor = get_editor_to_use(); - let status = process::Command::new(editor).arg(&path).status()?; + let editor = get_editor_to_use()?; + + let status = if cfg!(windows) { + let mut proc = process::Command::new(editor.clone()); + proc.arg(path); + proc + } else if cfg!(unix) { + let mut proc = process::Command::new("/bin/sh"); + proc.arg("-c").arg(format!( + "{} {}", + editor + .clone() + .into_string() + .map_err(|_| format_err!("$EDITOR or $VISUAL not a valid Unicode"))?, + shell_escape::escape(path.display().to_string().into()) + )); + proc + } else { + panic!("What platform are you running this on? Please submit a PR!"); + } + .status() + .with_context(|_e| format_err!("Couldn't start the editor: {}", editor.to_string_lossy()))?; if !status.success() { bail!("Editor returned {}", status); @@ -82,12 +101,12 @@ pub fn edit_proof_content_iteractively(content: &proof::Content) -> Result(res: std::result::Result) -> bool { +pub fn err_eprint_and_ignore(res: std::result::Result) -> Option { match res { Err(e) => { eprintln!("{}", e); - false + None } - Ok(_) => true, + Ok(o) => Some(o), } } diff --git a/crevsum/Cargo.toml b/crevsum/Cargo.toml index ccbd37d2..1a8a51b9 100644 --- a/crevsum/Cargo.toml +++ b/crevsum/Cargo.toml @@ -21,4 +21,4 @@ failure = "0.1" [dependencies.crev-common] path = "../crev-common" -version = "0.3" +version = "0.4" diff --git a/crevsum/rustfmt.toml b/crevsum/rustfmt.toml index 7d2cf549..807b07b8 100644 --- a/crevsum/rustfmt.toml +++ b/crevsum/rustfmt.toml @@ -1 +1,2 @@ +edition = "2018" merge_imports = true diff --git a/rblake2sum/rustfmt.toml b/rblake2sum/rustfmt.toml index 7d2cf549..807b07b8 100644 --- a/rblake2sum/rustfmt.toml +++ b/rblake2sum/rustfmt.toml @@ -1 +1,2 @@ +edition = "2018" merge_imports = true diff --git a/recursive-digest/rustfmt.toml b/recursive-digest/rustfmt.toml new file mode 100644 index 00000000..807b07b8 --- /dev/null +++ b/recursive-digest/rustfmt.toml @@ -0,0 +1,2 @@ +edition = "2018" +merge_imports = true diff --git a/recursive-digest/src/lib.rs b/recursive-digest/src/lib.rs index f30c8371..726df003 100644 --- a/recursive-digest/src/lib.rs +++ b/recursive-digest/src/lib.rs @@ -1,10 +1,10 @@ use blake2; use failure_derive::Fail; -use std::io::BufRead; use std::{ collections::{BTreeMap, HashSet}, ffi::OsString, fs, + io::BufRead, path::{Component, Path, PathBuf}, }; diff --git a/recursive-digest/tests/sanity.rs b/recursive-digest/tests/sanity.rs index b3321390..8f82cf2d 100644 --- a/recursive-digest/tests/sanity.rs +++ b/recursive-digest/tests/sanity.rs @@ -1,9 +1,11 @@ use crev_recursive_digest::DigestError; use digest::Digest; -use std::collections::HashSet; -use std::fs; -use std::io::Write; -use std::path::{Path, PathBuf}; +use std::{ + collections::HashSet, + fs, + io::Write, + path::{Path, PathBuf}, +}; use tempdir::TempDir; #[test] diff --git a/rustfmt.toml b/rustfmt.toml index 7d2cf549..807b07b8 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1 +1,2 @@ +edition = "2018" merge_imports = true