diff options
author | Nick Gerace <nickagerace@gmail.com> | 2022-03-30 01:06:52 -0400 |
---|---|---|
committer | Nick Gerace <nickagerace@gmail.com> | 2022-03-30 01:36:38 -0400 |
commit | fde1cd0c5f32e407cddbf677fb86d78a4e028d28 (patch) | |
tree | 1d22cf74e60598022ccd03047e6acdcfa78c237c | |
parent | 52ccb1444e7aa675430ce4016685e0a60054a1d9 (diff) | |
download | gfold-fde1cd0c5f32e407cddbf677fb86d78a4e028d28.zip |
Enable multi-platform development
TLDR: remove barriers to multi-platform development, like Bash and Make.
Moreover, remove the nightly fmt dependency and improve testing to catch
potential corner cases on multiple platforms.
Core:
- Rename "Reports" to "LabeledReports" since "Reports" is not of implied
type Vec<Report>
Testing:
- Add second half to the integration test: comparing found reports with
expected reports
- Essentially, we run the core gfold loop _again_, but only generate
reports and skip displaying them to stdout
CI:
- Removed nightly fmt check and job
- Moved fmt check to stable
- Update bors toml to match changes
Scripts:
- Convert scripts from Bash scripts to independent crates
(multi-platform friendly)
- Add README to scripts directory
- Ensure script crates do not have their Cargo lockfile tracked in Git
Docs:
- Adding DEVELOPING file to replace Makefile
- Move THANKS and RELEASE files to new docs directory, along with
DEVELOPING
- Replace make commands in README and RELEASE files
Misc:
- Remove Rust nightly dependency since it was solely for imports
granulaity for rustfmt
- This repository is once again only reliant on stable Rust
toolchains
- Remove "rust-version" from Cargo toml since it was unused
- Remove Makefile entirely in favor of DEVELOPING file (multi-platform
friendly)
- Update markdown code block languages as "shell" instead of "bash" to
be multi-platform friendly
Signed-off-by: Nick Gerace <nickagerace@gmail.com>
-rw-r--r-- | .github/workflows/ci.yml | 24 | ||||
-rw-r--r-- | Cargo.lock | 93 | ||||
-rw-r--r-- | Cargo.toml | 4 | ||||
-rw-r--r-- | Makefile | 44 | ||||
-rw-r--r-- | README.md | 31 | ||||
-rw-r--r-- | bors.toml | 3 | ||||
-rw-r--r-- | docs/DEVELOPING.md | 48 | ||||
-rw-r--r-- | docs/RELEASE.md (renamed from RELEASE.md) | 20 | ||||
-rw-r--r-- | docs/THANKS.md (renamed from THANKS.md) | 0 | ||||
-rw-r--r-- | rustfmt.toml | 2 | ||||
-rw-r--r-- | scripts/.gitignore | 1 | ||||
-rw-r--r-- | scripts/README.md | 15 | ||||
-rwxr-xr-x | scripts/bench-loosely.sh | 27 | ||||
-rw-r--r-- | scripts/bench-loosely/Cargo.toml | 7 | ||||
-rw-r--r-- | scripts/bench-loosely/src/main.rs | 95 | ||||
-rw-r--r-- | scripts/size/Cargo.toml | 7 | ||||
-rw-r--r-- | scripts/size/src/main.rs | 53 | ||||
-rw-r--r-- | src/display/mod.rs | 16 | ||||
-rw-r--r-- | src/main.rs | 79 | ||||
-rw-r--r-- | src/report/mod.rs | 14 | ||||
-rw-r--r-- | src/status.rs | 2 |
21 files changed, 425 insertions, 160 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ef7f93..0e892b2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,44 +10,32 @@ on: paths: - "**.rs" - "Cargo.*" - - "rustfmt.toml" + - "*.toml" + - ".github/workflows/ci.yml" concurrency: group: "${{ github.workflow }}-${{ github.ref }}" cancel-in-progress: true jobs: - lint: - name: "Nightly Fmt Check" + test: + name: "Lint, Test, and Build (ubuntu-latest)" runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - # Do not use "rust-cache" since nightly will wipe out the cache daily - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly + toolchain: stable override: true - components: rustfmt + components: rustfmt, clippy - uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check - test: - name: "Clippy Lint, Test, and Build (ubuntu-latest)" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - components: clippy - uses: Swatinem/rust-cache@v1 - uses: actions-rs/cargo@v1 with: command: clippy args: -- -D warnings - # FIXME: use cargo nextest - uses: actions-rs/cargo@v1 with: command: test @@ -3,6 +3,15 @@ version = 3 [[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] name = "argh" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -71,9 +80,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "crossbeam-channel" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" +checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" dependencies = [ "cfg-if", "crossbeam-utils", @@ -92,10 +101,11 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" dependencies = [ + "autocfg", "cfg-if", "crossbeam-utils", "lazy_static", @@ -105,15 +115,31 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" dependencies = [ "cfg-if", "lazy_static", ] [[package]] +name = "ctor" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "diff" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" + +[[package]] name = "dirs" version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -124,9 +150,9 @@ dependencies = [ [[package]] name = "dirs-sys" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", "redox_users", @@ -162,9 +188,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" dependencies = [ "cfg-if", "libc", @@ -180,6 +206,7 @@ dependencies = [ "env_logger", "git2", "log", + "pretty_assertions", "rayon", "serde", "serde_json", @@ -259,9 +286,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.119" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" +checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" [[package]] name = "libgit2-sys" @@ -289,9 +316,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.14" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" dependencies = [ "cfg-if", ] @@ -322,6 +349,15 @@ dependencies = [ ] [[package]] +name = "output_vt100" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" +dependencies = [ + "winapi", +] + +[[package]] name = "percent-encoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -334,6 +370,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" [[package]] +name = "pretty_assertions" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c038cb5319b9c704bf9c227c261d275bfec0ad438118a2787ce47944fb228b" +dependencies = [ + "ansi_term", + "ctor", + "diff", + "output_vt100", +] + +[[package]] name = "proc-macro2" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -344,9 +392,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" dependencies = [ "proc-macro2", ] @@ -378,21 +426,22 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c" +checksum = "8ae183fc1b06c149f0c1793e1eb447c8b04bfe46d48e9e48bfb8d2d7ed64ecf0" dependencies = [ "bitflags", ] [[package]] name = "redox_users" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +checksum = "7776223e2696f1aa4c6b0170e83212f47296a00424305117d013dfe86fb0fe55" dependencies = [ "getrandom", "redox_syscall", + "thiserror", ] [[package]] @@ -440,9 +489,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.86" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f" dependencies = [ "proc-macro2", "quote", @@ -11,7 +11,6 @@ repository = "https://github.com/nickgerace/gfold/" edition = "2021" version = "4.0.0-rc.1" -rust-version = "1.56.1" [dependencies] argh = "0" @@ -29,6 +28,9 @@ toml = "0" # Removed features: ["regex", "termcolor"] env_logger = { version = "0", features = ["atty", "humantime"], default_features = false } +[dev-dependencies] +pretty_assertions = "1" + [profile.release] codegen-units = 1 diff --git a/Makefile b/Makefile deleted file mode 100644 index 8d5b0a6..0000000 --- a/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -MAKEPATH := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) - -.DEFAULT_GOAL := prepare - -prepare: - cd $(MAKEPATH); cargo +nightly fmt - cd $(MAKEPATH); cargo update - cd $(MAKEPATH); cargo fix --edition-idioms --allow-dirty --allow-staged - cd $(MAKEPATH); cargo clippy --all-features --all-targets -.PHONY: prepare - -lint: - cd $(MAKEPATH); cargo +nightly fmt --all -- --check - cd $(MAKEPATH); cargo clippy -- -D warnings -.PHONY: lint - -test: - cd $(MAKEPATH); cargo nextest run --success-output immediate -.PHONY: test - -scan: - cd $(MAKEPATH); cargo +nightly udeps - cd $(MAKEPATH); cargo bloat --release - cd $(MAKEPATH); cargo bloat --release --crates - cd $(MAKEPATH); cargo audit - cd $(MAKEPATH); cargo msrv -.PHONY: scan - -clean: - cd $(MAKEPATH); cargo clean -.PHONY: clean - -install: - cargo install --locked --path $(MAKEPATH) -.PHONY: install - -bench-loosely: - $(MAKEPATH)/scripts/bench-loosely.sh -.PHONY: bench-loosely - -size: - cd $(MAKEPATH); cargo build --release - @du -h $(MAKEPATH)/target/release/gfold -.PHONY: size @@ -82,7 +82,7 @@ gfold ../../this/is/a/relative/path Upon execution, `gfold` will look for a config file at the following path on macOS, Linux and similar operating systems: -```bash +```shell $HOME/.config/gfold.toml ``` @@ -96,7 +96,7 @@ Creating and using the config file is entirely optional, and you can ignore your Here is an example creation workflow for a config file: -```bash +```shell gfold --classic ~/ --print > $HOME/.config/gfold.toml ``` @@ -112,7 +112,7 @@ display_mode = 'Classic' You can back up a config file and track its history with `git`. On macOS, Linux, and most systems, you can link the file back to a `git` repository. -```bash +```shell ln -s path/to/repository/gfold.toml $HOME/.config/gfold.toml ``` @@ -124,7 +124,7 @@ Now, you can update the config file within your repository and include the linki **macOS users:** you can use [Homebrew](https://brew.sh) to install the [tap](https://github.com/nickgerace/homebrew-nickgerace/blob/main/Formula/gfold.rb). -```bash +```shell brew install nickgerace/nickgerace/gfold ``` @@ -132,13 +132,13 @@ _Note:_ the tap may not work with [Linuxbrew](https://docs.brew.sh/Homebrew-on-L **Arch Linux users:** you can use [pacman](https://wiki.archlinux.org/title/Pacman) to install `gfold` from the [community repository](https://archlinux.org/packages/community/x86_64/gfold/). -```bash +```shell pacman -S gfold ``` If you'd like the [development (VCS) package](https://aur.archlinux.org/packages/gfold-git/), you can install it from the AUR. -```bash +```shell paru -S gfold-git ``` @@ -146,36 +146,35 @@ _Note:_ the above example uses [paru](https://github.com/Morganamilo/paru), whic **Nix and NixOS users:** you can install `gfold` from [nixpkgs](https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/version-management/git-and-tools/gfold/default.nix): -```bash +```shell nix-env --install ripgrep ``` **Rust developers and Cargo users:** you can use [cargo](https://crates.io) to install the [crate](https://crates.io/crates/gfold) on almost any platform. -```bash +```shell cargo install gfold ``` Keeping the crate up to date is easy with [cargo-update](https://crates.io/crates/cargo-update). -```bash +```shell cargo install cargo-update cargo install-update -a ``` **Build and install from source:** if you want to install from source, and not from [crates.io](https://crates.io/crates/gfold), you can clone the repository and build `gfold`. -```bash +```shell ( git clone https://github.com/nickgerace/gfold.git - cd gfold - make install + cargo install --locked --path gfold ) ``` **Download a binary:** if you do not want to use one of the above installation methods, you can download a binary from the [releases](https://github.com/nickgerace/gfold/releases) page. -```bash +```shell curl -s https://raw.githubusercontent.com/nickgerace/gfold/main/scripts/install.sh | bash ``` @@ -184,7 +183,7 @@ Discretion is advised, including downloading and reading the script before execu To uninstall `gfold` fully, after using this installation method, execute the following script: -```bash +```shell curl -s https://raw.githubusercontent.com/nickgerace/gfold/main/scripts/uninstall.sh | bash ``` @@ -194,7 +193,7 @@ The uninstall script can also be used for cleanup in the event of a failed insta ## Community -For more information and thanks to contributors, users, and the "community" at large, please refer to the **[THANKS](./THANKS.md)** file. +For more information and thanks to contributors, users, and the "community" at large, please refer to the **[THANKS](./docs/THANKS.md)** file. ### Projects @@ -222,6 +221,6 @@ If `fold` from [GNU Coreutils](https://www.gnu.org/software/coreutils/) is insta You can avoid this collision with shell aliases, shell functions, and/or `PATH` changes. Here is an example with the `o` dropped from `gfold`: -```bash +```shell alias gfld=$HOME/.cargo/bin/gfold ``` @@ -1,6 +1,5 @@ status = [ - "Nightly Fmt Check", - "Clippy Lint, Test, and Build (ubuntu-latest)", + "Lint, Test, and Build (ubuntu-latest)", "Build (windows-latest)", "Build (macos-latest)" ] diff --git a/docs/DEVELOPING.md b/docs/DEVELOPING.md new file mode 100644 index 0000000..d990684 --- /dev/null +++ b/docs/DEVELOPING.md @@ -0,0 +1,48 @@ +# Developing + +This document contains information related to development. + +## Preparing Changes + +First, update dependencies and tidy your changes. + +```shell +cargo fmt +cargo update +cargo fix --edition-idioms --allow-dirty --allow-staged +cargo clippy --all-features --all-targets +``` + +Now, ensure that lints, tests, and builds succeed. + +```shell +cargo fmt --all -- --check +cargo clippy -- -D warnings +cargo doc +cargo test +cargo build +``` + +> Alternatively, you can replace `cargo test` above with [cargo nextest](https://github.com/nextest-rs/nextest). +> +> ```shell +> cargo nextest run +> ``` + +## Performance Checks + +Navigate to the [README in the `scripts` directory](../scripts/README.md) for more information on +how to run performance checks. + +## Optional Checks + +The following checks are optional and should be run occasionally. + + +```shell +# This command requires a nightly toolchain to be installed. +cargo +nightly udeps +cargo bloat --release +cargo bloat --release --crates +cargo audit +```
\ No newline at end of file diff --git a/RELEASE.md b/docs/RELEASE.md index c92cc52..3c0f43e 100644 --- a/RELEASE.md +++ b/docs/RELEASE.md @@ -5,15 +5,19 @@ This document contains all information related to release. ## Checklist This checklist details the `gfold` release process. -Steps should (and frequently must) be executed in sequential order. +Steps should be executed in sequential order. - [ ] Checkout and rebase `main` to its latest commit, then checkout a new branch - [ ] Change the `version` field in `Cargo.toml` to the new tag - [ ] **Full Releases Only**: change the version in `CHANGELOG.md` and uncomment the following line: `<!--The latest version contains all changes.-->` -- [ ] Run final `make` targets and verify that everything looks/works as expected: - -```bash -make lint test; cargo build +- [ ] Verify that everything looks/works as expected: + +```shell +cargo fmt --all -- --check +cargo clippy -- -D warnings +cargo test +cargo doc +cargo build ``` - [ ] Create and _do not merge_ a commit with the following message: `Update to <tag>` @@ -27,21 +31,21 @@ cargo publish --dry-run - [ ] Checkout and rebase `main` to its latest commit, which should be the aforementioned commit - [ ] Tag and push the tag: -```bash +```shell git tag <tag> git push --tags origin main ``` - [ ] Publish the crate: -```bash +```shell cargo publish ``` - [ ] Verify that the [crate](https://crates.io/crates/gfold) on `crates.io` looks correct - [ ] Download and install the crate: -```bash +```shell # Full releases cargo install --locked gfold diff --git a/THANKS.md b/docs/THANKS.md index cf1a526..cf1a526 100644 --- a/THANKS.md +++ b/docs/THANKS.md diff --git a/rustfmt.toml b/rustfmt.toml deleted file mode 100644 index f8b799b..0000000 --- a/rustfmt.toml +++ /dev/null @@ -1,2 +0,0 @@ -imports_granularity = "Module" -newline_style = "Unix" diff --git a/scripts/.gitignore b/scripts/.gitignore new file mode 100644 index 0000000..6485775 --- /dev/null +++ b/scripts/.gitignore @@ -0,0 +1 @@ +**/Cargo.lock diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..513bacb --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,15 @@ +# Scripts + +Run a script by executing `cargo run` in its directory. + +```shell +cd ./scripts/<script>/ +cargo run +``` + +Alternatively, you can run `cargo run` from another directory. +Here is an example: + +```shell +cargo run --manifest-path ./scripts/<script>/Cargo.toml +```
\ No newline at end of file diff --git a/scripts/bench-loosely.sh b/scripts/bench-loosely.sh deleted file mode 100755 index b08a1b5..0000000 --- a/scripts/bench-loosely.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash -set -e - -REALPATH=realpath -if [ "$(uname -s)" = "Darwin" ]; then - REALPATH=grealpath -fi -REPOPATH=$(dirname $(dirname $($REALPATH -s $0))) - -( cd $REPOPATH; cargo build --release ) - -OLD=$HOME/.cargo/bin/gfold -NEW=$REPOPATH/target/release/gfold - -function run { - local BENCH_FILE - for COUNT in {1..4}; do - echo "- - - - - - - - - - - - -" - echo "[OLD]" - time $OLD -i $1 > /dev/null - echo "[NEW]" - time $NEW -i $1 > /dev/null - done -} - -run "$HOME/" "home" -run "$HOME/src" "src" diff --git a/scripts/bench-loosely/Cargo.toml b/scripts/bench-loosely/Cargo.toml new file mode 100644 index 0000000..b533aa1 --- /dev/null +++ b/scripts/bench-loosely/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "bench-loosely" +version = "0.1.0" +edition = "2021" + +[dependencies] +dirs = "4" diff --git a/scripts/bench-loosely/src/main.rs b/scripts/bench-loosely/src/main.rs new file mode 100644 index 0000000..68d58f6 --- /dev/null +++ b/scripts/bench-loosely/src/main.rs @@ -0,0 +1,95 @@ +use std::fs; +use std::fs::Metadata; +use std::io; +use std::path::{Path, PathBuf}; +use std::process::Command; +use std::time::{Duration, Instant}; + +fn main() { + let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); + let repo = manifest_dir + .parent() + .expect("could not get parent") + .parent() + .expect("could not get parent"); + + println!("Running \"cargo build --release\"..."); + let output = Command::new("cargo") + .arg("build") + .arg("--release") + .output() + .expect("could not execute command"); + if !output.status.success() { + panic!("command failed: \"cargo build --release\""); + } + + let binary = repo.join("target").join("release").join("gfold"); + let home = dirs::home_dir().expect("could not find home directory"); + let installed = home.join(".cargo").join("bin").join("gfold"); + let runs = 4; + + // Loosely bench using the home directory as the target. + for run in 0..runs { + println!( + "group {} of {} in {}", + run + 1, + runs, + home.to_str().expect("could not convert to str") + ); + loose_bench(&binary, &installed, &home); + } + + // Loosely bench with the parent directory of the repository as the target. + let parent_of_repo = repo.parent().expect("could not get parent"); + for run in 0..runs { + println!( + "group {} of {} in {}", + run + 1, + runs, + parent_of_repo.to_str().expect("could not convert to str") + ); + loose_bench(&binary, &installed, parent_of_repo); + } +} + +fn loose_bench(new: &Path, old: &Path, target: &Path) { + let new_duration = execute(new, target); + let old_duration = execute(old, target); + let (new_text, old_text) = if new_duration > old_duration { + ("LOST", "WON ") + } else if new_duration < old_duration { + ("WON ", "LOST") + } else { + ("TIE ", "TIE ") + }; + + println!( + " {} @ {:?} - {}", + new_text, + new_duration, + new.to_str().expect("could not convert to str"), + ); + println!( + " {} @ {:?} - {}", + old_text, + old_duration, + old.to_str().expect("could not convert to str"), + ); +} + +fn execute(binary: &Path, target: &Path) -> Duration { + let start = Instant::now(); + let output = Command::new(binary) + .arg("-i") + .arg(target) + .output() + .expect("could not execute command"); + let duration = start.elapsed(); + + // Check for failure _after_ the bench finishes. + if !output.status.success() { + panic!("bench failed"); + } + + duration +} diff --git a/scripts/size/Cargo.toml b/scripts/size/Cargo.toml new file mode 100644 index 0000000..1a6bc06 --- /dev/null +++ b/scripts/size/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "size" +version = "0.1.0" +edition = "2021" + +[dependencies] +dirs = "4" diff --git a/scripts/size/src/main.rs b/scripts/size/src/main.rs new file mode 100644 index 0000000..2d5f5e9 --- /dev/null +++ b/scripts/size/src/main.rs @@ -0,0 +1,53 @@ +use std::fs; +use std::fs::Metadata; +use std::io; +use std::path::{Path, PathBuf}; +use std::process::Command; + +fn main() { + let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); + let repo = manifest_dir + .parent() + .expect("could not get parent") + .parent() + .expect("could not get parent"); + + println!("Running \"cargo build --release\"..."); + let output = Command::new("cargo") + .arg("build") + .arg("--release") + .output() + .expect("could not execute command"); + if !output.status.success() { + panic!("command failed: \"cargo build --release\""); + } + + let binary = repo.join("target").join("release").join("gfold"); + let metadata = fs::metadata(&binary).expect("could not get metadata"); + print_binary_size(&metadata, &binary); + + // Check if the binary is currently installed to compare release build sizes. If it is not, + // we can skip this check. + let home = dirs::home_dir().expect("could not find home directory"); + let installed = home.join(".cargo").join("bin").join("gfold"); + match fs::metadata(&installed) { + Ok(metadata) => { + print_binary_size(&metadata, &installed); + } + Err(e) if e.kind() == io::ErrorKind::NotFound => {} + Err(e) => panic!( + "encountered error when trying to get metadata for file: {}", + e.to_string() + ), + } +} + +fn print_binary_size(metadata: &Metadata, path: &PathBuf) { + // Divisor used to perform human readable size conversion. + // 1048576.0 = 1024.0 * 1024.0 + println!( + "{:.3} MB - {}", + metadata.len() as f64 / 1048576.0, + path.to_str().expect("could not convert to str") + ); +} diff --git a/src/display/mod.rs b/src/display/mod.rs index 5da34fe..fc1a339 100644 --- a/src/display/mod.rs +++ b/src/display/mod.rs @@ -3,7 +3,7 @@ use crate::config::{ColorMode, DisplayMode}; use crate::display::color::ColorHarness; use crate::error::Error; -use crate::report::Reports; +use crate::report::LabeledReports; use crate::result::Result; use log::warn; use std::path::Path; @@ -16,7 +16,7 @@ const NONE: &str = "none"; /// This function chooses the display execution function based on the [`DisplayMode`] provided. pub fn display( display_mode: &DisplayMode, - reports: &Reports, + reports: &LabeledReports, color_mode: &ColorMode, ) -> Result<()> { match display_mode { @@ -26,8 +26,8 @@ pub fn display( } } -/// Display [`Reports`] to `stdout` in the standard (default) format. -fn standard(reports: &Reports, color_mode: &ColorMode) -> Result<()> { +/// Display [`LabeledReports`] to `stdout` in the standard (default) format. +fn standard(reports: &LabeledReports, color_mode: &ColorMode) -> Result<()> { let mut all_reports = Vec::new(); for grouped_report in reports { all_reports.append(&mut grouped_report.1.clone()); @@ -69,8 +69,8 @@ fn standard(reports: &Reports, color_mode: &ColorMode) -> Result<()> { Ok(()) } -/// Display [`Reports`] to `stdout` in JSON format. -fn json(reports: &Reports) -> Result<()> { +/// Display [`LabeledReports`] to `stdout` in JSON format. +fn json(reports: &LabeledReports) -> Result<()> { let mut all_reports = Vec::new(); for grouped_report in reports { all_reports.append(&mut grouped_report.1.clone()); @@ -81,8 +81,8 @@ fn json(reports: &Reports) -> Result<()> { Ok(()) } -/// Display [`Reports`] to `stdout` in the classic format. -fn classic(reports: &Reports, color_mode: &ColorMode) -> Result<()> { +/// Display [`LabeledReports`] to `stdout` in the classic format. +fn classic(reports: &LabeledReports, color_mode: &ColorMode) -> Result<()> { let color_harness = ColorHarness::new(color_mode); let length = reports.keys().len(); diff --git a/src/main.rs b/src/main.rs index 4797f6c..ea39e87 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,8 +22,12 @@ fn main() -> Result<()> { mod tests { use super::*; - use crate::config::Config; + use crate::config::{Config, DisplayMode}; + use crate::report::{LabeledReports, Report}; + use crate::status::Status; use git2::Repository; + use pretty_assertions::assert_eq; + use std::collections::BTreeMap; use std::path::Path; use std::{env, fs, io}; @@ -58,10 +62,10 @@ mod tests { // ├── bar // ├── baz // ├── foo - // │ └── newfile + // │ └── newfile // └── nested // ├── one - // │ ├── newfile + // │ └── newfile // ├── three // └── two @@ -94,7 +98,74 @@ mod tests { let mut config = Config::new().expect("could not create new config"); config.path = test; - assert!(run::run(&config).is_ok()); + + // Now, let's ensure our reports are what we expect. + let test_dir = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("target") + .join("test"); + let mut expected_reports: LabeledReports = BTreeMap::new(); + + let key = test_dir + .to_str() + .expect("could not convert to str") + .to_string(); + let mut reports = vec![ + Report::new(&test_dir.join("foo"), "HEAD", &Status::Unclean, None, None) + .expect("could not create report"), + Report::new(&test_dir.join("bar"), "HEAD", &Status::Clean, None, None) + .expect("could not create report"), + Report::new(&test_dir.join("baz"), "HEAD", &Status::Clean, None, None) + .expect("could not create report"), + ]; + reports.sort_by(|a, b| a.name.cmp(&b.name)); + expected_reports.insert(Some(key), reports); + + let nested_test_dir = test_dir.join("nested"); + let key = nested_test_dir + .to_str() + .expect("could not convert to str") + .to_string(); + let mut reports = vec![ + Report::new( + &nested_test_dir.join("one"), + "HEAD", + &Status::Unclean, + None, + None, + ) + .expect("could not create report"), + Report::new( + &nested_test_dir.join("two"), + "HEAD", + &Status::Clean, + None, + None, + ) + .expect("could not create report"), + Report::new( + &nested_test_dir.join("three"), + "HEAD", + &Status::Clean, + None, + None, + ) + .expect("could not create report"), + ]; + reports.sort_by(|a, b| a.name.cmp(&b.name)); + expected_reports.insert(Some(key), reports); + + // Use classic display mode to avoid collecting email results. + config.display_mode = DisplayMode::Classic; + let found_labeled_reports = report::generate_reports(&config.path, &config.display_mode) + .expect("could not generate reports"); + let mut found_labeled_reports_sorted = LabeledReports::new(); + for labeled_report in found_labeled_reports { + let mut value = labeled_report.1; + value.sort_by(|a, b| a.name.cmp(&b.name)); + found_labeled_reports_sorted.insert(labeled_report.0.clone(), value.clone()); + } + + assert_eq!(found_labeled_reports_sorted, expected_reports); } } diff --git a/src/report/mod.rs b/src/report/mod.rs index aadab52..bcf0dbb 100644 --- a/src/report/mod.rs +++ b/src/report/mod.rs @@ -19,10 +19,10 @@ const HEAD: &str = "HEAD"; /// parent directory for a group of reports ([`Vec<Report>`]). The values corresponding to those keys /// are the actual groups of reports. // NOTE: We use a BTreeMap over a HashMap for sorted keys. -pub type Reports = BTreeMap<Option<String>, Vec<Report>>; +pub type LabeledReports = BTreeMap<Option<String>, Vec<Report>>; /// A collection of results for a Git repository at a given path. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct Report { /// The directory name of the Git repository. pub name: String, @@ -41,7 +41,7 @@ pub struct Report { } impl Report { - fn new( + pub fn new( path: &Path, branch: &str, status: &Status, @@ -71,9 +71,9 @@ impl Report { } } -/// Generate [`Reports`] for a given path and its children. The [`DisplayMode`] is required because -/// any two display modes can require differing ammounts of data to be collected. -pub fn generate_reports(path: &Path, display_mode: &DisplayMode) -> Result<Reports> { +/// Generate [`LabeledReports`] for a given path and its children. The [`DisplayMode`] is required +/// because any two display modes can require differing amounts of data to be collected. +pub fn generate_reports(path: &Path, display_mode: &DisplayMode) -> Result<LabeledReports> { let include_email = match display_mode { DisplayMode::Standard | DisplayMode::Json => true, DisplayMode::Classic => false, @@ -84,7 +84,7 @@ pub fn generate_reports(path: &Path, display_mode: &DisplayMode) -> Result<Repor .map(|path| generate_report(path, include_email)) .collect::<Vec<Result<Report>>>(); - let mut processed = Reports::new(); + let mut processed = LabeledReports::new(); for wrapped_report in unprocessed { match wrapped_report { Ok(report) => { diff --git a/src/status.rs b/src/status.rs index a75ebde..2301dc9 100644 --- a/src/status.rs +++ b/src/status.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; /// A summarized interpretation of the status of a Git working tree. -#[derive(Debug, Clone, Copy, Deserialize, Serialize)] +#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq)] pub enum Status { Bare, Clean, |