Replace make with xtask

This commit is contained in:
Ajeet D'Souza 2021-09-13 03:06:21 +05:30
parent 61cfb0281c
commit 02029aef98
16 changed files with 267 additions and 126 deletions

2
.cargo/config Normal file
View File

@ -0,0 +1,2 @@
[alias]
xtask = "run --manifest-path ./xtask/Cargo.toml --"

View File

@ -21,4 +21,4 @@ jobs:
if: ${{ matrix.os != 'windows-latest' }} if: ${{ matrix.os != 'windows-latest' }}
with: with:
nix_path: nixpkgs=https://github.com/NixOS/nixpkgs/archive/20.09.tar.gz 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' }}

View File

@ -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)

View File

@ -1,5 +1,6 @@
# group_imports = "StdExternalCrate" # group_imports = "StdExternalCrate"
# imports_granularity = "Module" # imports_granularity = "Module"
max_width = 120
newline_style = "Native" newline_style = "Native"
use_field_init_shorthand = true use_field_init_shorthand = true
use_small_heuristics = "Max" use_small_heuristics = "Max"

View File

@ -10,7 +10,7 @@ const ENV_HELP: &str = "ENVIRONMENT VARIABLES:
_ZO_MAXAGE Maximum total age after which entries start getting deleted _ZO_MAXAGE Maximum total age after which entries start getting deleted
_ZO_RESOLVE_SYMLINKS Resolve symlinks when storing paths"; _ZO_RESOLVE_SYMLINKS Resolve symlinks when storing paths";
#[derive(Debug, Clap)] #[derive(Clap, Debug)]
#[clap( #[clap(
bin_name = env!("CARGO_PKG_NAME"), bin_name = env!("CARGO_PKG_NAME"),
about, about,

View File

