diff --git a/Cargo.lock b/Cargo.lock index 40b794e3ee..1f719cab7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1705,6 +1705,7 @@ dependencies = [ "syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "sys-info 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", "sysinfo 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2146,6 +2147,18 @@ dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand" version = "0.6.5" @@ -2829,6 +2842,15 @@ dependencies = [ "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tempdir" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tempfile" version = "3.1.0" @@ -3627,6 +3649,7 @@ dependencies = [ "checksum publicsuffix 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5afecba86dcf1e4fd610246f89899d1924fe12e1e89f555eb7c7f710f3c5ad1d" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" +"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" @@ -3705,6 +3728,7 @@ dependencies = [ "checksum syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e80b8831c5a543192ffc3727f01cf0e57579c6ac15558e3048bfb5708892167b" "checksum sys-info 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)" = "76d6cf7b349b6a6daaf7a3797227e2f4108c8dd398e0aca7e29b9fb239948541" "checksum sysinfo 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c3e2cab189e59f72710e3dd5e1e0d5be0f6c5c999c326f2fdcdf3bf4483ec9fd" +"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" "checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" diff --git a/Cargo.toml b/Cargo.toml index 08559b23c8..c7efe3f555 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,6 +81,7 @@ syntect = "3.2.0" [dev-dependencies] pretty_assertions = "0.6.1" +tempdir = "0.3.7" [lib] name = "nu" diff --git a/src/commands.rs b/src/commands.rs index 8d813191ce..9c60aabf16 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -42,7 +42,7 @@ crate mod trim; crate mod vtable; crate mod where_; -crate use command::{command, filter, EvaluatedFilterCommandArgs, EvaluatedStaticCommandArgs}; +crate use command::{command, EvaluatedStaticCommandArgs}; crate use config::Config; crate use open::Open; crate use rm::Remove; diff --git a/src/commands/command.rs b/src/commands/command.rs index 1e14fbfcea..d3cd6ba86b 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -27,18 +27,6 @@ impl ToDebug for UnevaluatedCallInfo { } impl UnevaluatedCallInfo { - fn name(&self) -> Result<&str, ShellError> { - let head = &self.args.head(); - match head.item() { - hir::RawExpression::Literal(hir::Literal::Bare) => Ok(head.span.slice(&self.source)), - hir::RawExpression::Literal(hir::Literal::String(span)) => Ok(span.slice(&self.source)), - other => Err(ShellError::type_error( - "Command name", - head.type_name().spanned(head.span), - )), - } - } - fn evaluate( self, registry: ®istry::CommandRegistry, @@ -94,20 +82,6 @@ impl CommandArgs { } } -pub enum EvaluatedInput { - Static(InputStream), - Filter(Spanned), -} - -impl EvaluatedInput { - pub fn stream(self) -> InputStream { - match self { - EvaluatedInput::Static(stream) => stream, - EvaluatedInput::Filter(value) => vec![value].into(), - } - } -} - pub struct EvaluatedStaticCommandArgs { pub args: EvaluatedCommandArgs, pub input: InputStream, @@ -152,6 +126,7 @@ impl EvaluatedStaticCommandArgs { #[get = "pub"] pub struct EvaluatedFilterCommandArgs { args: EvaluatedCommandArgs, + #[allow(unused)] input: Spanned, } @@ -189,8 +164,6 @@ pub struct EvaluatedCommandArgs { } impl EvaluatedCommandArgs { - pub fn parts(self) -> () {} - pub fn call_args(&self) -> ®istry::EvaluatedArgs { &self.call_info.args } @@ -199,10 +172,6 @@ impl EvaluatedCommandArgs { self.call_info.args.nth(pos) } - pub fn positional_iter(&self) -> impl Iterator> { - self.call_info.args.positional_iter() - } - pub fn expect_nth(&self, pos: usize) -> Result<&Spanned, ShellError> { self.call_info.args.expect_nth(pos) } @@ -308,6 +277,7 @@ pub trait Sink { } } +#[allow(unused)] pub struct FnFilterCommand { name: String, func: fn(EvaluatedFilterCommandArgs) -> Result, @@ -397,6 +367,7 @@ pub fn command( }) } +#[allow(unused)] pub fn filter( name: &str, func: fn(EvaluatedFilterCommandArgs) -> Result, diff --git a/src/commands/exit.rs b/src/commands/exit.rs index 8bba11b49b..9dabe99f84 100644 --- a/src/commands/exit.rs +++ b/src/commands/exit.rs @@ -2,6 +2,6 @@ use crate::commands::command::CommandAction; use crate::errors::ShellError; use crate::prelude::*; -pub fn exit(_args: CommandArgs, registry: &CommandRegistry) -> Result { +pub fn exit(_args: CommandArgs, _registry: &CommandRegistry) -> Result { Ok(vec![Ok(ReturnSuccess::Action(CommandAction::Exit))].into()) } diff --git a/src/commands/from_yaml.rs b/src/commands/from_yaml.rs index 69d42697df..eb38159df7 100644 --- a/src/commands/from_yaml.rs +++ b/src/commands/from_yaml.rs @@ -50,7 +50,7 @@ pub fn from_yaml_string_to_value( pub fn from_yaml( args: CommandArgs, - registry: &CommandRegistry, + _registry: &CommandRegistry, ) -> Result { let span = args.name_span(); let out = args.input; diff --git a/src/commands/open.rs b/src/commands/open.rs index d8d8a4a67a..172526e3e2 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -1,11 +1,10 @@ use crate::context::SpanSource; use crate::errors::ShellError; -use crate::object::{Primitive, Switch, Value}; +use crate::object::{Primitive, Value}; use crate::parser::hir::SyntaxType; use crate::parser::parse::span::Span; -use crate::parser::registry::{self, CommandConfig, NamedType, PositionalType}; +use crate::parser::registry::{self, CommandConfig, NamedType}; use crate::prelude::*; -use indexmap::IndexMap; use mime::Mime; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -77,17 +76,10 @@ impl Command for Open { } fn config(&self) -> CommandConfig { - let mut named = IndexMap::default(); - named.insert("raw".to_string(), NamedType::Switch); - - CommandConfig { - name: self.name().to_string(), - positional: vec![PositionalType::mandatory("path", SyntaxType::Block)], - rest_positional: false, - named, - is_sink: true, - is_filter: false, - } + CommandConfig::new(self.name()) + .required("path", SyntaxType::Block) + .named("raw", NamedType::Switch) + .sink() } } diff --git a/src/commands/ps.rs b/src/commands/ps.rs index 8abaa15b56..ed1479007f 100644 --- a/src/commands/ps.rs +++ b/src/commands/ps.rs @@ -3,7 +3,7 @@ use crate::object::process::process_dict; use crate::prelude::*; use sysinfo::{RefreshKind, SystemExt}; -pub fn ps(args: CommandArgs, registry: &CommandRegistry) -> Result { +pub fn ps(args: CommandArgs, _registry: &CommandRegistry) -> Result { let mut system = sysinfo::System::new_with_specifics(RefreshKind::new().with_processes()); system.refresh_processes(); let list = system.get_process_list(); diff --git a/src/commands/size.rs b/src/commands/size.rs index 1ed87effc0..9dc497aea8 100644 --- a/src/commands/size.rs +++ b/src/commands/size.rs @@ -2,7 +2,7 @@ use crate::errors::ShellError; use crate::object::{SpannedDictBuilder, Value}; use crate::prelude::*; -pub fn size(args: CommandArgs, registry: &CommandRegistry) -> Result { +pub fn size(args: CommandArgs, _registry: &CommandRegistry) -> Result { let input = args.input; Ok(input .values diff --git a/src/commands/sysinfo.rs b/src/commands/sysinfo.rs index d0eb2f69c9..c2296a0bdc 100644 --- a/src/commands/sysinfo.rs +++ b/src/commands/sysinfo.rs @@ -6,7 +6,7 @@ use crate::prelude::*; use sys_info::*; use sysinfo::{ComponentExt, DiskExt, NetworkExt, RefreshKind, SystemExt}; -pub fn sysinfo(args: CommandArgs, registry: &CommandRegistry) -> Result { +pub fn sysinfo(args: CommandArgs, _registry: &CommandRegistry) -> Result { let name_span = args.name_span(); let mut idx = SpannedDictBuilder::new(name_span); diff --git a/src/commands/to_array.rs b/src/commands/to_array.rs index d5fcfa3153..8cd7acc4ef 100644 --- a/src/commands/to_array.rs +++ b/src/commands/to_array.rs @@ -1,7 +1,10 @@ use crate::object::Value; use crate::prelude::*; -pub fn to_array(args: CommandArgs, registry: &CommandRegistry) -> Result { +pub fn to_array( + args: CommandArgs, + _registry: &CommandRegistry, +) -> Result { let out = args.input.values.collect(); Ok(out diff --git a/src/commands/trim.rs b/src/commands/trim.rs index 5224a23923..bede0e1b1d 100644 --- a/src/commands/trim.rs +++ b/src/commands/trim.rs @@ -2,7 +2,7 @@ use crate::errors::ShellError; use crate::object::Value; use crate::prelude::*; -pub fn trim(args: CommandArgs, registry: &CommandRegistry) -> Result { +pub fn trim(args: CommandArgs, _registry: &CommandRegistry) -> Result { let input = args.input; Ok(input diff --git a/src/commands/where_.rs b/src/commands/where_.rs index f852b46a54..d9480aa4ed 100644 --- a/src/commands/where_.rs +++ b/src/commands/where_.rs @@ -1,6 +1,5 @@ use crate::errors::ShellError; use crate::object::base as value; -use crate::object::{types, Value}; use crate::parser::hir::SyntaxType; use crate::parser::registry::{self, CommandConfig, PositionalType}; use crate::prelude::*; diff --git a/src/context.rs b/src/context.rs index 2f9d00c98f..1eed5d39b7 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,9 +1,5 @@ use crate::commands::command::{CallInfo, Sink, SinkCommandArgs, UnevaluatedCallInfo}; -use crate::parser::{ - hir, - registry::{self, CommandConfig}, - Span, -}; +use crate::parser::{hir, registry, Span}; use crate::prelude::*; use derive_new::new; @@ -51,12 +47,6 @@ impl CommandRegistry { } } - fn get_config(&self, name: &str) -> Option { - let registry = self.registry.lock().unwrap(); - - registry.get(name).map(|c| c.config()) - } - fn get_command(&self, name: &str) -> Option> { let registry = self.registry.lock().unwrap(); @@ -75,7 +65,7 @@ impl CommandRegistry { } crate fn names(&self) -> Vec { - let mut registry = self.registry.lock().unwrap(); + let registry = self.registry.lock().unwrap(); registry.keys().cloned().collect() } } diff --git a/src/lib.rs b/src/lib.rs index 15e3bf3ef1..813de129b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,7 @@ mod plugin; mod shell; mod stream; mod traits; +mod utils; pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue}; pub use crate::context::SpanSource; @@ -31,6 +32,7 @@ pub use crate::env::host::BasicHost; pub use crate::parser::parse::span::SpannedItem; pub use crate::parser::Spanned; pub use crate::plugin::{serve_plugin, Plugin}; +pub use crate::utils::{AbsolutePath, RelativePath}; pub use cli::cli; pub use errors::ShellError; pub use object::base::{Primitive, Value}; diff --git a/src/object.rs b/src/object.rs index f094c63a17..08f660fdae 100644 --- a/src/object.rs +++ b/src/object.rs @@ -6,6 +6,6 @@ crate mod into; crate mod process; crate mod types; -crate use base::{Block, Primitive, Switch, Value}; +crate use base::{Primitive, Value}; crate use dict::{Dictionary, SpannedDictBuilder}; crate use files::dir_entry_dict; diff --git a/src/object/base.rs b/src/object/base.rs index 6542ecaac2..1a49f97a11 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -266,6 +266,7 @@ pub enum Switch { } impl Switch { + #[allow(unused)] pub fn is_present(&self) -> bool { match self { Switch::Present => true, diff --git a/src/parser/parse_command.rs b/src/parser/parse_command.rs index a5a2b0fa4f..ec4702fc99 100644 --- a/src/parser/parse_command.rs +++ b/src/parser/parse_command.rs @@ -77,7 +77,7 @@ fn parse_command_tail( trace_remaining("nodes", tail.clone(), source); - for (name, kind) in config.named() { + for (name, kind) in &config.named { trace!(target: "nu::parse", "looking for {} : {:?}", name, kind); match kind { @@ -115,7 +115,7 @@ fn parse_command_tail( if tail.at_end() { return Err(ShellError::argument_error( - config.name().clone(), + config.name.clone(), ArgumentError::MissingValueForName(name.to_string()), flag.span, )); @@ -139,14 +139,14 @@ fn parse_command_tail( let mut positional = vec![]; - for arg in config.positional() { + for arg in &config.positional { trace!("Processing positional {:?}", arg); match arg { PositionalType::Mandatory(..) => { if tail.len() == 0 { return Err(ShellError::argument_error( - config.name().clone(), + config.name.clone(), ArgumentError::MissingMandatoryPositional(arg.name().to_string()), command_span, )); @@ -207,7 +207,7 @@ fn extract_mandatory( match flag { None => Err(ShellError::argument_error( - config.name().clone(), + config.name.clone(), ArgumentError::MissingMandatoryFlag(name.to_string()), span, )), diff --git a/src/parser/registry.rs b/src/parser/registry.rs index a0d0bf221b..159d99193d 100644 --- a/src/parser/registry.rs +++ b/src/parser/registry.rs @@ -4,7 +4,6 @@ use crate::evaluate::{evaluate_baseline_expr, Scope}; use crate::parser::{hir, hir::SyntaxType, parse_command, CallNode, Spanned}; use crate::prelude::*; use derive_new::new; -use getset::Getters; use indexmap::IndexMap; use log::trace; use serde::{Deserialize, Serialize}; @@ -70,8 +69,7 @@ impl PositionalType { } } -#[derive(Debug, Getters, Serialize, Deserialize, Clone)] -#[get = "crate"] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct CommandConfig { pub name: String, pub positional: Vec, @@ -81,6 +79,44 @@ pub struct CommandConfig { pub is_sink: bool, } +impl CommandConfig { + pub fn new(name: impl Into) -> CommandConfig { + CommandConfig { + name: name.into(), + positional: vec![], + rest_positional: false, + named: IndexMap::default(), + is_filter: false, + is_sink: false, + } + } + + pub fn required(mut self, name: impl Into, ty: impl Into) -> CommandConfig { + self.positional + .push(PositionalType::Mandatory(name.into(), ty.into())); + + self + } + + pub fn optional(mut self, name: impl Into, ty: impl Into) -> CommandConfig { + self.positional + .push(PositionalType::Optional(name.into(), ty.into())); + + self + } + + pub fn named(mut self, name: impl Into, ty: impl Into) -> CommandConfig { + self.named.insert(name.into(), ty.into()); + + self + } + + pub fn sink(mut self) -> CommandConfig { + self.is_sink = true; + self + } +} + #[derive(Debug, Default, new, Serialize, Deserialize)] pub struct EvaluatedArgs { pub positional: Option>>, diff --git a/src/prelude.rs b/src/prelude.rs index b6f3f5f112..8b112c8817 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -34,8 +34,7 @@ macro_rules! trace_stream { crate use crate::cli::MaybeOwned; crate use crate::commands::command::{ - Command, CommandAction, CommandArgs, EvaluatedCommandArgs, ReturnSuccess, ReturnValue, Sink, - SinkCommandArgs, + Command, CommandAction, CommandArgs, ReturnSuccess, ReturnValue, Sink, SinkCommandArgs, }; crate use crate::context::{CommandRegistry, Context}; crate use crate::env::host::handle_unexpected; diff --git a/src/shell/completer.rs b/src/shell/completer.rs index af1276e835..c2bb2c9228 100644 --- a/src/shell/completer.rs +++ b/src/shell/completer.rs @@ -1,5 +1,4 @@ use crate::context::CommandRegistry; -use crate::prelude::*; use derive_new::new; use rustyline::completion::Completer; diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000000..7915a35a16 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,62 @@ +use std::ops::Div; +use std::path::{Path, PathBuf}; + +pub struct AbsolutePath { + inner: PathBuf, +} + +impl AbsolutePath { + pub fn new(path: impl AsRef) -> AbsolutePath { + let path = path.as_ref(); + + if path.is_absolute() { + AbsolutePath { + inner: path.to_path_buf(), + } + } else { + panic!("AbsolutePath::new must take an absolute path") + } + } +} + +impl Div<&str> for AbsolutePath { + type Output = AbsolutePath; + + fn div(self, rhs: &str) -> Self::Output { + AbsolutePath { + inner: self.inner.join(rhs), + } + } +} + +impl AsRef for AbsolutePath { + fn as_ref(&self) -> &Path { + self.inner.as_path() + } +} + +pub struct RelativePath { + inner: PathBuf, +} + +impl RelativePath { + pub fn new(path: impl Into) -> RelativePath { + let path = path.into(); + + if path.is_relative() { + RelativePath { inner: path } + } else { + panic!("RelativePath::new must take a relative path") + } + } +} + +impl> Div for RelativePath { + type Output = RelativePath; + + fn div(self, rhs: T) -> Self::Output { + RelativePath { + inner: self.inner.join(rhs.as_ref()), + } + } +} diff --git a/tests/commands_test.rs b/tests/commands_test.rs index dd9863e05b..997bc7956f 100644 --- a/tests/commands_test.rs +++ b/tests/commands_test.rs @@ -3,6 +3,8 @@ mod helpers; use h::in_directory as cwd; use helpers as h; +use nu::AbsolutePath; + #[test] fn lines() { nu!(output, @@ -80,73 +82,102 @@ fn open_error_if_file_not_found() { } #[test] -fn save_can_write_out_csv() { - let (playground_path, tests_dir) = h::setup_playground_for("save_test"); +fn save_can_write_out_csv() -> Result<(), std::io::Error> { + let (playground, tmp, dir) = h::setup_playground_for("save_test")?; - let full_path = format!("{}/{}", playground_path, tests_dir ); - let expected_file = format!("{}/{}", full_path , "cargo_sample.csv"); + let tmp = AbsolutePath::new(tmp); - nu!( - _output, - cwd(&playground_path), - "open ../formats/cargo_sample.toml | inc package.version --minor | get package | save save_test/cargo_sample.csv" + let expected_file = tmp / "cargo_sample.csv"; + + let root = AbsolutePath::new(std::env::current_dir()?); + + let path = root / "tests" / "fixtures" / "formats" / "cargo_sample.toml"; + + let command = format!( + "open {} | inc package.version --minor | get package | save {}/cargo_sample.csv", + path.as_ref().display(), + dir ); + nu!(_output, playground.path().display(), command); + let actual = h::file_contents(&expected_file); assert!(actual.contains("[list list],A shell for the GitHub era,2018,ISC,nu,0.2.0")); + + Ok(()) } #[test] -fn rm_can_remove_a_file() { - let directory = "tests/fixtures/nuplayground"; - let file = format!("{}/rm_test.txt", directory); +fn rm_can_remove_a_file() -> Result<(), std::io::Error> { + let _ = pretty_env_logger::try_init(); - h::create_file_at(&file); + let (_playground, tmp, _) = h::setup_playground_for("remove_file")?; - nu!(_output, cwd(directory), "rm rm_test.txt"); + let file = AbsolutePath::new(&tmp) / "rm_test.txt"; + // let file = tmp.path().join("rm_test.txt"); - assert!(!h::file_exists_at(&file)); + h::create_file_at(&file)?; + + nu!(_output, tmp.path().display(), "rm rm_test.txt"); + + assert!(!file.as_ref().exists()); + + Ok(()) } #[test] -fn rm_can_remove_directory_contents_with_recursive_flag() { - let (playground_path, tests_dir) = h::setup_playground_for("rm_test"); +fn rm_can_remove_directory_contents_with_recursive_flag() -> Result<(), std::io::Error> { + let _ = pretty_env_logger::try_init(); + + let (playground, tmp, dir) = h::setup_playground_for("rm_test")?; for f in ["yehuda.txt", "jonathan.txt", "andres.txt"].iter() { - h::create_file_at(&format!("{}/{}/{}", playground_path, tests_dir, f)); + h::create_file_at(&tmp.path().join(f))?; } nu!( _output, - cwd("tests/fixtures/nuplayground"), - "rm rm_test --recursive" + playground.path().display(), + format!("rm {} --recursive", dir) ); - assert!(!h::file_exists_at(&format!("{}/{}", playground_path, tests_dir))); + assert!(!tmp.path().exists()); + + Ok(()) } #[test] -fn rm_error_if_attempting_to_delete_a_directory_without_recursive_flag() { - let (playground_path, tests_dir) = h::setup_playground_for("rm_test_2"); - let full_path = format!("{}/{}", playground_path, tests_dir); +fn rm_error_if_attempting_to_delete_a_directory_without_recursive_flag( +) -> Result<(), std::io::Error> { + let (playground, tmp, dir) = h::setup_playground_for("rm_test_2")?; - nu_error!(output, cwd("tests/fixtures/nuplayground"), "rm rm_test_2"); + nu_error!(output, playground.path().display(), format!("rm {}", dir)); - assert!(h::file_exists_at(&full_path)); + assert!(tmp.path().exists()); assert!(output.contains("is a directory")); - h::delete_directory_at(&full_path); + h::delete_directory_at(tmp.path()); + + Ok(()) } #[test] -fn rm_error_if_attempting_to_delete_single_dot_as_argument() { - nu_error!(output, cwd("tests/fixtures/nuplayground"), "rm ."); +fn rm_error_if_attempting_to_delete_single_dot_as_argument() -> Result<(), std::io::Error> { + let (_playground, tmp, _) = h::setup_playground_for("rm_test_2")?; + + nu_error!(output, tmp.path().display(), "rm ."); assert!(output.contains("may not be removed")); + + Ok(()) } #[test] -fn rm_error_if_attempting_to_delete_two_dot_as_argument() { - nu_error!(output, cwd("tests/fixtures/nuplayground"), "rm .."); +fn rm_error_if_attempting_to_delete_two_dot_as_argument() -> Result<(), std::io::Error> { + let (_playground, tmp, _) = h::setup_playground_for("rm_test_2")?; + + nu_error!(output, tmp.path().display(), "rm .."); assert!(output.contains("may not be removed")); + + Ok(()) } diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs index bd9a3106e7..81e4313b94 100644 --- a/tests/helpers/mod.rs +++ b/tests/helpers/mod.rs @@ -1,8 +1,10 @@ #![allow(dead_code)] -pub use std::path::PathBuf; +pub use std::path::{Path, PathBuf}; +use log::trace; use std::io::Read; +use tempdir::TempDir; #[macro_export] macro_rules! nu { @@ -79,20 +81,30 @@ macro_rules! nu_error { }; } -pub fn setup_playground_for(topic: &str) -> (String, String) { - let home = "tests/fixtures/nuplayground"; - let full_path = format!("{}/{}", home, topic); +pub fn setup_playground_for(topic: &str) -> Result<(TempDir, TempDir, String), std::io::Error> { + let home = TempDir::new("nuplayground")?; + let child = TempDir::new_in(home.path(), topic)?; + let relative = child + .path() + .file_name() + .unwrap() + .to_str() + .expect(&format!( + "file name {} was not valid", + child.path().display() + )) + .to_string(); - if file_exists_at(&full_path) { - delete_directory_at(&full_path); - } + trace!( + "created {:?} dir={}", + child.path().display(), + child.path().is_dir() + ); - create_directory_at(&full_path); - - (home.to_string(), topic.to_string()) + Ok((home, child, relative)) } -pub fn file_contents(full_path: &str) -> String { +pub fn file_contents(full_path: impl AsRef) -> String { let mut file = std::fs::File::open(full_path).expect("can not open file"); let mut contents = String::new(); file.read_to_string(&mut contents) @@ -100,19 +112,26 @@ pub fn file_contents(full_path: &str) -> String { contents } -pub fn create_file_at(full_path: &str) { - std::fs::write(PathBuf::from(full_path), "fake data".as_bytes()).expect("can not create file"); +pub fn create_file_at(full_path: impl AsRef) -> Result<(), std::io::Error> { + let full_path = full_path.as_ref(); + + assert!( + full_path.parent().unwrap().is_dir(), + "{:?} exists", + full_path.parent().unwrap().display(), + ); + std::fs::write(full_path, "fake data".as_bytes()) } pub fn file_exists_at(full_path: &str) -> bool { PathBuf::from(full_path).exists() } -pub fn delete_directory_at(full_path: &str) { +pub fn delete_directory_at(full_path: &Path) { std::fs::remove_dir_all(PathBuf::from(full_path)).expect("can not remove directory"); } -pub fn create_directory_at(full_path: &str) { +pub fn create_directory_at(full_path: &Path) { let path = PathBuf::from(full_path); println!("{:?} - is_dir: {:?}", path, path.is_dir());