Merge pull request #326 from androbtech/wrip-wrap

Unwrap surveying, improvements, adds.
This commit is contained in:
Jonathan Turner 2019-08-22 04:14:57 +12:00 committed by GitHub
commit 9b1034074a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 342 additions and 262 deletions

View File

@ -238,6 +238,7 @@ pub struct RunnableContext {
}
impl RunnableContext {
#[allow(unused)]
pub fn cwd(&self) -> PathBuf {
PathBuf::from(self.shell_manager.path())
}

View File

@ -10,8 +10,8 @@ pub struct Cpy;
#[derive(Deserialize)]
pub struct CopyArgs {
source: Tagged<PathBuf>,
destination: Tagged<PathBuf>,
src: Tagged<PathBuf>,
dst: Tagged<PathBuf>,
recursive: Tagged<bool>,
}
@ -32,44 +32,43 @@ impl PerItemCommand for Cpy {
fn signature(&self) -> Signature {
Signature::build("cp")
.required("source", SyntaxType::Path)
.required("destination", SyntaxType::Path)
.required("src", SyntaxType::Path)
.required("dst", SyntaxType::Path)
.named("file", SyntaxType::Any)
.switch("recursive")
}
}
pub fn cp(
args: CopyArgs,
context: &RunnablePerItemContext,
fn cp(
CopyArgs {
src,
dst,
recursive,
}: CopyArgs,
RunnablePerItemContext { name, .. }: &RunnablePerItemContext,
) -> Result<VecDeque<ReturnValue>, ShellError> {
let mut source = PathBuf::from(context.shell_manager.path());
let mut destination = PathBuf::from(context.shell_manager.path());
let name_span = context.name;
let source = src.item.clone();
let mut destination = dst.item.clone();
let name_span = name;
source.push(&args.source.item);
destination.push(&args.destination.item);
let sources = glob::glob(&source.to_string_lossy());
if sources.is_err() {
return Err(ShellError::labeled_error(
"Invalid pattern.",
"Invalid pattern.",
args.source.tag,
));
}
let sources: Vec<_> = sources.unwrap().collect();
let sources: Vec<_> = match glob::glob(&source.to_string_lossy()) {
Ok(files) => files.collect(),
Err(_) => {
return Err(ShellError::labeled_error(
"Invalid pattern.",
"Invalid pattern.",
src.tag,
))
}
};
if sources.len() == 1 {
if let Ok(entry) = &sources[0] {
if entry.is_dir() && !args.recursive.item {
if entry.is_dir() && !recursive.item {
return Err(ShellError::labeled_error(
"is a directory (not copied). Try using \"--recursive\".",
"is a directory (not copied). Try using \"--recursive\".",
args.source.tag,
src.tag,
));
}
@ -80,15 +79,19 @@ pub fn cp(
if entry.is_file() {
let strategy = |(source_file, _depth_level)| {
if destination.exists() {
let mut new_dst = dunce::canonicalize(destination.clone()).unwrap();
new_dst.push(entry.file_name().unwrap());
(source_file, new_dst)
let mut new_dst = dunce::canonicalize(destination.clone())?;
if let Some(name) = entry.file_name() {
new_dst.push(name);
}
Ok((source_file, new_dst))
} else {
(source_file, destination.clone())
Ok((source_file, destination.clone()))
}
};
for (ref src, ref dst) in sources.paths_applying_with(strategy) {
let sources = sources.paths_applying_with(strategy)?;
for (ref src, ref dst) in sources {
if src.is_file() {
match std::fs::copy(src, dst) {
Err(e) => {
@ -119,7 +122,7 @@ pub fn cp(
let strategy = |(source_file, depth_level)| {
let mut new_dst = destination.clone();
let path = dunce::canonicalize(&source_file).unwrap();
let path = dunce::canonicalize(&source_file)?;
let mut comps: Vec<_> = path
.components()
@ -134,10 +137,12 @@ pub fn cp(
new_dst.push(fragment);
}
(PathBuf::from(&source_file), PathBuf::from(new_dst))
Ok((PathBuf::from(&source_file), PathBuf::from(new_dst)))
};
for (ref src, ref dst) in sources.paths_applying_with(strategy) {
let sources = sources.paths_applying_with(strategy)?;
for (ref src, ref dst) in sources {
if src.is_dir() {
if !dst.exists() {
match std::fs::create_dir_all(dst) {
@ -167,7 +172,16 @@ pub fn cp(
}
}
} else {
destination.push(entry.file_name().unwrap());
match entry.file_name() {
Some(name) => destination.push(name),
None => {
return Err(ShellError::labeled_error(
"Copy aborted. Not a valid path",
"Copy aborted. Not a valid path",
name_span,
))
}
}
match std::fs::create_dir_all(&destination) {
Err(e) => {
@ -181,8 +195,8 @@ pub fn cp(
};
let strategy = |(source_file, depth_level)| {
let mut new_dst = dunce::canonicalize(&destination).unwrap();
let path = dunce::canonicalize(&source_file).unwrap();
let mut new_dst = dunce::canonicalize(&destination)?;
let path = dunce::canonicalize(&source_file)?;
let mut comps: Vec<_> = path
.components()
@ -197,10 +211,12 @@ pub fn cp(
new_dst.push(fragment);
}
(PathBuf::from(&source_file), PathBuf::from(new_dst))
Ok((PathBuf::from(&source_file), PathBuf::from(new_dst)))
};
for (ref src, ref dst) in sources.paths_applying_with(strategy) {
let sources = sources.paths_applying_with(strategy)?;
for (ref src, ref dst) in sources {
if src.is_dir() {
if !dst.exists() {
match std::fs::create_dir_all(dst) {
@ -234,18 +250,31 @@ pub fn cp(
}
} else {
if destination.exists() {
if !sources.iter().all(|x| (x.as_ref().unwrap()).is_file()) {
if !sources.iter().all(|x| match x {
Ok(f) => f.is_file(),
Err(_) => false,
}) {
return Err(ShellError::labeled_error(
"Copy aborted (directories found). Recursive copying in patterns not supported yet (try copying the directory directly)",
"Copy aborted (directories found). Recursive copying in patterns not supported yet (try copying the directory directly)",
args.source.tag,
src.tag,
));
}
for entry in sources {
if let Ok(entry) = entry {
let mut to = PathBuf::from(&destination);
to.push(&entry.file_name().unwrap());
match entry.file_name() {
Some(name) => to.push(name),
None => {
return Err(ShellError::labeled_error(
"Copy aborted. Not a valid path",
"Copy aborted. Not a valid path",
name_span,
))
}
}
if entry.is_file() {
match std::fs::copy(&entry, &to) {
@ -253,7 +282,7 @@ pub fn cp(
return Err(ShellError::labeled_error(
e.to_string(),
e.to_string(),
args.source.tag,
src.tag,
));
}
Ok(o) => o,
@ -262,16 +291,23 @@ pub fn cp(
}
}
} else {
let destination_file_name = {
match destination.file_name() {
Some(name) => PathBuf::from(name),
None => {
return Err(ShellError::labeled_error(
"Copy aborted. Not a valid destination",
"Copy aborted. Not a valid destination",
name_span,
))
}
}
};
return Err(ShellError::labeled_error(
format!(
"Copy aborted. (Does {:?} exist?)",
&destination.file_name().unwrap()
),
format!(
"Copy aborted. (Does {:?} exist?)",
&destination.file_name().unwrap()
),
args.destination.span(),
format!("Copy aborted. (Does {:?} exist?)", destination_file_name),
format!("Copy aborted. (Does {:?} exist?)", destination_file_name),
&dst.span(),
));
}
}

View File

@ -1,20 +1,25 @@
use crate::commands::command::RunnablePerItemContext;
use crate::errors::ShellError;
use crate::parser::hir::SyntaxType;
use crate::parser::registry::{CommandRegistry, Signature};
use crate::prelude::*;
use std::path::{Path, PathBuf};
use std::path::PathBuf;
pub struct Mkdir;
#[derive(Deserialize)]
struct MkdirArgs {
rest: Vec<Tagged<PathBuf>>,
}
impl PerItemCommand for Mkdir {
fn run(
&self,
call_info: &CallInfo,
registry: &CommandRegistry,
_registry: &CommandRegistry,
shell_manager: &ShellManager,
input: Tagged<Value>,
_input: Tagged<Value>,
) -> Result<VecDeque<ReturnValue>, ShellError> {
mkdir(call_info, registry, shell_manager, input)
call_info.process(shell_manager, mkdir)?.run()
}
fn name(&self) -> &str {
@ -22,29 +27,46 @@ impl PerItemCommand for Mkdir {
}
fn signature(&self) -> Signature {
Signature::build("mkdir").named("file", SyntaxType::Any)
Signature::build("mkdir").rest()
}
}
pub fn mkdir(
call_info: &CallInfo,
_registry: &CommandRegistry,
shell_manager: &ShellManager,
_input: Tagged<Value>,
fn mkdir(
MkdirArgs { rest: directories }: MkdirArgs,
RunnablePerItemContext {
name,
shell_manager,
..
}: &RunnablePerItemContext,
) -> Result<VecDeque<ReturnValue>, ShellError> {
let mut full_path = PathBuf::from(shell_manager.path());
let full_path = PathBuf::from(shell_manager.path());
match &call_info.args.nth(0) {
Some(Tagged { item: value, .. }) => full_path.push(Path::new(&value.as_string()?)),
_ => {}
if directories.len() == 0 {
return Err(ShellError::labeled_error(
"mkdir requires directory paths",
"needs parameter",
name,
));
}
match std::fs::create_dir_all(full_path) {
Err(reason) => Err(ShellError::labeled_error(
reason.to_string(),
reason.to_string(),
call_info.args.nth(0).unwrap().span(),
)),
Ok(_) => Ok(VecDeque::new()),
for dir in directories.iter() {
let create_at = {
let mut loc = full_path.clone();
loc.push(&dir.item);
loc
};
match std::fs::create_dir_all(create_at) {
Err(reason) => {
return Err(ShellError::labeled_error(
reason.to_string(),
reason.to_string(),
dir.span(),
))
}
Ok(_) => {}
}
}
Ok(VecDeque::new())
}

View File

@ -5,15 +5,12 @@ use crate::parser::registry::{CommandRegistry, Signature};
use crate::prelude::*;
use std::path::PathBuf;
#[cfg(windows)]
use crate::utils::FileStructure;
pub struct Move;
#[derive(Deserialize)]
pub struct MoveArgs {
source: Tagged<PathBuf>,
destination: Tagged<PathBuf>,
src: Tagged<PathBuf>,
dst: Tagged<PathBuf>,
}
impl PerItemCommand for Move {
@ -39,17 +36,16 @@ impl PerItemCommand for Move {
}
}
pub fn mv(
args: MoveArgs,
context: &RunnablePerItemContext,
fn mv(
MoveArgs { src, dst }: MoveArgs,
RunnablePerItemContext {
name,
shell_manager,
}: &RunnablePerItemContext,
) -> Result<VecDeque<ReturnValue>, ShellError> {
let mut source = PathBuf::from(context.shell_manager.path());
let mut destination = PathBuf::from(context.shell_manager.path());
let name_span = context.name;
source.push(&args.source.item);
destination.push(&args.destination.item);
let source = src.item.clone();
let mut destination = dst.item.clone();
let name_span = name;
let sources: Vec<_> = match glob::glob(&source.to_string_lossy()) {
Ok(files) => files.collect(),
@ -57,21 +53,23 @@ pub fn mv(
return Err(ShellError::labeled_error(
"Invalid pattern.",
"Invalid pattern.",
args.source.tag,
src.tag,
))
}
};
let destination_file_name = {
let path = &destination;
if "." == destination.to_string_lossy() {
destination = PathBuf::from(shell_manager.path());
}
match path.file_name() {
let destination_file_name = {
match destination.file_name() {
Some(name) => PathBuf::from(name),
None => {
return Err(ShellError::labeled_error(
"Rename aborted. Not a valid destination",
"Rename aborted. Not a valid destination",
name_span,
dst.span(),
))
}
}
@ -174,6 +172,8 @@ pub fn mv(
}
#[cfg(windows)]
{
use crate::utils::FileStructure;
let mut sources: FileStructure = FileStructure::new();
sources.walk_decorate(&entry)?;
@ -181,7 +181,7 @@ pub fn mv(
let strategy = |(source_file, depth_level)| {
let mut new_dst = destination.clone();
let path = dunce::canonicalize(&source_file).unwrap();
let path = dunce::canonicalize(&source_file)?;
let mut comps: Vec<_> = path
.components()
@ -196,10 +196,12 @@ pub fn mv(
new_dst.push(fragment);
}
(PathBuf::from(&source_file), PathBuf::from(new_dst))
Ok((PathBuf::from(&source_file), PathBuf::from(new_dst)))
};
for (ref src, ref dst) in sources.paths_applying_with(strategy) {
let sources = sources.paths_applying_with(strategy)?;
for (ref src, ref dst) in sources {
if src.is_dir() {
if !dst.exists() {
match std::fs::create_dir_all(dst) {
@ -284,7 +286,7 @@ pub fn mv(
return Err(ShellError::labeled_error(
"Rename aborted (directories found). Renaming in patterns not supported yet (try moving the directory directly)",
"Rename aborted (directories found). Renaming in patterns not supported yet (try moving the directory directly)",
args.source.tag,
src.tag,
));
}
@ -332,7 +334,7 @@ pub fn mv(
return Err(ShellError::labeled_error(
format!("Rename aborted. (Does {:?} exist?)", destination_file_name),
format!("Rename aborted. (Does {:?} exist?)", destination_file_name),
args.destination.span(),
dst.span(),
));
}
}

View File

@ -12,14 +12,6 @@ struct PickArgs {
pub struct Pick;
impl WholeStreamCommand for Pick {
fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
args.process(registry, pick)?.run()
}
fn name(&self) -> &str {
"pick"
}
@ -27,6 +19,14 @@ impl WholeStreamCommand for Pick {
fn signature(&self) -> Signature {
Signature::build("pick").rest()
}
fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
args.process(registry, pick)?.run()
}
}
fn pick(

View File

@ -10,7 +10,7 @@ pub struct Remove;
#[derive(Deserialize)]
pub struct RemoveArgs {
path: Tagged<PathBuf>,
target: Tagged<PathBuf>,
recursive: Tagged<bool>,
}
@ -36,32 +36,30 @@ impl PerItemCommand for Remove {
}
}
pub fn rm(
args: RemoveArgs,
context: &RunnablePerItemContext,
fn rm(
RemoveArgs { target, recursive }: RemoveArgs,
RunnablePerItemContext { name, .. }: &RunnablePerItemContext,
) -> Result<VecDeque<ReturnValue>, ShellError> {
let mut path = PathBuf::from(context.shell_manager.path());
let name_span = context.name;
let path = target.item.clone();
let name_span = name;
let file = &args.path.item.to_string_lossy();
let file = path.to_string_lossy();
if file == "." || file == ".." {
return Err(ShellError::labeled_error(
"Remove aborted. \".\" or \"..\" may not be removed.",
"Remove aborted. \".\" or \"..\" may not be removed.",
args.path.span(),
target.span(),
));
}
path.push(&args.path.item);
let entries: Vec<_> = match glob::glob(&path.to_string_lossy()) {
Ok(files) => files.collect(),
Err(_) => {
return Err(ShellError::labeled_error(
"Invalid pattern.",
"Invalid pattern.",
args.path.tag,
target.tag,
))
}
};
@ -73,17 +71,11 @@ pub fn rm(
source_dir.walk_decorate(&entry)?;
if source_dir.contains_files() && !args.recursive.item {
if source_dir.contains_files() && !recursive.item {
return Err(ShellError::labeled_error(
format!(
"{:?} is a directory. Try using \"--recursive\".",
&args.path.item.to_string_lossy()
),
format!(
"{:?} is a directory. Try using \"--recursive\".",
&args.path.item.to_string_lossy()
),
args.path.span(),
format!("{:?} is a directory. Try using \"--recursive\".", file),
format!("{:?} is a directory. Try using \"--recursive\".", file),
target.span(),
));
}
}
@ -94,9 +86,7 @@ pub fn rm(
match entry {
Ok(path) => {
let path_file_name = {
let p = &path;
match p.file_name() {
match path.file_name() {
Some(name) => PathBuf::from(name),
None => {
return Err(ShellError::labeled_error(
@ -112,7 +102,7 @@ pub fn rm(
source_dir.walk_decorate(&path)?;
if source_dir.contains_more_than_one_file() && !args.recursive.item {
if source_dir.contains_more_than_one_file() && !recursive.item {
return Err(ShellError::labeled_error(
format!(
"Directory {:?} found somewhere inside. Try using \"--recursive\".",
@ -122,7 +112,7 @@ pub fn rm(
"Directory {:?} found somewhere inside. Try using \"--recursive\".",
path_file_name
),
args.path.span(),
target.span(),
));
}

View File

@ -36,19 +36,26 @@ impl WholeStreamCommand for Save {
}
}
pub fn save(
fn save(
SaveArgs {
path,
raw: save_raw,
}: SaveArgs,
context: RunnableContext,
RunnableContext {
input,
name,
shell_manager,
source_map,
..
}: RunnableContext,
) -> Result<OutputStream, ShellError> {
let mut full_path = context.cwd();
let mut full_path = PathBuf::from(shell_manager.path());
let name_span = name;
if path.is_none() {
let source_map = context.source_map.clone();
let source_map = source_map.clone();
let stream = async_stream_block! {
let input: Vec<Tagged<Value>> = context.input.values.collect().await;
let input: Vec<Tagged<Value>> = input.values.collect().await;
// If there is no filename, check the metadata for the origin filename
if input.len() > 0 {
let origin = input[0].origin();
@ -61,7 +68,7 @@ pub fn save(
yield Err(ShellError::labeled_error(
"Save requires a filepath",
"needs path",
context.name,
name_span,
));
}
},
@ -69,7 +76,7 @@ pub fn save(
yield Err(ShellError::labeled_error(
"Save requires a filepath",
"needs path",
context.name,
name_span,
));
}
}
@ -77,139 +84,126 @@ pub fn save(
yield Err(ShellError::labeled_error(
"Save requires a filepath",
"needs path",
context.name,
name_span,
));
}
let contents = match full_path.extension() {
Some(x) if x == "csv" && !save_raw => {
if input.len() != 1 {
yield Err(ShellError::string(
"saving to csv requires a single object (or use --raw)",
));
}
to_csv_to_string(&value_to_csv_value(&input[0])).unwrap()
}
Some(x) if x == "toml" && !save_raw => {
if input.len() != 1 {
yield Err(ShellError::string(
"saving to toml requires a single object (or use --raw)",
));
}
toml::to_string(&value_to_toml_value(&input[0])).unwrap()
}
Some(x) if x == "json" && !save_raw => {
if input.len() != 1 {
yield Err(ShellError::string(
"saving to json requires a single object (or use --raw)",
));
}
serde_json::to_string(&value_to_json_value(&input[0])).unwrap()
}
Some(x) if x == "yml" && !save_raw => {
if input.len() != 1 {
yield Err(ShellError::string(
"saving to yml requires a single object (or use --raw)",
));
}
serde_yaml::to_string(&value_to_yaml_value(&input[0])).unwrap()
}
Some(x) if x == "yaml" && !save_raw => {
if input.len() != 1 {
yield Err(ShellError::string(
"saving to yaml requires a single object (or use --raw)",
));
}
serde_yaml::to_string(&value_to_yaml_value(&input[0])).unwrap()
}
_ => {
let mut save_data = String::new();
if input.len() > 0 {
let mut first = true;
for i in input.iter() {
if !first {
save_data.push_str("\n");
} else {
first = false;
}
save_data.push_str(&i.as_string().unwrap());
}
}
save_data
}
let content = if !save_raw {
to_string_for(full_path.extension(), &input)
} else {
string_from(&input)
};
let _ = std::fs::write(full_path, contents);
match content {
Ok(save_data) => match std::fs::write(full_path, save_data) {
Ok(o) => o,
Err(e) => yield Err(ShellError::string(e.to_string())),
},
Err(e) => yield Err(ShellError::string(e.to_string())),
}
};
Ok(OutputStream::new(stream))
} else {
full_path.push(path.unwrap().item());
if let Some(file) = path {
full_path.push(file.item());
}
let stream = async_stream_block! {
let input: Vec<Tagged<Value>> = context.input.values.collect().await;
let input: Vec<Tagged<Value>> = input.values.collect().await;
let contents = match full_path.extension() {
Some(x) if x == "csv" && !save_raw => {
if input.len() != 1 {
yield Err(ShellError::string(
"saving to csv requires a single object (or use --raw)",
));
}
to_csv_to_string(&value_to_csv_value(&input[0])).unwrap()
}
Some(x) if x == "toml" && !save_raw => {
if input.len() != 1 {
yield Err(ShellError::string(
"saving to toml requires a single object (or use --raw)",
));
}
toml::to_string(&value_to_toml_value(&input[0])).unwrap()
}
Some(x) if x == "json" && !save_raw => {
if input.len() != 1 {
yield Err(ShellError::string(
"saving to json requires a single object (or use --raw)",
));
}
serde_json::to_string(&value_to_json_value(&input[0])).unwrap()
}
Some(x) if x == "yml" && !save_raw => {
if input.len() != 1 {
yield Err(ShellError::string(
"saving to yml requires a single object (or use --raw)",
));
}
serde_yaml::to_string(&value_to_yaml_value(&input[0])).unwrap()
}
Some(x) if x == "yaml" && !save_raw => {
if input.len() != 1 {
yield Err(ShellError::string(
"saving to yaml requires a single object (or use --raw)",
));
}
serde_yaml::to_string(&value_to_yaml_value(&input[0])).unwrap()
}
_ => {
let mut save_data = String::new();
if input.len() > 0 {
let mut first = true;
for i in input.iter() {
if !first {
save_data.push_str("\n");
} else {
first = false;
}
save_data.push_str(&i.as_string().unwrap());
}
}
save_data
}
let content = if !save_raw {
to_string_for(full_path.extension(), &input)
} else {
string_from(&input)
};
let _ = std::fs::write(full_path, contents);
match content {
Ok(save_data) => match std::fs::write(full_path, save_data) {
Ok(o) => o,
Err(e) => yield Err(ShellError::string(e.to_string())),
},
Err(e) => yield Err(ShellError::string(e.to_string())),
}
};
Ok(OutputStream::new(stream))
}
}
fn string_from(input: &Vec<Tagged<Value>>) -> Result<String, ShellError> {
let mut save_data = String::new();
if input.len() > 0 {
let mut first = true;
for i in input.iter() {
if !first {
save_data.push_str("\n");
} else {
first = false;
}
if let Ok(data) = &i.as_string() {
save_data.push_str(data);
}
}
}
Ok(save_data)
}
fn to_string_for(
ext: Option<&std::ffi::OsStr>,
input: &Vec<Tagged<Value>>,
) -> Result<String, ShellError> {
let contents = match ext {
Some(x) if x == "csv" => {
if input.len() != 1 {
return Err(ShellError::string(
"saving to csv requires a single object (or use --raw)",
));
}
to_csv_to_string(&value_to_csv_value(&input[0]))?
}
Some(x) if x == "toml" => {
if input.len() != 1 {
return Err(ShellError::string(
"saving to toml requires a single object (or use --raw)",
));
}
toml::to_string(&value_to_toml_value(&input[0]))?
}
Some(x) if x == "json" => {
if input.len() != 1 {
return Err(ShellError::string(
"saving to json requires a single object (or use --raw)",
));
}
serde_json::to_string(&value_to_json_value(&input[0]))?
}
Some(x) if x == "yml" => {
if input.len() != 1 {
return Err(ShellError::string(
"saving to yml requires a single object (or use --raw)",
));
}
serde_yaml::to_string(&value_to_yaml_value(&input[0]))?
}
Some(x) if x == "yaml" => {
if input.len() != 1 {
return Err(ShellError::string(
"saving to yaml requires a single object (or use --raw)",
));
}
serde_yaml::to_string(&value_to_yaml_value(&input[0]))?
}
_ => {
return Err(ShellError::string(
"tried saving a single object with an unrecognized format.",
))
}
};
Ok(contents)
}

View File

@ -411,6 +411,16 @@ impl std::fmt::Display for ShellError {
impl std::error::Error for ShellError {}
impl std::convert::From<Box<dyn std::error::Error>> for ShellError {
fn from(input: Box<dyn std::error::Error>) -> ShellError {
ProximateShellError::String(StringError {
title: format!("{}", input),
error: Value::nothing(),
})
.start()
}
}
impl std::convert::From<std::io::Error> for ShellError {
fn from(input: std::io::Error) -> ShellError {
ProximateShellError::String(StringError {
@ -431,6 +441,17 @@ impl std::convert::From<subprocess::PopenError> for ShellError {
}
}
impl std::convert::From<serde_yaml::Error> for ShellError {
fn from(input: serde_yaml::Error) -> ShellError {
ProximateShellError::String(StringError {
title: format!("{:?}", input),
error: Value::nothing(),
})
.start()
}
}
impl std::convert::From<toml::ser::Error> for ShellError {
fn from(input: toml::ser::Error) -> ShellError {
ProximateShellError::String(StringError {

View File

@ -105,9 +105,9 @@ impl FileStructure {
self.root = path.to_path_buf();
}
pub fn paths_applying_with<F>(&mut self, to: F) -> Vec<(PathBuf, PathBuf)>
pub fn paths_applying_with<F>(&mut self, to: F) -> Result<Vec<(PathBuf, PathBuf)>, Box<dyn std::error::Error>>
where
F: Fn((PathBuf, usize)) -> (PathBuf, PathBuf),
F: Fn((PathBuf, usize)) -> Result<(PathBuf, PathBuf), Box<dyn std::error::Error>>,
{
self.resources
.iter()

View File

@ -2,7 +2,7 @@ mod helpers;
use h::{in_directory as cwd, Playground};
use helpers as h;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
#[test]
fn creates_directory() {
@ -19,11 +19,25 @@ fn creates_directory() {
}
#[test]
fn creates_intermediary_directories() {
fn accepts_and_creates_directories() {
let sandbox = Playground::setup_for("mkdir_test_2").test_dir_name();
let full_path = format!("{}/{}", Playground::root(), sandbox);
nu!(_output, cwd(&full_path), "mkdir dir_1 dir_2 dir_3");
assert!(h::files_exist_at(
vec![Path::new("dir_1"), Path::new("dir_2"), Path::new("dir_3")],
PathBuf::from(&full_path)
));
}
#[test]
fn creates_intermediary_directories() {
let sandbox = Playground::setup_for("mkdir_test_3").test_dir_name();
let full_path = format!("{}/{}", Playground::root(), sandbox);
nu!(
_output,
cwd(&full_path),