Unwrap cleanup mitigation.

This commit is contained in:
Andrés N. Robalino 2019-08-21 07:08:23 -05:00
parent 8b79b28971
commit 8d5fd6f379
7 changed files with 255 additions and 221 deletions

View File

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

View File

@ -10,8 +10,8 @@ pub struct Cpy;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct CopyArgs { pub struct CopyArgs {
source: Tagged<PathBuf>, src: Tagged<PathBuf>,
destination: Tagged<PathBuf>, dst: Tagged<PathBuf>,
recursive: Tagged<bool>, recursive: Tagged<bool>,
} }
@ -32,44 +32,43 @@ impl PerItemCommand for Cpy {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("cp") Signature::build("cp")
.required("source", SyntaxType::Path) .required("src", SyntaxType::Path)
.required("destination", SyntaxType::Path) .required("dst", SyntaxType::Path)
.named("file", SyntaxType::Any) .named("file", SyntaxType::Any)
.switch("recursive") .switch("recursive")
} }
} }
pub fn cp( fn cp(
args: CopyArgs, CopyArgs {
context: &RunnablePerItemContext, src,
dst,
recursive,
}: CopyArgs,
RunnablePerItemContext { name, .. }: &RunnablePerItemContext,
) -> Result<VecDeque<ReturnValue>, ShellError> { ) -> Result<VecDeque<ReturnValue>, ShellError> {
let mut source = PathBuf::from(context.shell_manager.path()); let source = src.item.clone();
let mut destination = PathBuf::from(context.shell_manager.path()); let mut destination = dst.item.clone();
let name_span = context.name; let name_span = name;
source.push(&args.source.item); let sources: Vec<_> = match glob::glob(&source.to_string_lossy()) {
Ok(files) => files.collect(),
destination.push(&args.destination.item); Err(_) => {
return Err(ShellError::labeled_error(
let sources = glob::glob(&source.to_string_lossy()); "Invalid pattern.",
"Invalid pattern.",
if sources.is_err() { src.tag,
return Err(ShellError::labeled_error( ))
"Invalid pattern.", }
"Invalid pattern.", };
args.source.tag,
));
}
let sources: Vec<_> = sources.unwrap().collect();
if sources.len() == 1 { if sources.len() == 1 {
if let Ok(entry) = &sources[0] { if let Ok(entry) = &sources[0] {
if entry.is_dir() && !args.recursive.item { if entry.is_dir() && !recursive.item {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"is a directory (not copied). Try using \"--recursive\".", "is a directory (not copied). Try using \"--recursive\".",
"is a directory (not copied). Try using \"--recursive\".", "is a directory (not copied). Try using \"--recursive\".",
args.source.tag, src.tag,
)); ));
} }
@ -167,7 +166,16 @@ pub fn cp(
} }
} }
} else { } 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) { match std::fs::create_dir_all(&destination) {
Err(e) => { Err(e) => {
@ -234,18 +242,31 @@ pub fn cp(
} }
} else { } else {
if destination.exists() { 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( 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)",
"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 { for entry in sources {
if let Ok(entry) = entry { if let Ok(entry) = entry {
let mut to = PathBuf::from(&destination); 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() { if entry.is_file() {
match std::fs::copy(&entry, &to) { match std::fs::copy(&entry, &to) {
@ -253,7 +274,7 @@ pub fn cp(
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
e.to_string(), e.to_string(),
e.to_string(), e.to_string(),
args.source.tag, src.tag,
)); ));
} }
Ok(o) => o, Ok(o) => o,
@ -262,16 +283,23 @@ pub fn cp(
} }
} }
} else { } 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( return Err(ShellError::labeled_error(
format!( format!("Copy aborted. (Does {:?} exist?)", destination_file_name),
"Copy aborted. (Does {:?} exist?)", format!("Copy aborted. (Does {:?} exist?)", destination_file_name),
&destination.file_name().unwrap() &dst.span(),
),
format!(
"Copy aborted. (Does {:?} exist?)",
&destination.file_name().unwrap()
),
args.destination.span(),
)); ));
} }
} }

View File

