diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8625534..d7d8ee8 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -2,6 +2,10 @@ name: Run CI/CD Jobs on: push: + branches: + - '**' + tags-ignore: + - '**' paths: - ".github/workflows/**.yml" - "**/Cargo.toml" diff --git a/rustfmt.toml b/rustfmt.toml index d14b82d..eda3988 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,3 +1,4 @@ # see: https://rust-lang.github.io/rustfmt/ -reorder_modules = false -reorder_imports = false +# +# reorder_modules = false +# reorder_imports = false diff --git a/xtask/src/cargo.rs b/xtask/src/cargo.rs new file mode 100644 index 0000000..c5ef6f9 --- /dev/null +++ b/xtask/src/cargo.rs @@ -0,0 +1,323 @@ +use crate::exec::{EnvVars, Execute}; +use crate::options::Options; +use duct::Expression; +use std::collections::HashMap; +use std::env; +use std::error::Error; +use std::ffi::OsString; +use std::path::PathBuf; + +type DynError = Box; + +#[derive(Clone, Debug, PartialEq)] +pub struct Cargo<'a> { + pub bin: String, + opts: &'a Options, +} + +impl<'a> Execute for Cargo<'a> { + fn bin(&self) -> String { + self.bin.to_owned() + } + + fn opts(&self) -> &Options { + self.opts + } +} + +impl<'a> Cargo<'a> { + pub fn new(opts: &'a Options) -> Cargo<'a> { + let bin = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); + Cargo { bin, opts } + } + + pub fn workspace_path(&self) -> Result { + let (args, envs) = self.workspace_path_params(); + let stdout = self.exec_safe(args, envs).read()?; + Ok(PathBuf::from(stdout.replace("Cargo.toml", "").trim())) + } + + fn workspace_path_params(&self) -> (Vec, EnvVars) { + let args = self.build_args( + ["locate-project", "--workspace", "--message-format", "plain"], + [""], + ); + (args, None) + } + + pub fn create(&self, path: P, arguments: U) -> Expression + where + P: Into, + U: IntoIterator, + U::Item: Into, + { + let (args, envs) = self.create_params(path, arguments); + self.exec_unsafe(args, envs) + } + + fn create_params(&self, path: P, arguments: U) -> (Vec, EnvVars) + where + P: Into, + U: IntoIterator, + U::Item: Into, + { + let args = self.build_args(["new".into(), path.into()], arguments); + (args, None) + } + + pub fn install(&self, arguments: U) -> Expression + where + U: IntoIterator, + U::Item: Into, + { + let (args, envs) = self.install_params(arguments); + self.exec_unsafe(args, envs) + } + + fn install_params(&self, arguments: U) -> (Vec, EnvVars) + where + U: IntoIterator, + U::Item: Into, + { + let args = self.build_args([OsString::from("install")], arguments); + (args, None) + } + + pub fn build(&self, arguments: U) -> Expression + where + U: IntoIterator, + U::Item: Into, + { + let (args, envs) = self.build_params(arguments); + self.exec_safe(args, envs) + } + + fn build_params(&self, arguments: U) -> (Vec, EnvVars) + where + U: IntoIterator, + U::Item: Into, + { + let args = self.build_args([OsString::from("build")], arguments); + (args, None) + } + + pub fn clean(&self, arguments: U) -> Expression + where + U: IntoIterator, + U::Item: Into, + { + let (args, envs) = self.clean_params(arguments); + self.exec_unsafe(args, envs) + } + + fn clean_params(&self, arguments: U) -> (Vec, EnvVars) + where + U: IntoIterator, + U::Item: Into, + { + let args = self.build_args([OsString::from("clean")], arguments); + (args, None) + } + + pub fn test(&self, arguments: U) -> Expression + where + U: IntoIterator, + U::Item: Into, + { + let (args, envs) = self.test_params(arguments); + self.exec_safe(args, envs) + } + + fn test_params(&self, arguments: U) -> (Vec, EnvVars) + where + U: IntoIterator, + U::Item: Into, + { + let args = self.build_args([OsString::from("test")], arguments); + (args, None) + } + + pub fn coverage

(&self, path: P) -> Expression + where + P: Into, + { + let (args, envs) = self.coverage_params(path); + self.exec_unsafe(args, envs) + } + + fn coverage_params

