diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..d8c2032 --- /dev/null +++ b/.cargo/config @@ -0,0 +1,2 @@ +[alias] +xtask = "run --manifest-path ./xtask/Cargo.toml --" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ba95a67..284fe2f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,4 +21,4 @@ jobs: if: ${{ matrix.os != 'windows-latest' }} with: nix_path: nixpkgs=https://github.com/NixOS/nixpkgs/archive/20.09.tar.gz - - run: make test + - run: cargo xtask ci --nix ${{ matrix.os != 'windows-latest' }} diff --git a/Makefile b/Makefile deleted file mode 100644 index a45c1b0..0000000 --- a/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -ifeq ($(CI), true) - ci_color_always := --color=always -endif - -ifeq ($(OS), Windows_NT) - NIX := false -else - NIX := true -endif - -.PHONY: build clean install test uninstall - -build: - cargo build $(ci_color_always) - -clean: - cargo clean $(ci_color_always) - -install: - cargo install --path=. $(ci_color_always) - -ifeq ($(NIX), true) -test: - nix-shell --pure --run 'cargo fmt -- --check --files-with-diff $(ci_color_always)' - nix-shell --pure --run 'cargo check --all-features $(ci_color_always)' - nix-shell --pure --run 'cargo clippy --all-features $(ci_color_always) -- --deny warnings --deny clippy::all' - nix-shell --pure --run 'cargo test --all-features --no-fail-fast $(ci_color_always)' - nix-shell --pure --run 'cargo audit --deny warnings $(ci_color_always)' -else -test: - cargo fmt -- --check --files-with-diff $(ci_color_always) - cargo check --all-features $(ci_color_always) - cargo clippy --all-features $(ci_color_always) -- --deny warnings --deny clippy::all - cargo test --no-fail-fast $(ci_color_always) - cargo audit --deny warnings $(ci_color_always) -endif - -uninstall: - cargo uninstall $(ci_color_always) diff --git a/rustfmt.toml b/rustfmt.toml index 1e7bc52..c007369 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,5 +1,6 @@ # group_imports = "StdExternalCrate" # imports_granularity = "Module" +max_width = 120 newline_style = "Native" use_field_init_shorthand = true use_small_heuristics = "Max" diff --git a/src/app/_app.rs b/src/app/_app.rs index cf7a0de..32859c6 100644 --- a/src/app/_app.rs +++ b/src/app/_app.rs @@ -10,7 +10,7 @@ const ENV_HELP: &str = "ENVIRONMENT VARIABLES: _ZO_MAXAGE Maximum total age after which entries start getting deleted _ZO_RESOLVE_SYMLINKS Resolve symlinks when storing paths"; -#[derive(Debug, Clap)] +#[derive(Clap, Debug)] #[clap( bin_name = env!("CARGO_PKG_NAME"), about, diff --git a/src/app/add.rs b/src/app/add.rs index c735f19..6a63f0d 100644 --- a/src/app/add.rs +++ b/src/app/add.rs @@ -22,11 +22,7 @@ impl Run for Add { let mut db = db.open()?; for path in &self.paths { - let path = if config::resolve_symlinks() { - util::canonicalize(path) - } else { - util::resolve_path(path) - }?; + let path = if config::resolve_symlinks() { util::canonicalize(path) } else { util::resolve_path(path) }?; let path = util::path_to_str(&path)?; // Ignore path if it contains unsupported characters, or if it's in diff --git a/src/app/import.rs b/src/app/import.rs index 2b23749..b1f573b 100644 --- a/src/app/import.rs +++ b/src/app/import.rs @@ -8,9 +8,8 @@ use std::fs; impl Run for Import { fn run(&self) -> Result<()> { - let buffer = fs::read_to_string(&self.path).with_context(|| { - format!("could not open database for importing: {}", &self.path.display()) - })?; + let buffer = fs::read_to_string(&self.path) + .with_context(|| format!("could not open database for importing: {}", &self.path.display()))?; let data_dir = config::data_dir()?; let mut db = DatabaseFile::new(data_dir); @@ -64,8 +63,7 @@ fn from_z<'a>(db: &mut Database<'a>, buffer: &'a str) -> Result<()> { let mut split = line.rsplitn(3, '|'); let last_accessed = split.next().with_context(|| format!("invalid entry: {}", line))?; - let last_accessed = - last_accessed.parse().with_context(|| format!("invalid epoch: {}", last_accessed))?; + let last_accessed = last_accessed.parse().with_context(|| format!("invalid epoch: {}", last_accessed))?; let rank = split.next().with_context(|| format!("invalid entry: {}", line))?; let rank = rank.parse().with_context(|| format!("invalid rank: {}", rank))?; diff --git a/src/config.rs b/src/config.rs index 1a71785..9854fd3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -45,8 +45,7 @@ pub fn exclude_dirs() -> Result> { env::split_paths(&paths) .map(|path| { let pattern = path.to_str().context("invalid unicode in _ZO_EXCLUDE_DIRS")?; - Pattern::new(pattern) - .with_context(|| format!("invalid glob in _ZO_EXCLUDE_DIRS: {}", pattern)) + Pattern::new(pattern).with_context(|| format!("invalid glob in _ZO_EXCLUDE_DIRS: {}", pattern)) }) .collect() }, @@ -61,9 +60,8 @@ pub fn maxage() -> Result { match env::var_os("_ZO_MAXAGE") { Some(maxage) => { let maxage = maxage.to_str().context("invalid unicode in _ZO_MAXAGE")?; - let maxage = maxage - .parse::() - .with_context(|| format!("unable to parse _ZO_MAXAGE as integer: {}", maxage))?; + let maxage = + maxage.parse::().with_context(|| format!("unable to parse _ZO_MAXAGE as integer: {}", maxage))?; Ok(maxage as Rank) } None => Ok(10000.0), diff --git a/src/db/mod.rs b/src/db/mod.rs index ba2b712..da49e52 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -25,21 +25,18 @@ impl<'file> Database<'file> { } let buffer = self.dirs.to_bytes()?; - let mut file = NamedTempFile::new_in(self.data_dir).with_context(|| { - format!("could not create temporary database in: {}", self.data_dir.display()) - })?; + let mut file = NamedTempFile::new_in(self.data_dir) + .with_context(|| format!("could not create temporary database in: {}", self.data_dir.display()))?; // Preallocate enough space on the file, preventing copying later on. // This optimization may fail on some filesystems, but it is safe to // ignore it and proceed. let _ = file.as_file().set_len(buffer.len() as _); - file.write_all(&buffer).with_context(|| { - format!("could not write to temporary database: {}", file.path().display()) - })?; + file.write_all(&buffer) + .with_context(|| format!("could not write to temporary database: {}", file.path().display()))?; let path = db_path(&self.data_dir); - persist(file, &path) - .with_context(|| format!("could not replace database: {}", path.display()))?; + persist(file, &path).with_context(|| format!("could not replace database: {}", path.display()))?; self.modified = false; Ok(()) @@ -51,11 +48,7 @@ impl<'file> Database<'file> { match self.dirs.iter_mut().find(|dir| dir.path == path) { None => { - self.dirs.push(Dir { - path: path.to_string().into(), - last_accessed: now, - rank: 1.0, - }); + self.dirs.push(Dir { path: path.to_string().into(), last_accessed: now, rank: 1.0 }); } Some(dir) => { dir.last_accessed = now; @@ -184,23 +177,19 @@ impl DatabaseFile { match fs::read(&path) { Ok(buffer) => { self.buffer = buffer; - let dirs = DirList::from_bytes(&self.buffer).with_context(|| { - format!("could not deserialize database: {}", path.display()) - })?; + let dirs = DirList::from_bytes(&self.buffer) + .with_context(|| format!("could not deserialize database: {}", path.display()))?; Ok(Database { dirs, modified: false, data_dir: &self.data_dir }) } Err(e) if e.kind() == io::ErrorKind::NotFound => { // Create data directory, but don't create any file yet. // The file will be created later by [`Database::save`] // if any data is modified. - fs::create_dir_all(&self.data_dir).with_context(|| { - format!("unable to create data directory: {}", self.data_dir.display()) - })?; + fs::create_dir_all(&self.data_dir) + .with_context(|| format!("unable to create data directory: {}", self.data_dir.display()))?; Ok(Database { dirs: DirList::new(), modified: false, data_dir: &self.data_dir }) } - Err(e) => { - Err(e).with_context(|| format!("could not read from database: {}", path.display())) - } + Err(e) => Err(e).with_context(|| format!("could not read from database: {}", path.display())), } } } diff --git a/src/db/stream.rs b/src/db/stream.rs index 5386dab..6811c09 100644 --- a/src/db/stream.rs +++ b/src/db/stream.rs @@ -151,8 +151,7 @@ mod tests { #[case(&["/foo/", "/bar"], "/foo/bar", false)] #[case(&["/foo/", "/bar"], "/foo/baz/bar", true)] fn query(#[case] keywords: &[&str], #[case] path: &str, #[case] is_match: bool) { - let mut db = - Database { dirs: Vec::new().into(), modified: false, data_dir: &PathBuf::new() }; + let mut db = Database { dirs: Vec::new().into(), modified: false, data_dir: &PathBuf::new() }; let stream = db.stream(0).with_keywords(keywords); assert_eq!(is_match, stream.matches_keywords(path)); } diff --git a/src/shell.rs b/src/shell.rs index 1bb4eb7..6765e7e 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -50,13 +50,7 @@ mod tests { ) { let opts = Opts { cmd, hook, echo, resolve_symlinks }; let source = Bash(&opts).render().unwrap(); - - Command::new("bash") - .args(&["--noprofile", "--norc", "-c", &source]) - .assert() - .success() - .stdout("") - .stderr(""); + Command::new("bash").args(&["--noprofile", "--norc", "-c", &source]).assert().success().stdout("").stderr(""); } #[rstest] @@ -110,19 +104,12 @@ mod tests { // Filter out lines using edit:*, since those functions // are only available in the interactive editor. - for line in - Elvish(&opts).render().unwrap().split('\n').filter(|line| !line.contains("edit:")) - { + for line in Elvish(&opts).render().unwrap().split('\n').filter(|line| !line.contains("edit:")) { source.push_str(line); source.push('\n'); } - Command::new("elvish") - .args(&["-c", &source, "-norc"]) - .assert() - .success() - .stdout("") - .stderr(""); + Command::new("elvish").args(&["-c", &source, "-norc"]).assert().success().stdout("").stderr(""); } #[rstest] @@ -184,12 +171,8 @@ mod tests { let tempdir = tempfile::tempdir().unwrap(); let tempdir = tempdir.path().to_str().unwrap(); - let assert = Command::new("nu") - .env("HOME", tempdir) - .args(&["--commands", &source]) - .assert() - .success() - .stderr(""); + let assert = + Command::new("nu").env("HOME", tempdir).args(&["--commands", &source]).assert().success().stderr(""); if opts.hook != InitHook::Pwd { assert.stdout(""); @@ -302,12 +285,7 @@ mod tests { let mut source = Xonsh(&opts).render().unwrap(); source.push('\n'); - Command::new("black") - .args(&["--check", "--diff", "-"]) - .write_stdin(source) - .assert() - .success() - .stdout(""); + Command::new("black").args(&["--check", "--diff", "-"]).write_stdin(source).assert().success().stdout(""); } #[rstest] diff --git a/src/util.rs b/src/util.rs index a2635e3..6eb7e46 100644 --- a/src/util.rs +++ b/src/util.rs @@ -7,8 +7,7 @@ use std::path::{Component, Path, PathBuf}; use std::time::SystemTime; pub fn canonicalize>(path: &P) -> Result { - dunce::canonicalize(path) - .with_context(|| format!("could not resolve path: {}", path.as_ref().display())) + dunce::canonicalize(path).with_context(|| format!("could not resolve path: {}", path.as_ref().display())) } pub fn current_dir() -> Result { @@ -16,10 +15,8 @@ pub fn current_dir() -> Result { } pub fn current_time() -> Result { - let current_time = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .context("system clock set to invalid time")? - .as_secs(); + let current_time = + SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).context("system clock set to invalid time")?.as_secs(); Ok(current_time) } @@ -51,9 +48,7 @@ pub fn resolve_path>(path: &P) -> Result { match components.next() { Some(Component::Prefix(prefix)) => match prefix.kind() { - Prefix::Disk(drive_letter) | Prefix::VerbatimDisk(drive_letter) => { - Some(drive_letter) - } + Prefix::Disk(drive_letter) | Prefix::VerbatimDisk(drive_letter) => Some(drive_letter), _ => None, }, _ => None, @@ -106,9 +101,8 @@ pub fn resolve_path>(path: &P) -> Result { components.next(); let current_dir = env::current_dir()?; - let drive_letter = get_drive_letter(¤t_dir).with_context(|| { - format!("could not get drive letter: {}", current_dir.display()) - })?; + let drive_letter = get_drive_letter(¤t_dir) + .with_context(|| format!("could not get drive letter: {}", current_dir.display()))?; base_path = get_drive_path(drive_letter); stack.extend(base_path.components()); } diff --git a/tests/completion.rs b/tests/completion.rs index 118e18e..f7c6c4c 100644 --- a/tests/completion.rs +++ b/tests/completion.rs @@ -6,12 +6,7 @@ use assert_cmd::Command; #[test] fn completions_bash() { let source = include_str!("../contrib/completions/zoxide.bash"); - Command::new("bash") - .args(&["--noprofile", "--norc", "-c", source]) - .assert() - .success() - .stdout("") - .stderr(""); + Command::new("bash").args(&["--noprofile", "--norc", "-c", source]).assert().success().stdout("").stderr(""); } // Elvish: the completions file uses editor commands to add completions to the diff --git a/xtask/Cargo.lock b/xtask/Cargo.lock new file mode 100644 index 0000000..c026cf8 --- /dev/null +++ b/xtask/Cargo.lock @@ -0,0 +1,170 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "clap" +version = "3.0.0-beta.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcd70aa5597dbc42f7217a543f9ef2768b2ef823ba29036072d30e1d88e98406" +dependencies = [ + "bitflags", + "clap_derive", + "indexmap", + "lazy_static", + "os_str_bytes", + "textwrap", + "vec_map", +] + +[[package]] +name = "clap_derive" +version = "3.0.0-beta.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5bb0d655624a0b8770d1c178fb8ffcb1f91cc722cb08f451e3dc72465421ac" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "os_str_bytes" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6acbef58a60fe69ab50510a55bc8cdd4d6cf2283d27ad338f54cb52747a9cf2d" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "textwrap" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" + +[[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "xtask" +version = "0.1.0" +dependencies = [ + "clap", +] diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 0000000..5926f49 --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { version = "=3.0.0-beta.4", default-features = false, features = [ + "derive", + "std", +] } diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 0000000..df53e81 --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,48 @@ +use clap::{ArgEnum, Clap}; + +use std::process::Command; + +#[derive(Clap, Debug)] +struct App { + #[clap(arg_enum)] + task: Task, + #[clap(long)] + nix: Option, +} + +#[derive(ArgEnum, Debug)] +enum Task { + CI, +} + +fn run(args: &[&str], nix: bool) { + let args_str = args.join(" "); + println!(">>> {}", args_str); + + let status = if nix { + Command::new("nix-shell").args(&["--pure", "--run", &args_str]).status() + } else { + let (cmd, args) = args.split_first().unwrap(); + Command::new(cmd).args(args).status() + }; + if !status.unwrap().success() { + panic!("command exited with an error"); + } +} + +fn main() { + let app = App::parse(); + let color = if std::env::var_os("CI").is_some() { "--color=always" } else { "--color=auto" }; + let nix = app.nix.unwrap_or_else(|| Command::new("nix-shell").arg("--version").output().is_ok()); + let run = |args: &[&str]| run(args, nix); + match app.task { + Task::CI => { + run(&["cargo", "fmt", "--", "--check", color, "--files-with-diff"]); + run(&["cargo", "check", "--all-features", color]); + run(&["cargo", "clippy", "--all-features", color, "--", "--deny=warnings", "--deny=clippy::all"]); + run(&["cargo", "test", if nix { "--all-features" } else { "" }, color, "--no-fail-fast"]); + // color: https://github.com/rustsec/rustsec/pull/436 + run(&["cargo", "audit", "--deny=warnings"]); + } + } +}