Move 'start' to use ShellError (#1743)

* Move 'start' to use ShellError

* Remove unnecessary changes

* Add missing macOS change

* Add default

* More fixed

* More fixed
This commit is contained in:
Jonathan Turner 2020-05-10 08:08:53 +12:00 committed by GitHub
parent ccd5d69fd1
commit 0f0847e45b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 115 additions and 103 deletions

1
Cargo.lock generated
View File

@ -2432,7 +2432,6 @@ dependencies = [
name = "nu_plugin_start" name = "nu_plugin_start"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"ansi_term 0.12.1",
"nu-build", "nu-build",
"nu-errors", "nu-errors",
"nu-plugin", "nu-plugin",

View File

@ -14,9 +14,10 @@ nu-plugin = { path = "../nu-plugin", version = "0.13.0" }
nu-protocol = { path = "../nu-protocol", version = "0.13.0" } nu-protocol = { path = "../nu-protocol", version = "0.13.0" }
nu-source = { path = "../nu-source", version = "0.13.0" } nu-source = { path = "../nu-source", version = "0.13.0" }
nu-errors = { path = "../nu-errors", version = "0.13.0" } nu-errors = { path = "../nu-errors", version = "0.13.0" }
ansi_term = "0.12.1"
url = "2.1.1" url = "2.1.1"
open = "1.4.0" open = "1.4.0"
[build-dependencies] [build-dependencies]
nu-build = { version = "0.13.0", path = "../nu-build" } nu-build = { version = "0.13.0", path = "../nu-build" }
nu-errors = { version = "0.13.0", path = "../nu-errors" }
nu-source = { version = "0.13.0", path = "../nu-source" }

View File

@ -2,8 +2,5 @@ use nu_plugin::serve_plugin;
use nu_plugin_start::Start; use nu_plugin_start::Start;
fn main() { fn main() {
serve_plugin(&mut Start { serve_plugin(&mut Start::new());
filenames: vec![],
application: None,
});
} }

View File

@ -1,6 +1,6 @@
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_plugin::Plugin; use nu_plugin::Plugin;
use nu_protocol::{CallInfo, Signature, SyntaxShape, Value}; use nu_protocol::{CallInfo, ReturnValue, Signature, SyntaxShape};
use crate::start::Start; use crate::start::Start;
@ -14,12 +14,11 @@ impl Plugin for Start {
SyntaxShape::String, SyntaxShape::String,
"Specifies the application used for opening the files/directories/urls", "Specifies the application used for opening the files/directories/urls",
Some('a'), Some('a'),
)) )
.filter())
} }
fn sink(&mut self, call_info: CallInfo, input: Vec<Value>) { fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
self.parse(call_info, input); self.parse(call_info)?;
if let Err(e) = self.exec() { self.exec().map(|_| vec![])
println!("{}", e);
}
} }
} }

View File