@ -22,11 +22,7 @@ impl Run for Add {
let mut db = db.open()?; let mut db = db.open()?;
for path in &self.paths { for path in &self.paths {
let path = if config::resolve_symlinks() { let path = if config::resolve_symlinks() { util::canonicalize(path) } else { util::resolve_path(path) }?;
util::canonicalize(path)
} else {
util::resolve_path(path)
}?;
let path = util::path_to_str(&path)?; let path = util::path_to_str(&path)?;
// Ignore path if it contains unsupported characters, or if it's in // Ignore path if it contains unsupported characters, or if it's in

View File

@ -8,9 +8,8 @@ use std::fs;
impl Run for Import { impl Run for Import {
fn run(&self) -> Result<()> { fn run(&self) -> Result<()> {
let buffer = fs::read_to_string(&self.path).with_context(|| { let buffer = fs::read_to_string(&self.path)
format!("could not open database for importing: {}", &self.path.display()) .with_context(|| format!("could not open database for importing: {}", &self.path.display()))?;
})?;
let data_dir = config::data_dir()?; let data_dir = config::data_dir()?;
let mut db = DatabaseFile::new(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 mut split = line.rsplitn(3, '|');
let last_accessed = split.next().with_context(|| format!("invalid entry: {}", line))?; let last_accessed = split.next().with_context(|| format!("invalid entry: {}", line))?;
let last_accessed = let last_accessed = last_accessed.parse().with_context(|| format!("invalid epoch: {}", last_accessed))?;
last_accessed.parse().with_context(|| format!("invalid epoch: {}", last_accessed))?;
let rank = split.next().with_context(|| format!("invalid entry: {}", line))?; let rank = split.next().with_context(|| format!("invalid entry: {}", line))?;
let rank = rank.parse().with_context(|| format!("invalid rank: {}", rank))?; let rank = rank.parse().with_context(|| format!("invalid rank: {}", rank))?;

View File

@ -45,8 +45,7 @@ pub fn exclude_dirs() -> Result<Vec<Pattern>> {
env::split_paths(&paths) env::split_paths(&paths)
.map(|path| { .map(|path| {
let pattern = path.to_str().context("invalid unicode in _ZO_EXCLUDE_DIRS")?; let pattern = path.to_str().context("invalid unicode in _ZO_EXCLUDE_DIRS")?;
Pattern::new(pattern) Pattern::new(pattern).with_context(|| format!("invalid glob in _ZO_EXCLUDE_DIRS: {}", pattern))
.with_context(|| format!("invalid glob in _ZO_EXCLUDE_DIRS: {}", pattern))
}) })
.collect() .collect()
}, },
@ -61,9 +60,8 @@ pub fn maxage() -> Result<Rank> {
match env::var_os("_ZO_MAXAGE") { match env::var_os("_ZO_MAXAGE") {
Some(maxage) => { Some(maxage) => {
let maxage = maxage.to_str().context("invalid unicode in _ZO_MAXAGE")?; let maxage = maxage.to_str().context("invalid unicode in _ZO_MAXAGE")?;
let maxage = maxage let maxage =
.parse::<u64>() maxage.parse::<u64>().with_context(|| format!("unable to parse _ZO_MAXAGE as integer: {}", maxage))?;
.with_context(|| format!("unable to parse _ZO_MAXAGE as integer: {}", maxage))?;
Ok(maxage as Rank) Ok(maxage as Rank)
} }
None => Ok(10000.0), None => Ok(10000.0),

View File

@ -25,21 +25,18 @@ impl<'file> Database<'file> {
} }
let buffer = self.dirs.to_bytes()?; let buffer = self.dirs.to_bytes()?;
let mut file = NamedTempFile::new_in(self.data_dir).with_context(|| { let mut file = NamedTempFile::new_in(self.data_dir)
format!("could not create temporary database in: {}", self.data_dir.display()) .with_context(|| format!("could not create temporary database in: {}", self.data_dir.display()))?;
})?;
// Preallocate enough space on the file, preventing copying later on. // Preallocate enough space on the file, preventing copying later on.
// This optimization may fail on some filesystems, but it is safe to // This optimization may fail on some filesystems, but it is safe to
// ignore it and proceed. // ignore it and proceed.
let _ = file.as_file().set_len(buffer.len() as _); let _ = file.as_file().set_len(buffer.len() as _);
file.write_all(&buffer).with_context(|| { file.write_all(&buffer)
format!("could not write to temporary database: {}", file.path().display()) .with_context(|| format!("could not write to temporary database: {}", file.path().display()))?;
})?;
let path = db_path(&self.data_dir); let path = db_path(&self.data_dir);
persist(file, &path) persist(file, &path).with_context(|| format!("could not replace database: {}", path.display()))?;
.with_context(|| format!("could not replace database: {}", path.display()))?;
self.modified = false; self.modified = false;
Ok(()) Ok(())
@ -51,11 +48,7 @@ impl<'file> Database<'file> {
match self.dirs.iter_mut().find(|dir| dir.path == path) { match self.dirs.iter_mut().find(|dir| dir.path == path) {
None => { None => {
self.dirs.push(Dir { self.dirs.push(Dir { path: path.to_string().into(), last_accessed: now, rank: 1.0 });
path: path.to_string().into(),
last_accessed: now,
rank: 1.0,
});
} }
Some(dir) => { Some(dir) => {
dir.last_accessed = now; dir.last_accessed = now;
@ -184,23 +177,19 @@ impl DatabaseFile {
match fs::read(&path) { match fs::read(&path) {
Ok(buffer) => { Ok(buffer) => {
self.buffer = buffer; self.buffer = buffer;
let dirs = DirList::from_bytes(&self.buffer).with_context(|| { let dirs = DirList::from_bytes(&self.buffer)
format!("could not deserialize database: {}", path.display()) .with_context(|| format!("could not deserialize database: {}", path.display()))?;
})?;
Ok(Database { dirs, modified: false, data_dir: &self.data_dir }) Ok(Database { dirs, modified: false, data_dir: &self.data_dir })
} }
Err(e) if e.kind() == io::ErrorKind::NotFound => { Err(e) if e.kind() == io::ErrorKind::NotFound => {
// Create data directory, but don't create any file yet. // Create data directory, but don't create any file yet.
// The file will be created later by [`Database::save`] // The file will be created later by [`Database::save`]
// if any data is modified. // if any data is modified.
fs::create_dir_all(&self.data_dir).with_context(|| { fs::create_dir_all(&self.data_dir)
format!("unable to create data directory: {}", self.data_dir.display()) .with_context(|| format!("unable to create data directory: {}", self.data_dir.display()))?;
})?;
Ok(Database { dirs: DirList::new(), modified: false, data_dir: &self.data_dir }) Ok(Database { dirs: DirList::new(), modified: false, data_dir: &self.data_dir })
} }
Err(e) => { Err(e) => Err(e).with_context(|| format!("could not read from database: {}", path.display())),
Err(e).with_context(|| format!("could not read from database: {}", path.display()))
}
} }
} }
} }

View File

@ -151,8 +151,7 @@ mod tests {
#[case(&["/foo/", "/bar"], "/foo/bar", false)] #[case(&["/foo/", "/bar"], "/foo/bar", false)]
#[case(&["/foo/", "/bar"], "/foo/baz/bar", true)] #[case(&["/foo/", "/bar"], "/foo/baz/bar", true)]
fn query(#[case] keywords: &[&str], #[case] path: &str, #[case] is_match: bool) { fn query(#[case] keywords: &[&str], #[case] path: &str, #[case] is_match: bool) {
let mut db = let mut db = Database { dirs: Vec::new().into(), modified: false, data_dir: &PathBuf::new() };
Database { dirs: Vec::new().into(), modified: false, data_dir: &PathBuf::new() };
let stream = db.stream(0).with_keywords(keywords); let stream = db.stream(0).with_keywords(keywords);
assert_eq!(is_match, stream.matches_keywords(path)); assert_eq!(is_match, stream.matches_keywords(path));
} }

View File

@ -50,13 +50,7 @@ mod tests {
) { ) {
let opts = Opts { cmd, hook, echo, resolve_symlinks }; let opts = Opts { cmd, hook, echo, resolve_symlinks };
let source = Bash(&opts).render().unwrap(); 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] #[rstest]
@ -110,19 +104,12 @@ mod tests {
// Filter out lines using edit:*, since those functions // Filter out lines using edit:*, since those functions
// are only available in the interactive editor. // are only available in the interactive editor.
for line in for line in Elvish(&opts).render().unwrap().split('\n').filter(|line| !line.contains("edit:")) {
Elvish(&opts).render().unwrap().split('\n').filter(|line| !line.contains("edit:"))
{
source.push_str(line); source.push_str(line);
source.push('\n'); source.push('\n');
} }
Command::new("elvish") Command::new("elvish").args(&["-c", &source, "-norc"]).assert().success().stdout("").stderr("");
.args(&["-c", &source, "-norc"])
.assert()
.success()
.stdout("")
.stderr("");
} }
#[rstest] #[rstest]
@ -184,12 +171,8 @@ mod tests {
let tempdir = tempfile::tempdir().unwrap(); let tempdir = tempfile::tempdir().unwrap();
let tempdir = tempdir.path().to_str().unwrap(); let tempdir = tempdir.path().to_str().unwrap();
let assert = Command::new("nu") let assert =
.env("HOME", tempdir) Command::new("nu").env("HOME", tempdir).args(&["--commands", &source]).assert().success().stderr("");
.args(&["--commands", &source])
.assert()
.success()
.stderr("");
if opts.hook != InitHook::Pwd { if opts.hook != InitHook::Pwd {
assert.stdout(""); assert.stdout("");
@ -302,12 +285,7 @@ mod tests {
let mut source = Xonsh(&opts).render().unwrap(); let mut source = Xonsh(&opts).render().unwrap();
source.push('\n'); source.push('\n');
Command::new("black") Command::new("black").args(&["--check", "--diff", "-"]).write_stdin(source).assert().success().stdout("");
.args(&["--check", "--diff", "-"])
.write_stdin(source)
.assert()
.success()
.stdout("");
} }
#[rstest] #[rstest]

View File

@ -7,8 +7,7 @@ use std::path::{Component, Path, PathBuf};
use std::time::SystemTime; use std::time::SystemTime;
pub fn canonicalize<P: AsRef<Path>>(path: &P) -> Result<PathBuf> { pub fn canonicalize<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
dunce::canonicalize(path) dunce::canonicalize(path).with_context(|| format!("could not resolve path: {}", path.as_ref().display()))
.with_context(|| format!("could not resolve path: {}", path.as_ref().display()))
} }
pub fn current_dir() -> Result<PathBuf> { pub fn current_dir() -> Result<PathBuf> {
@ -16,10 +15,8 @@ pub fn current_dir() -> Result<PathBuf> {
} }
pub fn current_time() -> Result<Epoch> { pub fn current_time() -> Result<Epoch> {
let current_time = SystemTime::now() let current_time =
.duration_since(SystemTime::UNIX_EPOCH) SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).context("system clock set to invalid time")?.as_secs();
.context("system clock set to invalid time")?
.as_secs();
Ok(current_time) Ok(current_time)
} }
@ -51,9 +48,7 @@ pub fn resolve_path<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
match components.next() { match components.next() {
Some(Component::Prefix(prefix)) => match prefix.kind() { Some(Component::Prefix(prefix)) => match prefix.kind() {
Prefix::Disk(drive_letter) | Prefix::VerbatimDisk(drive_letter) => { Prefix::Disk(drive_letter) | Prefix::VerbatimDisk(drive_letter) => Some(drive_letter),
Some(drive_letter)
}
_ => None, _ => None,
}, },
_ => None, _ => None,
@ -106,9 +101,8 @@ pub fn resolve_path<P: AsRef<Path>>(path: &P) -> Result<PathBuf> {
components.next(); components.next();
let current_dir = env::current_dir()?; let current_dir = env::current_dir()?;
let drive_letter = get_drive_letter(&current_dir).with_context(|| { let drive_letter = get_drive_letter(&current_dir)
format!("could not get drive letter: {}", current_dir.display()) .with_context(|| format!("could not get drive letter: {}", current_dir.display()))?;
})?;
base_path = get_drive_path(drive_letter); base_path = get_drive_path(drive_letter);
stack.extend(base_path.components()); stack.extend(base_path.components());
} }

View File

@ -6,12 +6,7 @@ use assert_cmd::Command;
#[test] #[test]
fn completions_bash() { fn completions_bash() {
let source = include_str!("../contrib/completions/zoxide.bash"); let source = include_str!("../contrib/completions/zoxide.bash");
Command::new("bash") Command::new("bash").args(&["--noprofile", "--norc", "-c", source]).assert().success().stdout("").stderr("");
.args(&["--noprofile", "--norc", "-c", source])
.assert()
.success()
.stdout("")
.stderr("");
} }
// Elvish: the completions file uses editor commands to add completions to the // Elvish: the completions file uses editor commands to add completions to the

170
xtask/Cargo.lock generated Normal file
View File

@ -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",
]

12
xtask/Cargo.toml Normal file
View File

@ -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",
] }

48
xtask/src/main.rs Normal file
View File

@ -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<bool>,
}
#[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"]);
}
}
}