nushell/src/commands/cp.rs

317 lines
11 KiB
Rust
Raw Normal View History

use crate::commands::command::RunnablePerItemContext;
2019-07-22 04:23:02 +02:00
use crate::errors::ShellError;
use crate::parser::hir::SyntaxType;
2019-08-09 06:51:21 +02:00
use crate::parser::registry::{CommandRegistry, Signature};
2019-07-22 04:23:02 +02:00
use crate::prelude::*;
2019-08-14 22:04:44 +02:00
use crate::utils::FileStructure;
use std::path::PathBuf;
2019-07-22 04:23:02 +02:00
2019-08-14 22:04:44 +02:00
pub struct Cpy;
2019-07-22 04:23:02 +02:00
#[derive(Deserialize)]
pub struct CopyArgs {
2019-08-21 14:08:23 +02:00
src: Tagged<PathBuf>,
dst: Tagged<PathBuf>,
recursive: Tagged<bool>,
}
2019-08-15 07:02:02 +02:00
impl PerItemCommand for Cpy {
2019-08-09 06:51:21 +02:00
fn run(
&self,
2019-08-15 07:02:02 +02:00
call_info: &CallInfo,
_registry: &CommandRegistry,
shell_manager: &ShellManager,
_input: Tagged<Value>,
2019-08-15 07:02:02 +02:00
) -> Result<VecDeque<ReturnValue>, ShellError> {
call_info.process(shell_manager, cp)?.run()
2019-07-22 04:23:02 +02:00
}
fn name(&self) -> &str {
"cp"
}
2019-08-09 06:51:21 +02:00
fn signature(&self) -> Signature {
Signature::build("cp")
2019-08-21 14:08:23 +02:00
.required("src", SyntaxType::Path)
.required("dst", SyntaxType::Path)
2019-08-09 06:51:21 +02:00
.named("file", SyntaxType::Any)
.switch("recursive")
2019-07-22 04:23:02 +02:00
}
}
2019-08-21 14:08:23 +02:00
fn cp(
CopyArgs {
src,
dst,
recursive,
}: CopyArgs,
RunnablePerItemContext { name, .. }: &RunnablePerItemContext,
2019-08-15 07:02:02 +02:00
) -> Result<VecDeque<ReturnValue>, ShellError> {
2019-08-21 14:08:23 +02:00
let source = src.item.clone();
let mut destination = dst.item.clone();
let name_span = name;
2019-07-22 04:23:02 +02:00
2019-08-21 14:08:23 +02:00
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,
))
}
};
2019-08-06 09:05:47 +02:00
if sources.len() == 1 {
if let Ok(entry) = &sources[0] {
2019-08-21 14:08:23 +02:00
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\".",
2019-08-21 14:08:23 +02:00
src.tag,
));
}
2019-08-06 09:05:47 +02:00
let mut sources: FileStructure = FileStructure::new();
sources.walk_decorate(&entry)?;
if entry.is_file() {
let strategy = |(source_file, _depth_level)| {
if destination.exists() {
let mut new_dst = dunce::canonicalize(destination.clone())?;
if let Some(name) = entry.file_name() {
new_dst.push(name);
}
Ok((source_file, new_dst))
2019-08-06 09:05:47 +02:00
} else {
Ok((source_file, destination.clone()))
}
};
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) => {
return Err(ShellError::labeled_error(
e.to_string(),
e.to_string(),
name_span,
));
}
Ok(o) => o,
};
2019-08-06 09:05:47 +02:00
}
}
}
if entry.is_dir() {
if !destination.exists() {
match std::fs::create_dir_all(&destination) {
Err(e) => {
return Err(ShellError::labeled_error(
e.to_string(),
e.to_string(),
name_span,
));
2019-08-06 09:05:47 +02:00
}
Ok(o) => o,
};
2019-08-06 09:05:47 +02:00
let strategy = |(source_file, depth_level)| {
let mut new_dst = destination.clone();
let path = dunce::canonicalize(&source_file)?;
2019-08-06 09:05:47 +02:00
let mut comps: Vec<_> = path
.components()
.map(|fragment| fragment.as_os_str())
.rev()
.take(1 + depth_level)
.collect();
2019-08-06 09:05:47 +02:00
comps.reverse();
for fragment in comps.iter() {
new_dst.push(fragment);
}
Ok((PathBuf::from(&source_file), PathBuf::from(new_dst)))
};
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) {
Err(e) => {
2019-08-06 09:05:47 +02:00
return Err(ShellError::labeled_error(
e.to_string(),
e.to_string(),
name_span,
2019-08-06 09:05:47 +02:00
));
}
Ok(o) => o,
};
}
}
if src.is_file() {
match std::fs::copy(src, dst) {
Err(e) => {
return Err(ShellError::labeled_error(
e.to_string(),
e.to_string(),
name_span,
));
2019-08-06 09:05:47 +02:00
}
Ok(o) => o,
};
}
}
} else {
2019-08-21 14:08:23 +02:00
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,
))
}
}
2019-08-06 09:05:47 +02:00
match std::fs::create_dir_all(&destination) {
Err(e) => {
return Err(ShellError::labeled_error(
e.to_string(),
e.to_string(),
name_span,
));
}
Ok(o) => o,
};
2019-08-06 09:05:47 +02:00
let strategy = |(source_file, depth_level)| {
let mut new_dst = dunce::canonicalize(&destination)?;
let path = dunce::canonicalize(&source_file)?;
2019-08-06 09:05:47 +02:00
let mut comps: Vec<_> = path
.components()
.map(|fragment| fragment.as_os_str())
.rev()
.take(1 + depth_level)
.collect();
2019-08-06 09:05:47 +02:00
comps.reverse();
2019-08-06 09:05:47 +02:00
for fragment in comps.iter() {
new_dst.push(fragment);
}
2019-08-06 09:05:47 +02:00
Ok((PathBuf::from(&source_file), PathBuf::from(new_dst)))
};
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) {
Err(e) => {
return Err(ShellError::labeled_error(
e.to_string(),
e.to_string(),
name_span,
));
}
Ok(o) => o,
};
2019-08-06 09:05:47 +02:00
}
}
if src.is_file() {
match std::fs::copy(src, dst) {
Err(e) => {
return Err(ShellError::labeled_error(
e.to_string(),
e.to_string(),
name_span,
));
}
Ok(o) => o,
};
}
2019-08-06 09:05:47 +02:00
}
}
}
}
} else {
if destination.exists() {
2019-08-21 14:08:23 +02:00
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)",
2019-08-21 14:08:23 +02:00
src.tag,
));
2019-08-06 09:05:47 +02:00
}
for entry in sources {
if let Ok(entry) = entry {
let mut to = PathBuf::from(&destination);
2019-08-21 14:08:23 +02:00
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,
))
}
}
2019-08-06 09:05:47 +02:00
2019-08-14 22:04:44 +02:00
if entry.is_file() {
match std::fs::copy(&entry, &to) {
Err(e) => {
return Err(ShellError::labeled_error(
e.to_string(),
e.to_string(),
2019-08-21 14:08:23 +02:00
src.tag,
2019-08-14 22:04:44 +02:00
));
}
Ok(o) => o,
};
}
}
}
} else {
2019-08-21 14:08:23 +02:00
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(
2019-08-21 14:08:23 +02:00
format!("Copy aborted. (Does {:?} exist?)", destination_file_name),
format!("Copy aborted. (Does {:?} exist?)", destination_file_name),
&dst.span(),
));
2019-07-22 04:23:02 +02:00
}
}
2019-08-15 07:02:02 +02:00
Ok(VecDeque::new())
}