@ -5,15 +5,12 @@ use crate::parser::registry::{CommandRegistry, Signature};
use crate::prelude::*; use crate::prelude::*;
use std::path::PathBuf; use std::path::PathBuf;
#[cfg(windows)]
use crate::utils::FileStructure;
pub struct Move; pub struct Move;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct MoveArgs { pub struct MoveArgs {
source: Tagged<PathBuf>, src: Tagged<PathBuf>,
destination: Tagged<PathBuf>, dst: Tagged<PathBuf>,
} }
impl PerItemCommand for Move { impl PerItemCommand for Move {
@ -39,17 +36,16 @@ impl PerItemCommand for Move {
} }
} }
pub fn mv( fn mv(
args: MoveArgs, MoveArgs { src, dst }: MoveArgs,
context: &RunnablePerItemContext, RunnablePerItemContext {
name,
shell_manager,
}: &RunnablePerItemContext,
) -> Result<VecDeque<ReturnValue>, ShellError> { ) -> Result<VecDeque<ReturnValue>, ShellError> {
let mut source = PathBuf::from(context.shell_manager.path()); let source = src.item.clone();
let mut destination = PathBuf::from(context.shell_manager.path()); let mut destination = dst.item.clone();
let name_span = context.name; let name_span = name;
source.push(&args.source.item);
destination.push(&args.destination.item);
let sources: Vec<_> = match glob::glob(&source.to_string_lossy()) { let sources: Vec<_> = match glob::glob(&source.to_string_lossy()) {
Ok(files) => files.collect(), Ok(files) => files.collect(),
@ -57,21 +53,23 @@ pub fn mv(
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Invalid pattern.", "Invalid pattern.",
"Invalid pattern.", "Invalid pattern.",
args.source.tag, src.tag,
)) ))
} }
}; };
let destination_file_name = { if "." == destination.to_string_lossy() {
let path = &destination; destination = PathBuf::from(shell_manager.path());
}
match path.file_name() { let destination_file_name = {
match destination.file_name() {
Some(name) => PathBuf::from(name), Some(name) => PathBuf::from(name),
None => { None => {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Rename aborted. Not a valid destination", "Rename aborted. Not a valid destination",
"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)] #[cfg(windows)]
{ {
use crate::utils::FileStructure;
let mut sources: FileStructure = FileStructure::new(); let mut sources: FileStructure = FileStructure::new();
sources.walk_decorate(&entry)?; sources.walk_decorate(&entry)?;
@ -284,7 +284,7 @@ pub fn mv(
return Err(ShellError::labeled_error( 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)",
"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 +332,7 @@ pub fn mv(
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
format!("Rename aborted. (Does {:?} exist?)", destination_file_name), format!("Rename aborted. (Does {:?} exist?)", destination_file_name),
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; pub struct Pick;
impl WholeStreamCommand for Pick { impl WholeStreamCommand for Pick {
fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
args.process(registry, pick)?.run()
}
fn name(&self) -> &str { fn name(&self) -> &str {
"pick" "pick"
} }
@ -27,6 +19,14 @@ impl WholeStreamCommand for Pick {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("pick").rest() Signature::build("pick").rest()
} }
fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
args.process(registry, pick)?.run()
}
} }
fn pick( fn pick(

View File

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

View File

@ -36,19 +36,26 @@ impl WholeStreamCommand for Save {
} }
} }
pub fn save( fn save(
SaveArgs { SaveArgs {
path, path,
raw: save_raw, raw: save_raw,
}: SaveArgs, }: SaveArgs,
context: RunnableContext, RunnableContext {
input,
name,
shell_manager,
source_map,
..
}: RunnableContext,
) -> Result<OutputStream, ShellError> { ) -> 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() { if path.is_none() {
let source_map = context.source_map.clone(); let source_map = source_map.clone();
let stream = async_stream_block! { 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 there is no filename, check the metadata for the origin filename
if input.len() > 0 { if input.len() > 0 {
let origin = input[0].origin(); let origin = input[0].origin();
@ -61,7 +68,7 @@ pub fn save(
yield Err(ShellError::labeled_error( yield Err(ShellError::labeled_error(
"Save requires a filepath", "Save requires a filepath",
"needs path", "needs path",
context.name, name_span,
)); ));
} }
}, },
@ -69,7 +76,7 @@ pub fn save(
yield Err(ShellError::labeled_error( yield Err(ShellError::labeled_error(
"Save requires a filepath", "Save requires a filepath",
"needs path", "needs path",
context.name, name_span,
)); ));
} }
} }
@ -77,139 +84,126 @@ pub fn save(
yield Err(ShellError::labeled_error( yield Err(ShellError::labeled_error(
"Save requires a filepath", "Save requires a filepath",
"needs path", "needs path",
context.name, name_span,
)); ));
} }
let contents = match full_path.extension() { let content = if !save_raw {
Some(x) if x == "csv" && !save_raw => { to_string_for(full_path.extension(), &input)
if input.len() != 1 { } else {
yield Err(ShellError::string( string_from(&input)
"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 _ = 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)) Ok(OutputStream::new(stream))
} else { } else {
full_path.push(path.unwrap().item()); if let Some(file) = path {
full_path.push(file.item());
}
let stream = async_stream_block! { 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() { let content = if !save_raw {
Some(x) if x == "csv" && !save_raw => { to_string_for(full_path.extension(), &input)
if input.len() != 1 { } else {
yield Err(ShellError::string( string_from(&input)
"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 _ = 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)) 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::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 { impl std::convert::From<std::io::Error> for ShellError {
fn from(input: std::io::Error) -> ShellError { fn from(input: std::io::Error) -> ShellError {
ProximateShellError::String(StringError { 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 { impl std::convert::From<toml::ser::Error> for ShellError {
fn from(input: toml::ser::Error) -> ShellError { fn from(input: toml::ser::Error) -> ShellError {
ProximateShellError::String(StringError { ProximateShellError::String(StringError {