mirror of
https://github.com/nushell/nushell.git
synced 2025-06-30 06:30:08 +02:00
Move old plugins (#4721)
This commit is contained in:
26
crates/old/nu_plugin_start/Cargo.toml
Normal file
26
crates/old/nu_plugin_start/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
||||
[package]
|
||||
authors = ["The Nu Project Contributors"]
|
||||
description = "A plugin to open files/URLs directly from Nushell"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
name = "nu_plugin_start"
|
||||
version = "0.59.1"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
glob = "0.3.0"
|
||||
nu-errors = { path="../nu-errors", version = "0.59.1" }
|
||||
nu-plugin = { path="../nu-plugin", version = "0.59.1" }
|
||||
nu-protocol = { path="../nu-protocol", version = "0.59.1" }
|
||||
nu-source = { path="../nu-source", version = "0.59.1" }
|
||||
url = "2.2.0"
|
||||
webbrowser = "0.5.5"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
open = "1.4.0"
|
||||
|
||||
[build-dependencies]
|
||||
nu-errors = { version = "0.59.1", path="../nu-errors" }
|
||||
nu-source = { version = "0.59.1", path="../nu-source" }
|
4
crates/old/nu_plugin_start/src/lib.rs
Normal file
4
crates/old/nu_plugin_start/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
||||
mod nu;
|
||||
mod start;
|
||||
|
||||
pub use start::Start;
|
6
crates/old/nu_plugin_start/src/main.rs
Normal file
6
crates/old/nu_plugin_start/src/main.rs
Normal file
@ -0,0 +1,6 @@
|
||||
use nu_plugin::serve_plugin;
|
||||
use nu_plugin_start::Start;
|
||||
|
||||
fn main() {
|
||||
serve_plugin(&mut Start::new());
|
||||
}
|
28
crates/old/nu_plugin_start/src/nu/mod.rs
Normal file
28
crates/old/nu_plugin_start/src/nu/mod.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use nu_errors::ShellError;
|
||||
use nu_plugin::Plugin;
|
||||
use nu_protocol::{CallInfo, ReturnValue, Signature, SyntaxShape};
|
||||
|
||||
use crate::start::Start;
|
||||
|
||||
impl Plugin for Start {
|
||||
fn config(&mut self) -> Result<Signature, ShellError> {
|
||||
Ok(Signature::build("start")
|
||||
.desc("Opens each file/directory/URL using the default application")
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::String,
|
||||
"files/urls/directories to open",
|
||||
)
|
||||
.named(
|
||||
"application",
|
||||
SyntaxShape::String,
|
||||
"Specifies the application used for opening the files/directories/urls",
|
||||
Some('a'),
|
||||
)
|
||||
.filter())
|
||||
}
|
||||
fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
|
||||
self.parse(call_info)?;
|
||||
self.exec().map(|_| Vec::new())
|
||||
}
|
||||
}
|
260
crates/old/nu_plugin_start/src/start.rs
Normal file
260
crates/old/nu_plugin_start/src/start.rs
Normal file
@ -0,0 +1,260 @@
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{CallInfo, Value};
|
||||
use nu_source::{Tag, Tagged, TaggedItem};
|
||||
use std::path::Path;
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Start {
|
||||
pub tag: Tag,
|
||||
pub filenames: Vec<Tagged<String>>,
|
||||
pub application: Option<String>,
|
||||
}
|
||||
|
||||
impl Start {
|
||||
pub fn new() -> Start {
|
||||
Start {
|
||||
tag: Tag::unknown(),
|
||||
filenames: vec![],
|
||||
application: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(&mut self, call_info: CallInfo) -> Result<(), ShellError> {
|
||||
self.tag = call_info.name_tag.clone();
|
||||
self.parse_input_parameters(&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 glob_to_values(&self, value: &Value) -> Result<Vec<Tagged<String>>, ShellError> {
|
||||
let mut result = vec![];
|
||||
match glob::glob(&value.as_string()?) {
|
||||
Ok(paths) => {
|
||||
for path_result in paths {
|
||||
match path_result {
|
||||
Ok(path) => result
|
||||
.push(path.to_string_lossy().to_string().tagged(value.tag.clone())),
|
||||
Err(glob_error) => {
|
||||
return Err(ShellError::labeled_error(
|
||||
glob_error.to_string(),
|
||||
"glob error",
|
||||
value.tag.clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(pattern_error) => {
|
||||
return Err(ShellError::labeled_error(
|
||||
pattern_error.to_string(),
|
||||
"invalid pattern",
|
||||
value.tag.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn parse_input_parameters(&mut self, call_info: &CallInfo) -> Result<(), ShellError> {
|
||||
let candidates = match &call_info.args.positional {
|
||||
Some(values) => {
|
||||
let mut result = vec![];
|
||||
|
||||
for value in values {
|
||||
let val_str = value.as_string();
|
||||
match val_str {
|
||||
Ok(s) => {
|
||||
if s.to_ascii_lowercase().starts_with("http")
|
||||
|| s.to_ascii_lowercase().starts_with("https")
|
||||
{
|
||||
if webbrowser::open(&s).is_ok() {
|
||||
result.push("http/web".to_string().tagged_unknown())
|
||||
} else {
|
||||
return Err(ShellError::labeled_error(
|
||||
&format!("error opening {}", &s),
|
||||
"error opening url",
|
||||
self.tag.span,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
let res = self.glob_to_values(value)?;
|
||||
result.extend(res);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(ShellError::labeled_error(
|
||||
e.to_string(),
|
||||
"no input given",
|
||||
self.tag.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result.is_empty() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"No input given",
|
||||
"no input given",
|
||||
self.tag.span,
|
||||
));
|
||||
}
|
||||
result
|
||||
}
|
||||
None => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"No input given",
|
||||
"no input given",
|
||||
self.tag.span,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
for candidate in candidates {
|
||||
if !candidate.contains("http/web") {
|
||||
self.add_filename(candidate)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_application(&mut self, call_info: &CallInfo) {
|
||||
self.application = if let Some(app) = call_info.args.get("application") {
|
||||
match app.as_string() {
|
||||
Ok(name) => Some(name),
|
||||
Err(_) => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn exec(&mut self) -> Result<(), ShellError> {
|
||||
let mut args = vec![];
|
||||
args.append(
|
||||
&mut self
|
||||
.filenames
|
||||
.iter()
|
||||
.map(|x| x.item.clone())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
if let Some(app_name) = &self.application {
|
||||
args.append(&mut vec![String::from("-a"), app_name.to_string()]);
|
||||
}
|
||||
exec_cmd("open", &args, self.tag.clone())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn exec(&mut self) -> Result<(), ShellError> {
|
||||
if let Some(app_name) = &self.application {
|
||||
for file in &self.filenames {
|
||||
match open::with(&file.item, app_name) {
|
||||
Ok(_) => continue,
|
||||
Err(_) => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Failed to open file with specified application",
|
||||
"can't open with specified application",
|
||||
file.tag.span,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for file in &self.filenames {
|
||||
match open::that(&file.item) {
|
||||
Ok(_) => continue,
|
||||
Err(_) => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Failed to open file with default application",
|
||||
"can't open with default application",
|
||||
file.tag.span,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
|
||||
pub fn exec(&mut self) -> Result<(), ShellError> {
|
||||
let mut args = vec![];
|
||||
args.append(
|
||||
&mut self
|
||||
.filenames
|
||||
.iter()
|
||||
.map(|x| x.item.clone())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
if let Some(app_name) = &self.application {
|
||||
exec_cmd(app_name, &args, self.tag.clone())
|
||||
} else {
|
||||
for cmd in ["xdg-open", "gnome-open", "kde-open", "wslview"] {
|
||||
if exec_cmd(cmd, &args, self.tag.clone()).is_err() {
|
||||
continue;
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Err(ShellError::labeled_error(
|
||||
"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,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn exec_cmd(cmd: &str, args: &[String], tag: Tag) -> Result<(), ShellError> {
|
||||
if args.is_empty() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"No file(s) or application provided",
|
||||
"no file(s) or application provided",
|
||||
tag,
|
||||
));
|
||||
}
|
||||
let status = match Command::new(cmd)
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.args(args)
|
||||
.status()
|
||||
{
|
||||
Ok(exit_code) => exit_code,
|
||||
Err(_) => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Failed to run native open syscall",
|
||||
"failed to run native open call",
|
||||
tag,
|
||||
))
|
||||
}
|
||||
};
|
||||
if status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Failed to run start. Hint: The file(s)/application may not exist",
|
||||
"failed to run",
|
||||
tag,
|
||||
))
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user