(&self, path: P) -> (Vec, EnvVars) + where + P: Into, + { + let mut profile_ptn: OsString = path.into(); + profile_ptn.push("/cargo-test-%p-%m.profraw"); + let args = self.build_args([OsString::from("test")], [""]); + let envs = HashMap::from([ + ("CARGO_INCREMENTAL".into(), "0".into()), + ("RUSTFLAGS".into(), "-Cinstrument-coverage".into()), + ("LLVM_PROFILE_FILE".into(), profile_ptn), + ]); + + (args, Some(envs)) + } + + pub fn lint(&self) -> Expression { + let (args, envs) = self.lint_params(); + self.exec_safe(args, envs) + } + + fn lint_params(&self) -> (Vec, EnvVars) { + let args = self.build_args( + [OsString::from("clippy")], + ["--all-targets", "--all-features", "--no-deps"], + ); + let envs = HashMap::from([("RUSTFLAGS".into(), "-Dwarnings".into())]); + + (args, Some(envs)) + } + + pub fn doc(&self, arguments: U) -> Expression + where + U: IntoIterator, + U::Item: Into, + { + let (args, envs) = self.doc_params(arguments); + self.exec_unsafe(args, envs) + } + + fn doc_params(&self, arguments: U) -> (Vec, EnvVars) + where + U: IntoIterator, + U::Item: Into, + { + let args = self.build_args([OsString::from("doc")], arguments); + (args, None) + } + + pub fn publish_package>(&self, name: N) -> Expression { + let (args, envs) = self.publish_package_params(name); + self.exec_unsafe(args, envs) + } + + fn publish_package_params>(&self, name: N) -> (Vec, EnvVars) { + let args = self.build_args(["publish", "--package", name.as_ref()], [""]); + (args, None) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::task_flags; + + #[test] + fn it_builds_args_for_getting_workspace_path() { + let opts = Options::new(vec![], task_flags! {}).unwrap(); + let cargo = Cargo::new(&opts); + let (args, envs) = cargo.workspace_path_params(); + assert_eq!( + args, + ["locate-project", "--workspace", "--message-format", "plain"] + ); + assert_eq!(envs, None); + } + + #[test] + fn it_builds_args_for_the_create_subcommand() { + let opts = Options::new(vec![], task_flags! {}).unwrap(); + let cargo = Cargo::new(&opts); + let path = PathBuf::from("fake-crate-path"); + let (args, envs) = cargo.create_params(path, ["--name", "my-crate", "--lib"]); + assert_eq!( + args, + ["new", "fake-crate-path", "--name", "my-crate", "--lib"] + ); + assert_eq!(envs, None); + } + + #[test] + fn it_builds_args_for_the_install_subcommand() { + let opts = Options::new(vec![], task_flags! {}).unwrap(); + let cargo = Cargo::new(&opts); + let (args, envs) = cargo.install_params(["grcov"]); + assert_eq!(args, ["install", "grcov"]); + assert_eq!(envs, None); + } + + #[test] + fn it_builds_args_for_the_build_subcommand() { + let opts = Options::new(vec![], task_flags! {}).unwrap(); + let cargo = Cargo::new(&opts); + let (args, envs) = cargo.build_params(["--release"]); + assert_eq!(args, ["build", "--release"]); + assert_eq!(envs, None); + } + + #[test] + fn it_builds_args_for_the_clean_subcommand() { + let opts = Options::new(vec![], task_flags! {}).unwrap(); + let cargo = Cargo::new(&opts); + let (args, envs) = cargo.clean_params(["--release"]); + assert_eq!(args, ["clean", "--release"]); + assert_eq!(envs, None); + } + + #[test] + fn it_builds_args_for_the_test_subcommand() { + let opts = Options::new(vec![], task_flags! {}).unwrap(); + let cargo = Cargo::new(&opts); + let (args, envs) = cargo.test_params(["--doc"]); + assert_eq!(args, ["test", "--doc"]); + assert_eq!(envs, None); + } + + #[test] + fn it_builds_args_for_the_coverage_subcommand() { + let opts = Options::new(vec![], task_flags! {}).unwrap(); + let cargo = Cargo::new(&opts); + let path = PathBuf::from("fake-coverage-path"); + let (args, envs) = cargo.coverage_params(path); + let expected_envs = HashMap::from([ + ("CARGO_INCREMENTAL".into(), "0".into()), + ("RUSTFLAGS".into(), "-Cinstrument-coverage".into()), + ( + "LLVM_PROFILE_FILE".into(), + "fake-coverage-path/cargo-test-%p-%m.profraw".into(), + ), + ]); + + assert_eq!(args, ["test"]); + assert_eq!(envs, Some(expected_envs)); + } + + #[test] + fn it_builds_args_for_the_lint_subcommand() { + let opts = Options::new(vec![], task_flags! {}).unwrap(); + let cargo = Cargo::new(&opts); + let (args, envs) = cargo.lint_params(); + let expected_envs = HashMap::from([("RUSTFLAGS".into(), "-Dwarnings".into())]); + assert_eq!( + args, + ["clippy", "--all-targets", "--all-features", "--no-deps"] + ); + assert_eq!(envs, Some(expected_envs)); + } + + #[test] + fn it_builds_args_for_the_doc_subcommand() { + let opts = Options::new(vec![], task_flags! {}).unwrap(); + let cargo = Cargo::new(&opts); + let (args, envs) = cargo.doc_params(["--workspace", "--no-deps"]); + assert_eq!(args, ["doc", "--workspace", "--no-deps"]); + assert_eq!(envs, None); + } + + #[test] + fn it_builds_args_for_the_publish_package_subcommand() { + let opts = Options::new(vec![], task_flags! {}).unwrap(); + let cargo = Cargo::new(&opts); + let (args, envs) = cargo.publish_package_params("my-crate"); + assert_eq!(args, ["publish", "--package", "my-crate"]); + assert_eq!(envs, None); + } +} diff --git a/xtask/src/exec.rs b/xtask/src/exec.rs new file mode 100644 index 0000000..a2d571e --- /dev/null +++ b/xtask/src/exec.rs @@ -0,0 +1,100 @@ +use crate::options::Options; +use duct::{cmd, Expression}; +use std::collections::HashMap; +use std::ffi::OsString; + +pub type EnvVars = Option>; + +pub trait Execute { + fn bin(&self) -> String; + + fn opts(&self) -> &Options; + + fn exec_safe(&self, args: Vec, envs: EnvVars) -> Expression { + if envs.is_none() { + return cmd(self.bin(), args); + } + + let envs = envs.unwrap(); + let mut exp = cmd(self.bin(), args); + + for (key, value) in envs.iter() { + exp = exp.env(key, value); + } + + exp + } + + fn exec_unsafe(&self, args: Vec, envs: EnvVars) -> Expression { + if self.opts().has("dry-run") { + let mut args = args.clone(); + args.insert(0, "skipping:".into()); + args.insert(1, self.bin().into()); + // TODO (busticated): windows? see: https://stackoverflow.com/a/61857874/579167 + return cmd("echo", args); + } + + self.exec_safe(args, envs) + } + + fn build_args(&self, args1: U, args2: UU) -> Vec + where + U: IntoIterator, + U::Item: Into, + UU: IntoIterator, + UU::Item: Into, + { + let mut args = args1 + .into_iter() + .map(Into::::into) + .collect::>(); + + args.extend( + args2 + .into_iter() + .map(Into::::into) + .collect::>(), + ); + + args.retain(|a| !a.is_empty()); + args + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::task_flags; + + struct TestExecutable { + bin: String, + opts: Options, + } + + impl Execute for TestExecutable { + fn bin(&self) -> String { + self.bin.to_owned() + } + + fn opts(&self) -> &Options { + &self.opts + } + } + + impl TestExecutable { + fn new(opts: Options) -> Self { + TestExecutable { + bin: "test".to_string(), + opts, + } + } + } + + #[test] + fn it_builds_args() { + let opts = Options::new(vec![], task_flags! {}).unwrap(); + let fake = TestExecutable::new(opts); + let args = fake.build_args(["one"], ["two", "three"]); + assert_eq!(args, ["one", "two", "three"]); + } +} diff --git a/xtask/src/fs.rs b/xtask/src/fs.rs new file mode 100644 index 0000000..28f83b5 --- /dev/null +++ b/xtask/src/fs.rs @@ -0,0 +1,62 @@ +use crate::options::Options; +use std::fs; +use std::path::Path; + +type IOResult = std::io::Result<()>; + +#[derive(Clone, Debug, PartialEq)] +pub struct FS<'a> { + opts: &'a Options, +} + +impl<'a> FS<'a> { + pub fn new(opts: &'a Options) -> FS<'a> { + FS { opts } + } + + pub fn write, D: AsRef<[u8]>>(&self, path: P, data: D) -> IOResult { + if self.opts.has("dry-run") { + let path = path.as_ref().to_string_lossy(); + println!("Skipping: write {}", path); + return Ok(()); + } + + fs::write(path, data) + } + + pub fn remove_dir_all>(&self, path: P) -> IOResult { + if self.opts.has("dry-run") { + let path = path.as_ref().to_string_lossy(); + println!("Skipping: remove_dir_all {}", path); + return Ok(()); + } + + fs::remove_dir_all(path) + } + + pub fn create_dir_all>(&self, path: P) -> IOResult { + if self.opts.has("dry-run") { + let path = path.as_ref().to_string_lossy(); + println!("Skipping: create_dir_all {}", path); + return Ok(()); + } + + fs::create_dir_all(path) + } + + pub fn read_dir>(&self, path: P) -> std::io::Result { + fs::read_dir(path) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::task_flags; + + #[test] + fn it_initializes() { + let opts = Options::new(vec![], task_flags! {}).unwrap(); + let _ = FS::new(&opts); + } +} diff --git a/xtask/src/git.rs b/xtask/src/git.rs index 0854c3d..39e7afd 100644 --- a/xtask/src/git.rs +++ b/xtask/src/git.rs @@ -1,52 +1,29 @@ +use crate::exec::Execute; use crate::options::Options; -use duct::{cmd, Expression}; +use duct::Expression; use std::ffi::OsString; use std::path::Path; #[derive(Clone, Debug, PartialEq)] pub struct Git<'a> { + pub bin: String, opts: &'a Options, } -impl<'a> Git<'a> { - pub fn new(opts: &'a Options) -> Git<'a> { - Git { opts } +impl<'a> Execute for Git<'a> { + fn bin(&self) -> String { + self.bin.to_owned() } - pub fn cmd(&self, args: Vec) -> Expression { - let mut args = args.clone(); - - if self.opts.has("dry-run") { - args.insert(0, "skipping:".into()); - args.insert(1, "git".into()); - // TODO (mirande): windows? see: https://stackoverflow.com/a/61857874/579167 - return cmd("echo", args); - } - - cmd("git", args) + fn opts(&self) -> &Options { + self.opts } +} - fn build_args(&self, args1: U, args2: UU) -> Vec - where - U: IntoIterator, - U::Item: Into, - UU: IntoIterator, - UU::Item: Into, - { - let mut args = args1 - .into_iter() - .map(Into::::into) - .collect::>(); - - args.extend( - args2 - .into_iter() - .map(Into::::into) - .collect::>(), - ); - - args.retain(|a| !a.is_empty()); - args +impl<'a> Git<'a> { + pub fn new(opts: &'a Options) -> Git<'a> { + let bin = "git".to_string(); + Git { bin, opts } } pub fn add(&self, path: P, arguments: U) -> Expression @@ -55,18 +32,18 @@ impl<'a> Git<'a> { U: IntoIterator, U::Item: Into, { - let args = self.add_raw(path, arguments); - self.cmd(args) + let args = self.add_params(path, arguments); + self.exec_unsafe(args, None) } - fn add_raw(&self, path: P, arguments: U) -> Vec + fn add_params(&self, path: P, arguments: U) -> Vec where P: AsRef, U: IntoIterator, U::Item: Into, { self.build_args( - vec![OsString::from("add"), path.as_ref().to_owned().into()], + [OsString::from("add"), path.as_ref().to_owned().into()], arguments, ) } @@ -77,17 +54,17 @@ impl<'a> Git<'a> { U: IntoIterator, U::Item: Into, { - let args = self.commit_raw(message, arguments); - self.cmd(args) + let args = self.commit_params(message, arguments); + self.exec_unsafe(args, None) } - fn commit_raw(&self, message: M, arguments: U) -> Vec + fn commit_params(&self, message: M, arguments: U) -> Vec where M: AsRef, U: IntoIterator, U::Item: Into, { - self.build_args(vec!["commit", "--message", message.as_ref()], arguments) + self.build_args(["commit", "--message", message.as_ref()], arguments) } pub fn tag(&self, tag: T, arguments: U) -> Expression @@ -96,19 +73,62 @@ impl<'a> Git<'a> { U: IntoIterator, U::Item: Into, { - let args = self.tag_raw(tag, arguments); - self.cmd(args) + let args = self.tag_params(tag, arguments); + self.exec_unsafe(args, None) } - fn tag_raw(&self, tag: T, arguments: U) -> Vec + fn tag_params(&self, tag: T, arguments: U) -> Vec where T: AsRef, U: IntoIterator, U::Item: Into, { + self.build_args(["tag", tag.as_ref(), "--message", tag.as_ref()], arguments) + } + + pub fn get_tags(&self, arguments: U) -> Expression + where + U: IntoIterator, + U::Item: Into, + { + let args = self.get_tags_params(arguments); + self.exec_safe(args, None) + } + + fn get_tags_params(&self, arguments: U) -> Vec + where + U: IntoIterator, + U::Item: Into, + { + self.build_args(["tag"], arguments) + } + + pub fn todos(&self) -> Expression { + let args = self.todos_params(); + self.exec_safe(args, None) + } + + fn todos_params(&self) -> Vec { + let ptn = r"TODO\s?\(.*\)|todo!\(\)"; + self.build_args( - vec!["tag", tag.as_ref(), "--message", tag.as_ref()], - arguments, + [ + "grep", + "-P", + "-e", + ptn, + "--ignore-case", + "--heading", + "--break", + "--context", + "2", + "--full-name", + "--line-number", + "--", + ":!./target/*", + ":!./tmp/*", + ], + [""], ) } } @@ -116,28 +136,14 @@ impl<'a> Git<'a> { #[cfg(test)] mod tests { use super::*; - use std::path::Path; use crate::task_flags; - - #[test] - fn it_initializes() { - let opts = Options::new(vec![], task_flags! {}).unwrap(); - let _ = Git::new(&opts); - } - - #[test] - fn it_builds_args() { - let opts = Options::new(vec![], task_flags! {}).unwrap(); - let git = Git::new(&opts); - let args = git.build_args(["one"], vec!["two", "three"]); - assert_eq!(args, ["one", "two", "three"]); - } + use std::path::Path; #[test] fn it_builds_args_for_the_add_subcommand() { let opts = Options::new(vec![], task_flags! {}).unwrap(); let git = Git::new(&opts); - let args = git.add_raw(Path::new("path/to/file"), [""]); + let args = git.add_params(Path::new("path/to/file"), [""]); assert_eq!(args, ["add", "path/to/file"]); } @@ -145,7 +151,7 @@ mod tests { fn it_builds_args_for_the_commit_subcommand() { let opts = Options::new(vec![], task_flags! {}).unwrap(); let git = Git::new(&opts); - let args = git.commit_raw("my message", ["--one", "--two"]); + let args = git.commit_params("my message", ["--one", "--two"]); assert_eq!( args, ["commit", "--message", "my message", "--one", "--two"] @@ -156,10 +162,44 @@ mod tests { fn it_builds_args_for_the_tag_subcommand() { let opts = Options::new(vec![], task_flags! {}).unwrap(); let git = Git::new(&opts); - let args = git.tag_raw("my tag", ["--one", "--two"]); + let args = git.tag_params("my tag", ["--one", "--two"]); assert_eq!( args, ["tag", "my tag", "--message", "my tag", "--one", "--two"] ); } + + #[test] + fn it_builds_args_for_getting_tags() { + let opts = Options::new(vec![], task_flags! {}).unwrap(); + let git = Git::new(&opts); + let args = git.get_tags_params(["--points-at", "HEAD"]); + assert_eq!(args, ["tag", "--points-at", "HEAD"]); + } + + #[test] + fn it_builds_args_for_getting_todos() { + let opts = Options::new(vec![], task_flags! {}).unwrap(); + let git = Git::new(&opts); + let args = git.todos_params(); + assert_eq!( + args, + [ + "grep", + "-P", + "-e", + r"TODO\s?\(.*\)|todo!\(\)", + "--ignore-case", + "--heading", + "--break", + "--context", + "2", + "--full-name", + "--line-number", + "--", + ":!./target/*", + ":!./tmp/*" + ] + ); + } } diff --git a/xtask/src/krate.rs b/xtask/src/krate.rs index 1e28694..904793f 100644 --- a/xtask/src/krate.rs +++ b/xtask/src/krate.rs @@ -1,11 +1,11 @@ +use crate::fs::FS; use crate::readme::Readme; use crate::toml::Toml; +use semver::Version; use std::error::Error; use std::fmt::{Display, Formatter}; -use std::fs; use std::path::PathBuf; use std::str::FromStr; -use semver::Version; type DynError = Box; @@ -107,12 +107,18 @@ impl Krate { Ok(()) } - pub fn clean(&self) -> Result<(), DynError> { - Ok(fs::remove_dir_all(self.tmp_path())?) + pub fn clean(&self, fs: &FS) -> Result<(), DynError> { + use std::io::ErrorKind; + + match fs.remove_dir_all(self.tmp_path()) { + Err(e) if e.kind() == ErrorKind::NotFound => Ok(()), + Err(e) => Err(Box::new(e)), + Ok(()) => Ok(()), + } } - pub fn create_dirs(&self) -> Result<(), DynError> { - Ok(fs::create_dir_all(self.coverage_path())?) + pub fn create_dirs(&self, fs: &FS) -> Result<(), DynError> { + Ok(fs.create_dir_all(self.coverage_path())?) } } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 41df5c5..329d2d3 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,3 +1,6 @@ +mod cargo; +mod exec; +mod fs; mod git; mod krate; mod options; @@ -7,14 +10,12 @@ mod tasks; mod toml; mod workspace; -use crate::git::Git; use crate::krate::{Krate, KratePaths}; -use crate::tasks::{Task, Tasks}; use crate::semver::VersionChoice; -use crate::workspace::Workspace; +use crate::tasks::{Task, Tasks}; use duct::cmd; -use inquire::required; use inquire::list_option::ListOption as InquireListOption; +use inquire::required; use inquire::validator::Validation as InquireValidation; use inquire::{MultiSelect as InquireMultiSelect, Select as InquireSelect, Text as InquireText}; use regex::RegexBuilder; @@ -54,13 +55,8 @@ fn try_main() -> Result<(), DynError> { let tasks = init_tasks(); match tasks.get(cmd.clone()) { + Some(task) => task.exec(args, &tasks), None => print_help(cmd, args, tasks), - Some(task) => { - let cargo_cmd = get_cargo_cmd(); - let root_path = get_root_path(&cargo_cmd)?; - let mut workspace = Workspace::from_path(cargo_cmd, root_path)?; - task.exec(args, &mut workspace, &tasks) - } } } @@ -88,7 +84,7 @@ fn init_tasks() -> Tasks { name: "ci".into(), description: "run checks for CI".into(), flags: task_flags! {}, - run: |_opts, workspace, tasks| { + run: |_opts, _fs, _git, _cargo, _workspace, tasks| { println!(":::::::::::::::::::::::::::::::::"); println!(":::: Checking Project for CI ::::"); println!(":::::::::::::::::::::::::::::::::"); @@ -97,11 +93,11 @@ fn init_tasks() -> Tasks { tasks .get("lint") .unwrap() - .exec(vec![], workspace, tasks)?; + .exec(vec![], tasks)?; tasks .get("coverage") .unwrap() - .exec(vec![], workspace, tasks)?; + .exec(vec![], tasks)?; println!(":::: Done!"); println!(); @@ -112,15 +108,14 @@ fn init_tasks() -> Tasks { name: "clean".into(), description: "delete temporary files".into(), flags: task_flags! {}, - run: |_opts, workspace, _tasks| { + run: |_opts, fs, _git, cargo, workspace, _tasks| { println!("::::::::::::::::::::::::::::"); println!(":::: Cleaning Workspace ::::"); println!("::::::::::::::::::::::::::::"); println!(); - workspace.clean().unwrap_or(()); // ignore error - workspace.create_dirs()?; - cmd!(&workspace.cargo_cmd, "clean", "--release").run()?; + workspace.clean(&fs, &cargo)?; + workspace.create_dirs(&fs)?; println!(":::: Done!"); println!(); @@ -138,7 +133,7 @@ fn init_tasks() -> Tasks { flags: task_flags! { "open" => "open coverage report for viewing" }, - run: |opts, workspace, tasks| { + run: |opts, _fs, _git, cargo, _workspace, tasks| { println!("::::::::::::::::::::::::::::::"); println!(":::: Calculating Coverage ::::"); println!("::::::::::::::::::::::::::::::"); @@ -147,16 +142,8 @@ fn init_tasks() -> Tasks { let coverage_root = PathBuf::from("tmp/coverage").display().to_string(); let report = format!("{}/html/index.html", &coverage_root); - tasks.get("clean").unwrap().exec(vec![], workspace, tasks)?; - - cmd!(&workspace.cargo_cmd, "test") - .env("CARGO_INCREMENTAL", "0") - .env("RUSTFLAGS", "-Cinstrument-coverage") - .env( - "LLVM_PROFILE_FILE", - format!("{}/cargo-test-%p-%m.profraw", &coverage_root), - ) - .run()?; + tasks.get("clean").unwrap().exec(vec![], tasks)?; + cargo.coverage(&coverage_root).run()?; println!(":::: Done!"); println!(); @@ -189,23 +176,23 @@ fn init_tasks() -> Tasks { ) .run()?; - if opts.has("open"){ - cmd!("open", report).run()?; - } else { - println!(":::: Done!"); - println!(":::: Report: {}", report); - println!(); + cmd!("open", &report).run()?; } + println!(":::: Done!"); + println!(":::: Report: {}", report); + println!(); Ok(()) }, }, Task { name: "crate:add".into(), description: "add new crate to workspace".into(), - flags: task_flags! {}, - run: |_opts, workspace, _tasks| { + flags: task_flags! { + "dry-run" => "run thru steps but do not create new crate" + }, + run: |_opts, fs, _git, cargo, workspace, _tasks| { println!(":::::::::::::::::::"); println!(":::: Add Crate ::::"); println!(":::::::::::::::::::"); @@ -235,12 +222,18 @@ fn init_tasks() -> Tasks { let question = InquireSelect::new("Crate type?", vec!["--lib", "--bin"]); let kind_flag = question.prompt()?; + let krate = Krate::new( + kind_flag, + "0.1.0", + &name, + description, + workspace.krates_path().join(&name) + ); - workspace.add_krate(kind_flag, "0.1.0", &name, &description)?; + workspace.add_krate(&fs, &cargo, krate)?; println!(":::: Done!"); println!(); - Ok(()) }, }, @@ -248,13 +241,13 @@ fn init_tasks() -> Tasks { name: "crate:list".into(), description: "list workspace crates".into(), flags: task_flags! {}, - run: |_opts, workspace, _tasks| { + run: |_opts, fs, _git, _cargo, workspace, _tasks| { println!("::::::::::::::::::::::::::"); println!(":::: Available Crates ::::"); println!("::::::::::::::::::::::::::"); println!(); - let krates = workspace.krates()?; + let krates = workspace.krates(&fs)?; for krate in krates.values() { let kind = krate.kind.to_string().replace('-', ""); @@ -264,7 +257,6 @@ fn init_tasks() -> Tasks { println!(); println!(":::: Done!"); println!(); - Ok(()) }, }, @@ -274,14 +266,14 @@ fn init_tasks() -> Tasks { flags: task_flags! { "dry-run" => "run thru steps but do not publish" }, - run: |opts, workspace, _tasks| { + run: |_opts, fs, git, cargo, workspace, _tasks| { println!(":::::::::::::::::::::::::::"); println!(":::: Publishing Crates ::::"); println!(":::::::::::::::::::::::::::"); println!(); - let krates = workspace.krates()?; - let tag_text = cmd!("git", "tag", "--points-at", "HEAD").read()?; + let krates = workspace.krates(&fs)?; + let tag_text = git.get_tags(["--points-at", "HEAD"]).read()?; let mut tags = vec![]; for line in tag_text.lines() { @@ -301,13 +293,8 @@ fn init_tasks() -> Tasks { let (name, _ver) = tag.split_once('@').unwrap_or_else(|| panic!("Invalid Tag: `{}`!", tag)); let krate = krates.get(name).unwrap_or_else(|| panic!("Could Not Find Crate: `{}`!", name)); let message = format!("Publishing: {} at v{}", &krate.name, &krate.version); - - if opts.has("dry-run") { - println!("{} [skip]", &message); - } else { - println!("{}", &message); - cmd!(&workspace.cargo_cmd, "publish", "--package", &krate.name).run()?; - } + println!("{}", &message); + cargo.publish_package(&krate.name).run()?; } println!(); @@ -322,14 +309,13 @@ fn init_tasks() -> Tasks { flags: task_flags! { "dry-run" => "run thru steps but do not save changes" }, - run: |opts, workspace, _tasks| { + run: |_opts, fs, git, _cargo, workspace, _tasks| { println!("::::::::::::::::::::::::::"); println!(":::: Releasing Crates ::::"); println!("::::::::::::::::::::::::::"); println!(); - let git = Git::new(&opts); - let mut krates = workspace.krates()?; + let mut krates = workspace.krates(&fs)?; let question = InquireMultiSelect::new("Which crates should be published?", krates.keys().cloned().collect()); let to_publish = question .with_validator(|selections: &[InquireListOption<&String>]| { @@ -351,11 +337,7 @@ fn init_tasks() -> Tasks { let question = InquireSelect::new(&message, options); let choice = question.prompt()?; krate.set_version(choice.get_version())?; - if opts.has("dry-run") { - println!("Skipping: Version bump for {}", krate.toml.path.display()); - } else { - krate.toml.save()?; - } + krate.toml.save(&fs)?; git.add(&krate.toml.path, [""]).run()?; } @@ -376,19 +358,18 @@ fn init_tasks() -> Tasks { name: "dist".into(), description: "create release artifacts".into(), flags: task_flags! {}, - run: |_opts, workspace, _tasks| { + run: |_opts, _fs, _git, cargo, workspace, _tasks| { println!(":::::::::::::::::::::::::::::::::::::::::::"); println!(":::: Building Project for Distribution ::::"); println!(":::::::::::::::::::::::::::::::::::::::::::"); println!(); let dist_dir = workspace.path().join("target/release"); - cmd!(&workspace.cargo_cmd, "build", "--release").run()?; + cargo.build(["--release"]).run()?; println!(":::: Done!"); println!(":::: Artifacts: {}", dist_dir.display()); println!(); - Ok(()) }, }, @@ -396,19 +377,20 @@ fn init_tasks() -> Tasks { name: "doc".into(), description: "build project documentation".into(), flags: task_flags! { + "dry-run" => "run thru steps but do not generate docs", "open" => "open rendered docs for viewing" }, - run: |opts, workspace, _tasks| { + run: |opts, fs, _git, cargo, mut workspace, _tasks| { println!(":::::::::::::::::::::::::::"); println!(":::: Building All Docs ::::"); println!(":::::::::::::::::::::::::::"); println!(); println!(":::: Updating Workspace README..."); - let krates = workspace.krates()?; + let krates = workspace.krates(&fs)?; let readme_path = workspace.readme.path.clone(); - workspace.readme.update_crates_list(krates)?; + workspace.readme.update_crates_list(&fs, krates)?; println!(":::: Done: {:?}", readme_path); println!(); @@ -420,22 +402,21 @@ fn init_tasks() -> Tasks { println!(":::: Testing Examples..."); println!(); - cmd!(&workspace.cargo_cmd, "test", "--doc").run()?; + cargo.test(["--doc"]).run()?; println!(":::: Rendering Docs..."); println!(); - let mut args = vec!["doc", "--workspace", "--no-deps"]; + let mut args = vec!["--workspace", "--no-deps"]; if opts.has("open") { args.push("--open"); } - cmd(&workspace.cargo_cmd, args).run()?; + cargo.doc(args).run()?; println!(":::: Done!"); println!(); - Ok(()) }, }, @@ -443,25 +424,16 @@ fn init_tasks() -> Tasks { name: "lint".into(), description: "run the linter (clippy)".into(), flags: task_flags! {}, - run: |_opts, workspace, _tasks| { + run: |_opts, _fs, _git, cargo, _workspace, _tasks| { println!(":::::::::::::::::::::::::"); println!(":::: Linting Project ::::"); println!(":::::::::::::::::::::::::"); println!(); - cmd!( - &workspace.cargo_cmd, - "clippy", - "--all-targets", - "--all-features", - "--no-deps" - ) - .env("RUSTFLAGS", "-Dwarnings") - .run()?; + cargo.lint().run()?; println!(":::: Done!"); println!(); - Ok(()) }, }, @@ -469,7 +441,7 @@ fn init_tasks() -> Tasks { name: "setup".into(), description: "bootstrap project for local development".into(), flags: task_flags! {}, - run: |_opts, workspace, _tasks| { + run: |_opts, _fs, _git, cargo, _workspace, _tasks| { println!("::::::::::::::::::::::::::::"); println!(":::: Setting up Project ::::"); println!("::::::::::::::::::::::::::::"); @@ -484,11 +456,10 @@ fn init_tasks() -> Tasks { // TODO (busticated): is there a way to includes these in Cargo.toml or similar? cmd!("rustup", "component", "add", "clippy").run()?; cmd!("rustup", "component", "add", "llvm-tools-preview").run()?; - cmd!(&workspace.cargo_cmd, "install", "grcov").run()?; + cargo.install(["grcov"]).run()?; println!(":::: Done!"); println!(); - Ok(()) }, }, @@ -496,17 +467,16 @@ fn init_tasks() -> Tasks { name: "test".into(), description: "run all tests".into(), flags: task_flags! {}, - run: |_opts, workspace, _tasks| { + run: |_opts, _fs, _git, cargo, _workspace, _tasks| { println!(":::::::::::::::::::::::::"); println!(":::: Testing Project ::::"); println!(":::::::::::::::::::::::::"); println!(); - cmd!(&workspace.cargo_cmd, "test").run()?; + cargo.test([""]).run()?; println!(":::: Done!"); println!(); - Ok(()) }, }, @@ -514,37 +484,16 @@ fn init_tasks() -> Tasks { name: "todo".into(), description: "list open to-dos based on inline source code comments".into(), flags: task_flags! {}, - run: |_opts, _workspace, _tasks| { + run: |_opts, _fs, git, _cargo, _workspace, _tasks| { println!(":::::::::::::::"); println!(":::: TODOs ::::"); println!(":::::::::::::::"); println!(); - // so we don't include this fn in the list (x_X) - let mut ptn = String::from("TODO"); - ptn.push_str(" (.*)"); - - cmd!( - "git", - "grep", - "-e", - ptn, - "--ignore-case", - "--heading", - "--break", - "--context", - "2", - "--full-name", - "--line-number", - "--", - ":!./target/*", - ":!./tmp/*", - ) - .run()?; + git.todos().run()?; println!(":::: Done!"); println!(); - Ok(()) }, }, @@ -552,20 +501,3 @@ fn init_tasks() -> Tasks { tasks } - -fn get_cargo_cmd() -> String { - env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()) -} - -fn get_root_path>(cargo_cmd: T) -> Result { - let stdout = cmd!( - cargo_cmd.as_ref().to_owned(), - "locate-project", - "--workspace", - "--message-format", - "plain", - ) - .read()?; - - Ok(PathBuf::from(stdout.replace("Cargo.toml", "").trim())) -} diff --git a/xtask/src/options.rs b/xtask/src/options.rs index 231c951..33f51bb 100644 --- a/xtask/src/options.rs +++ b/xtask/src/options.rs @@ -5,7 +5,7 @@ use std::error::Error; type DynError = Box; type TaskFlags = BTreeMap; -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, Default, PartialEq)] pub struct Options { pub args: Vec, pub flags: TaskFlags, diff --git a/xtask/src/readme.rs b/xtask/src/readme.rs index 5e4b4b7..8e695e3 100644 --- a/xtask/src/readme.rs +++ b/xtask/src/readme.rs @@ -1,3 +1,4 @@ +use crate::fs::FS; use crate::krate::Krate; use regex::RegexBuilder; use std::collections::BTreeMap; @@ -29,6 +30,7 @@ impl Readme { } pub fn read(&self) -> Result { + // TODO (busticated): pull into FS wrapper? Ok(fs::read_to_string(&self.path)?) } @@ -37,17 +39,13 @@ impl Readme { Ok(self.clone()) } - pub fn create, D: AsRef>( - &mut self, - name: N, - description: D, - ) -> Result<(), DynError> { - self.text = self.render(name, description); - self.save() + pub fn create(&mut self, fs: &FS, krate: &Krate) -> Result<(), DynError> { + self.text = self.render(&krate.name, &krate.description); + self.save(fs) } - pub fn save(&self) -> Result<(), DynError> { - Ok(fs::write(&self.path, &self.text)?) + pub fn save(&self, fs: &FS) -> Result<(), DynError> { + Ok(fs.write(&self.path, &self.text)?) } pub fn render, D: AsRef>(&self, name: N, description: D) -> String { @@ -73,6 +71,7 @@ impl Readme { pub fn update_crates_list( &mut self, + fs: &FS, mut krates: BTreeMap, ) -> Result<(), DynError> { self.load()?; @@ -97,7 +96,7 @@ impl Readme { entries.push_str(marker_end); let updated = re.replace(&self.text, &entries); self.text = updated.as_ref().to_owned(); - self.save() + self.save(fs) } } diff --git a/xtask/src/semver.rs b/xtask/src/semver.rs index 3fd87a6..96fea8a 100644 --- a/xtask/src/semver.rs +++ b/xtask/src/semver.rs @@ -1,5 +1,5 @@ -use std::fmt::{Display, Formatter}; use semver::{BuildMetadata, Prerelease, Version}; +use std::fmt::{Display, Formatter}; #[derive(Clone, Debug, PartialEq)] pub enum VersionChoice { diff --git a/xtask/src/tasks.rs b/xtask/src/tasks.rs index e0cf255..a12b885 100644 --- a/xtask/src/tasks.rs +++ b/xtask/src/tasks.rs @@ -1,16 +1,27 @@ +use crate::cargo::Cargo; +use crate::fs::FS; +use crate::git::Git; use crate::options::Options; use crate::workspace::Workspace; use std::collections::BTreeMap; use std::error::Error; type DynError = Box; +type TaskRunner = fn( + opts: &Options, + fs: FS, + git: Git, + cargo: Cargo, + workspace: Workspace, + tasks: &Tasks, +) -> Result<(), DynError>; #[derive(Clone, Debug, PartialEq)] pub struct Task { pub name: String, pub description: String, pub flags: BTreeMap, - pub run: fn(opts: Options, &mut Workspace, &Tasks) -> Result<(), DynError>, + pub run: TaskRunner, } impl Task { @@ -19,7 +30,7 @@ impl Task { name: N, description: D, flags: BTreeMap, - run: fn(args: Options, &mut Workspace, &Tasks) -> Result<(), DynError>, + run: TaskRunner, ) -> Self { Task { name: name.as_ref().to_owned(), @@ -29,14 +40,13 @@ impl Task { } } - pub fn exec( - &self, - args: Vec, - workspace: &mut Workspace, - tasks: &Tasks, - ) -> Result<(), DynError> { + pub fn exec(&self, args: Vec, tasks: &Tasks) -> Result<(), DynError> { let opts = Options::new(args, self.flags.clone())?; - (self.run)(opts, workspace, tasks)?; + let cargo = Cargo::new(&opts); + let git = Git::new(&opts); + let fs = FS::new(&opts); + let workspace = Workspace::from_path(cargo.workspace_path()?)?; + (self.run)(&opts, fs, git, cargo, workspace, tasks)?; Ok(()) } } @@ -103,10 +113,12 @@ mod tests { use super::*; use crate::task_flags; + static FAKE_RUN: TaskRunner = |_, _, _, _, _, _| Ok(()); + #[test] fn it_initializes_a_task() { let flags = BTreeMap::from([("foo".into(), "does the foo".into())]); - let task = Task::new("test", "my test task", flags, |_, _, _| Ok(())); + let task = Task::new("test", "my test task", flags, FAKE_RUN); assert_eq!(task.name, "test"); assert_eq!(task.description, "my test task"); } @@ -114,10 +126,9 @@ mod tests { #[test] fn it_executes_a_task() { let tasks = Tasks::new(); - let mut workspace = Workspace::new("fake-cargo", std::path::PathBuf::from("fake-root")); let flags = BTreeMap::from([("foo".into(), "does the foo".into())]); - let task = Task::new("test", "my test task", flags, |_, _, _| Ok(())); - task.exec(vec![], &mut workspace, &tasks).unwrap(); + let task = Task::new("test", "my test task", flags, FAKE_RUN); + task.exec(vec![], &tasks).unwrap(); } #[test] @@ -130,8 +141,8 @@ mod tests { fn it_add_a_task() { let mut tasks = Tasks::new(); let flags = BTreeMap::from([("foo".into(), "does the foo".into())]); - let task1 = Task::new("one", "task 01", flags.clone(), |_, _, _| Ok(())); - let task2 = Task::new("two", "task 02", flags, |_, _, _| Ok(())); + let task1 = Task::new("one", "task 01", flags.clone(), FAKE_RUN); + let task2 = Task::new("two", "task 02", flags, FAKE_RUN); tasks.add(vec![task1, task2]); @@ -144,8 +155,8 @@ mod tests { fn it_gets_a_task() { let mut tasks = Tasks::new(); let flags = BTreeMap::from([("foo".into(), "does the foo".into())]); - let task1 = Task::new("one", "task 01", flags.clone(), |_, _, _| Ok(())); - let task2 = Task::new("two", "task 02", flags, |_, _, _| Ok(())); + let task1 = Task::new("one", "task 01", flags.clone(), FAKE_RUN); + let task2 = Task::new("two", "task 02", flags, FAKE_RUN); tasks.add(vec![task1, task2]); let task = tasks.get("one").unwrap(); @@ -166,7 +177,7 @@ mod tests { "foo" => "does the foo", "bar" => "enables bar", }, - run: |_, _, _| Ok(()), + run: FAKE_RUN, }, Task { name: "two".into(), @@ -174,7 +185,7 @@ mod tests { flags: task_flags! { "baz" => "invokes a baz", }, - run: |_, _, _| Ok(()), + run: FAKE_RUN, }, ]); diff --git a/xtask/src/toml.rs b/xtask/src/toml.rs index 1f1dede..4d10e59 100644 --- a/xtask/src/toml.rs +++ b/xtask/src/toml.rs @@ -1,8 +1,10 @@ +use crate::fs::FS; +use crate::krate::Krate; +use semver::Version; use std::error::Error; use std::fs; use std::path::{Path, PathBuf}; -use toml_edit::{Document, value as toml_value}; -use semver::Version; +use toml_edit::{value as toml_value, Document}; type DynError = Box; @@ -28,6 +30,7 @@ impl Toml { } pub fn read(&self) -> Result { + // TODO (busticated): pull into FS wrapper? let text = fs::read_to_string(&self.path)?; Ok(text.parse::()?) } @@ -37,18 +40,14 @@ impl Toml { Ok(self.clone()) } - pub fn create, D: AsRef>( - &mut self, - name: N, - description: D, - ) -> Result<(), DynError> { - let text = self.render(name, description); + pub fn create(&mut self, fs: &FS, krate: &Krate) -> Result<(), DynError> { + let text = self.render(&krate.name, &krate.description); self.data = text.parse::()?; - self.save() + self.save(fs) } - pub fn save(&self) -> Result<(), DynError> { - Ok(fs::write(&self.path, self.data.to_string())?) + pub fn save(&self, fs: &FS) -> Result<(), DynError> { + Ok(fs.write(&self.path, self.data.to_string())?) } pub fn render, D: AsRef>(&self, name: N, description: D) -> String { diff --git a/xtask/src/workspace.rs b/xtask/src/workspace.rs index de755e4..cb3cb36 100644 --- a/xtask/src/workspace.rs +++ b/xtask/src/workspace.rs @@ -1,11 +1,11 @@ +use crate::cargo::Cargo; +use crate::fs::FS; use crate::krate::{Krate, KratePaths}; use crate::readme::Readme; use crate::toml::Toml; -use duct::cmd; use std::collections::BTreeMap; use std::error::Error; -use std::fs; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; type DynError = Box; @@ -14,7 +14,6 @@ const CRATES_DIRNAME: &str = "crates"; #[derive(Clone, Debug, Default)] pub struct Workspace { pub path: PathBuf, - pub cargo_cmd: String, pub readme: Readme, pub toml: Toml, } @@ -27,38 +26,28 @@ impl KratePaths for Workspace { impl Workspace { #[allow(dead_code)] - pub fn new>(cargo_cmd: C, path: PathBuf) -> Self { - let cargo_cmd = cargo_cmd.as_ref().to_owned(); + pub fn new>(path: P) -> Self { + let path = path.as_ref().to_owned(); let readme = Readme::new(path.clone()); let toml = Toml::new(path.clone()); - Workspace { - cargo_cmd, - path, - readme, - toml, - } + Workspace { path, readme, toml } } - pub fn from_path>(cargo_cmd: C, path: PathBuf) -> Result { - let cargo_cmd = cargo_cmd.as_ref().to_owned(); + pub fn from_path>(path: P) -> Result { + let path = path.as_ref().to_owned(); let readme = Readme::from_path(path.clone())?; let toml = Toml::from_path(path.clone())?; - Ok(Workspace { - cargo_cmd, - path, - readme, - toml, - }) + Ok(Workspace { path, readme, toml }) } pub fn krates_path(&self) -> PathBuf { self.path().join(CRATES_DIRNAME) } - pub fn krates(&self) -> Result, DynError> { + pub fn krates(&self, fs: &FS) -> Result, DynError> { let mut krates = BTreeMap::new(); - for entry in fs::read_dir(self.krates_path())? { + for entry in fs.read_dir(self.krates_path())? { let entry = entry?; let path = entry.path(); if path.is_dir() { @@ -70,49 +59,41 @@ impl Workspace { Ok(krates) } - pub fn add_krate, V: AsRef, N: AsRef, D: AsRef>( - &self, - kind: K, - version: V, - name: N, - description: D, - ) -> Result { - let path = self.krates_path().join(name.as_ref()); - let mut krate = Krate::new(kind, version, name, description, path); - - cmd!( - &self.cargo_cmd, - "new", - &krate.path, - "--name", - &krate.name, - krate.kind.to_string() - ) - .run()?; - - krate.readme.create(&krate.name, &krate.description)?; - krate.toml.create(&krate.name, &krate.description)?; - + pub fn add_krate(&self, fs: &FS, cargo: &Cargo, mut krate: Krate) -> Result { + let kind = krate.kind.to_string(); + cargo + .create(&krate.path, ["--name", &krate.name, &kind]) + .run()?; + krate.readme.create(fs, &krate.clone())?; + krate.toml.create(fs, &krate.clone())?; Ok(krate) } - pub fn clean(&self) -> Result<(), DynError> { - fs::remove_dir_all(self.tmp_path())?; - let krates = self.krates()?; + pub fn clean(&self, fs: &FS, cargo: &Cargo) -> Result<(), DynError> { + use std::io::ErrorKind; + + match fs.remove_dir_all(self.tmp_path()) { + Err(e) if e.kind() == ErrorKind::NotFound => (), + Err(e) => return Err(Box::new(e)), + Ok(()) => (), + }; + + let krates = self.krates(fs)?; for krate in krates.values() { - krate.clean()?; + krate.clean(fs)?; } + cargo.clean(["--release"]).run()?; Ok(()) } - pub fn create_dirs(&self) -> Result<(), DynError> { - fs::create_dir_all(self.coverage_path())?; - let krates = self.krates()?; + pub fn create_dirs(&self, fs: &FS) -> Result<(), DynError> { + fs.create_dir_all(self.coverage_path())?; + let krates = self.krates(fs)?; for krate in krates.values() { - krate.create_dirs()?; + krate.create_dirs(fs)?; } Ok(()) @@ -127,28 +108,32 @@ mod tests { #[test] fn it_initializes_a_workspace() { - let workspace = Workspace::new("fake-cargo", PathBuf::from("fake-root")); - assert!(!workspace.cargo_cmd.is_empty()); + let fake_path = PathBuf::from("fake-path"); + let workspace = Workspace::new(fake_path); + assert_eq!(workspace.path, PathBuf::from("fake-path")); } #[test] fn it_gets_path_to_workspace() { - let workspace = Workspace::new("fake-cargo", PathBuf::from("fake-root")); - assert!(!workspace.cargo_cmd.is_empty()); - assert_eq!(workspace.path(), PathBuf::from("fake-root")); + let fake_path = PathBuf::from("fake-path"); + let workspace = Workspace::new(fake_path); + assert_eq!(workspace.path(), PathBuf::from("fake-path")); } #[test] fn it_gets_path_to_workspace_tmp_dir() { - let path = PathBuf::from("fake-root"); - let workspace = Workspace::new("fake-cargo", path.clone()); - assert_eq!(workspace.tmp_path(), path.join("tmp")); + let fake_path = PathBuf::from("fake-path"); + let workspace = Workspace::new(&fake_path); + assert_eq!(workspace.tmp_path(), fake_path.join("tmp")); } #[test] fn it_gets_path_to_workspace_coverage_dir() { - let path = PathBuf::from("fake-root"); - let workspace = Workspace::new("fake-cargo", path.clone()); - assert_eq!(workspace.coverage_path(), path.join("tmp").join("coverage")); + let fake_path = PathBuf::from("fake-path"); + let workspace = Workspace::new(&fake_path); + assert_eq!( + workspace.coverage_path(), + fake_path.join("tmp").join("coverage") + ); } }