@ -1,78 +1,79 @@
use ansi_term::Color; use nu_errors::ShellError;
use nu_protocol::{CallInfo, Value}; use nu_protocol::CallInfo;
use std::error::Error; use nu_source::{Tag, Tagged, TaggedItem};
use std::fmt;
use std::path::Path; use std::path::Path;
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
#[derive(Default)]
pub struct Start { pub struct Start {
pub filenames: Vec<String>, pub tag: Tag,
pub filenames: Vec<Tagged<String>>,
pub application: Option<String>, pub application: Option<String>,
} }
#[derive(Debug)]
pub struct StartError {
msg: String,
}
impl StartError {
fn new(msg: &str) -> StartError {
StartError {
msg: msg.to_owned(),
}
}
}
impl fmt::Display for StartError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}: {}",
Color::Red.bold().paint("start error"),
self.msg
)
}
}
impl Error for StartError {}
impl Start { impl Start {
pub fn parse(&mut self, call_info: CallInfo, input: Vec<Value>) { pub fn new() -> Start {
input.iter().for_each(|val| { Start {
if val.is_some() { tag: Tag::unknown(),
self.parse_value(val); filenames: vec![],
} application: None,
});
self.parse_filenames(&call_info);
self.parse_application(&call_info);
}
fn add_filename(&mut self, filename: String) {
if Path::new(&filename).exists() || url::Url::parse(&filename).is_ok() {
self.filenames.push(filename);
} else {
print_warning(format!(
"The file '{}' does not exist",
Color::White.bold().paint(filename)
));
} }
} }
fn parse_filenames(&mut self, call_info: &CallInfo) { pub fn parse(&mut self, call_info: CallInfo) -> Result<(), ShellError> {
self.tag = call_info.name_tag.clone();
self.parse_filenames(&call_info)?;
self.parse_application(&call_info);
Ok(())
}
fn add_filename(&mut self, filename: Tagged<String>) -> Result<(), ShellError> {
if Path::new(&filename.item).exists() || url::Url::parse(&filename.item).is_ok() {
self.filenames.push(filename);
Ok(())
} else {
Err(ShellError::labeled_error(
format!("The file '{}' does not exist", filename.item),
"doesn't exist",
filename.tag,
))
}
}
fn parse_filenames(&mut self, call_info: &CallInfo) -> Result<(), ShellError> {
let candidates = match &call_info.args.positional { let candidates = match &call_info.args.positional {
Some(values) => values Some(values) => {
.iter() let mut result = vec![];
.map(|val| val.as_string())
.collect::<Result<Vec<String>, _>>() for value in values.iter() {
.unwrap_or_else(|_| vec![]), result.push(value.as_string()?.tagged(value.tag.clone()));
None => vec![], }
if result.is_empty() {
return Err(ShellError::labeled_error(
"No filename(s) given",
"no filename(s) given",
self.tag.span,
));
}
result
}
None => {
return Err(ShellError::labeled_error(
"No filename(s) given",
"no filename(s) given",
self.tag.span,
))
}
}; };
for candidate in candidates { for candidate in candidates {
self.add_filename(candidate); self.add_filename(candidate)?;
} }
Ok(())
} }
fn parse_application(&mut self, call_info: &CallInfo) { fn parse_application(&mut self, call_info: &CallInfo) {
@ -86,45 +87,47 @@ impl Start {
}; };
} }
pub fn parse_value(&mut self, input: &Value) {
if let Ok(filename) = input.as_string() {
self.add_filename(filename);
} else {
print_warning(format!("Could not convert '{:?}' to string", input));
}
}
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub fn exec(&mut self) -> Result<(), StartError> { pub fn exec(&mut self) -> Result<(), ShellError> {
let mut args = vec![]; let mut args = vec![];
args.append(&mut self.filenames); args.append(
&mut self
.filenames
.iter()
.map(|x| x.item.clone())
.collect::<Vec<_>>(),
);
if let Some(app_name) = &self.application { if let Some(app_name) = &self.application {
args.append(&mut vec![String::from("-a"), app_name.to_string()]); args.append(&mut vec![String::from("-a"), app_name.to_string()]);
} }
exec_cmd("open", &args) exec_cmd("open", &args, self.tag.clone())
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub fn exec(&mut self) -> Result<(), StartError> { pub fn exec(&mut self) -> Result<(), ShellError> {
if let Some(app_name) = &self.application { if let Some(app_name) = &self.application {
for file in &self.filenames { for file in &self.filenames {
match open::with(file, app_name) { match open::with(&file.item, app_name) {
Ok(_) => continue, Ok(_) => continue,
Err(_) => { Err(_) => {
return Err(StartError::new( return Err(ShellError::labeled_error(
"Failed to open file with specified application", "Failed to open file with specified application",
"can't open with specified application",
file.tag.span,
)) ))
} }
} }
} }
} else { } else {
for file in &self.filenames { for file in &self.filenames {
match open::that(file) { match open::that(&file.item) {
Ok(_) => continue, Ok(_) => continue,
Err(_) => { Err(_) => {
return Err(StartError::new( return Err(ShellError::labeled_error(
"Failed to open file with default application", "Failed to open file with default application",
"can't open with default application",
file.tag.span,
)) ))
} }
} }
@ -134,35 +137,43 @@ impl Start {
} }
#[cfg(not(any(target_os = "windows", target_os = "macos")))] #[cfg(not(any(target_os = "windows", target_os = "macos")))]
pub fn exec(&mut self) -> Result<(), StartError> { pub fn exec(&mut self) -> Result<(), ShellError> {
let mut args = vec![]; let mut args = vec![];
args.append(&mut self.filenames); args.append(
&mut self
.filenames
.iter()
.map(|x| x.item.clone())
.collect::<Vec<_>>(),
);
if let Some(app_name) = &self.application { if let Some(app_name) = &self.application {
exec_cmd(&app_name, &args) exec_cmd(&app_name, &args, self.tag.clone())
} else { } else {
for cmd in &["xdg-open", "gnome-open", "kde-open", "wslview"] { for cmd in &["xdg-open", "gnome-open", "kde-open", "wslview"] {
if exec_cmd(cmd, &args).is_err() { if exec_cmd(cmd, &args, self.tag.clone()).is_err() {
continue; continue;
} else { } else {
return Ok(()); return Ok(());
} }
} }
Err(StartError::new( Err(ShellError::labeled_error(
"Failed to open file(s) with xdg-open. gnome-open, kde-open, and wslview", "Failed to open file(s) with xdg-open. gnome-open, kde-open, and wslview",
"failed to open xdg-open. gnome-open, kde-open, and wslview",
self.tag.span,
)) ))
} }
} }
} }
fn print_warning(msg: String) {
println!("{}: {}", Color::Yellow.bold().paint("warning"), msg);
}
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
fn exec_cmd(cmd: &str, args: &[String]) -> Result<(), StartError> { fn exec_cmd(cmd: &str, args: &[String], tag: Tag) -> Result<(), ShellError> {
if args.is_empty() { if args.is_empty() {
return Err(StartError::new("No file(s) or application provided")); return Err(ShellError::labeled_error(
"No file(s) or application provided",
"no file(s) or application provided",
tag,
));
} }
let status = match Command::new(cmd) let status = match Command::new(cmd)
.stdout(Stdio::null()) .stdout(Stdio::null())
@ -171,13 +182,21 @@ fn exec_cmd(cmd: &str, args: &[String]) -> Result<(), StartError> {
.status() .status()
{ {
Ok(exit_code) => exit_code, Ok(exit_code) => exit_code,
Err(_) => return Err(StartError::new("Failed to run native open syscall")), Err(_) => {
return Err(ShellError::labeled_error(
"Failed to run native open syscall",
"failed to run native open call",
tag,
))
}
}; };
if status.success() { if status.success() {
Ok(()) Ok(())
} else { } else {
Err(StartError::new( Err(ShellError::labeled_error(
"Failed to run start. Hint: The file(s)/application may not exist", "Failed to run start. Hint: The file(s)/application may not exist",
"failed to run",
tag,
)) ))
} }
} }

View File

@ -2,8 +2,5 @@ use nu_plugin::serve_plugin;
use nu_plugin_start::Start; use nu_plugin_start::Start;
fn main() { fn main() {
serve_plugin(&mut Start { serve_plugin(&mut Start::new());
filenames: vec![],
application: None,
});
} }