mirror of
https://github.com/nushell/nushell.git
synced 2025-08-17 22:59:52 +02:00
Merge master
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
use crate::commands::{RawCommandArgs, StaticCommand};
|
||||
use crate::context::{SourceMap, SpanSource};
|
||||
use crate::errors::ShellError;
|
||||
use crate::prelude::*;
|
||||
use std::path::Path;
|
||||
|
||||
pub struct Autoview;
|
||||
|
||||
@@ -36,15 +34,15 @@ pub fn autoview(
|
||||
let input = context.input.drain_vec().await;
|
||||
|
||||
if input.len() > 0 {
|
||||
if let Spanned {
|
||||
if let Tagged {
|
||||
item: Value::Binary(_),
|
||||
..
|
||||
} = input[0]
|
||||
} = input[0usize]
|
||||
{
|
||||
let binary = context.expect_command("binaryview");
|
||||
binary.run(raw.with_input(input), &context.commands).await;
|
||||
} else if is_single_text_value(&input) {
|
||||
view_text_value(&input[0], &raw.call_info.source_map);
|
||||
//view_text_value(&input[0], &raw.call_info.source_map);
|
||||
} else if equal_shapes(&input) {
|
||||
let table = context.expect_command("table");
|
||||
let result = table.run(raw.with_input(input), &context.commands).await.unwrap();
|
||||
@@ -63,7 +61,7 @@ pub fn autoview(
|
||||
}))
|
||||
}
|
||||
|
||||
fn equal_shapes(input: &Vec<Spanned<Value>>) -> bool {
|
||||
fn equal_shapes(input: &Vec<Tagged<Value>>) -> bool {
|
||||
let mut items = input.iter();
|
||||
|
||||
let item = match items.next() {
|
||||
@@ -82,11 +80,11 @@ fn equal_shapes(input: &Vec<Spanned<Value>>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn is_single_text_value(input: &Vec<Spanned<Value>>) -> bool {
|
||||
fn is_single_text_value(input: &Vec<Tagged<Value>>) -> bool {
|
||||
if input.len() != 1 {
|
||||
return false;
|
||||
}
|
||||
if let Spanned {
|
||||
if let Tagged {
|
||||
item: Value::Primitive(Primitive::String(_)),
|
||||
..
|
||||
} = input[0]
|
||||
@@ -96,63 +94,3 @@ fn is_single_text_value(input: &Vec<Spanned<Value>>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn view_text_value(value: &Spanned<Value>, source_map: &SourceMap) {
|
||||
match value {
|
||||
Spanned {
|
||||
item: Value::Primitive(Primitive::String(s)),
|
||||
span,
|
||||
} => {
|
||||
let source = span.source.map(|x| source_map.get(&x)).flatten();
|
||||
|
||||
if let Some(source) = source {
|
||||
match source {
|
||||
SpanSource::File(file) => {
|
||||
let path = Path::new(file);
|
||||
match path.extension() {
|
||||
Some(extension) => {
|
||||
use syntect::easy::HighlightLines;
|
||||
use syntect::highlighting::{Style, ThemeSet};
|
||||
use syntect::parsing::SyntaxSet;
|
||||
use syntect::util::{as_24_bit_terminal_escaped, LinesWithEndings};
|
||||
|
||||
// Load these once at the start of your program
|
||||
let ps: SyntaxSet = syntect::dumps::from_binary(include_bytes!(
|
||||
"../../assets/syntaxes.bin"
|
||||
));
|
||||
|
||||
if let Some(syntax) =
|
||||
ps.find_syntax_by_extension(extension.to_str().unwrap())
|
||||
{
|
||||
let ts: ThemeSet = syntect::dumps::from_binary(include_bytes!(
|
||||
"../../assets/themes.bin"
|
||||
));
|
||||
let mut h =
|
||||
HighlightLines::new(syntax, &ts.themes["OneHalfDark"]);
|
||||
|
||||
for line in LinesWithEndings::from(s) {
|
||||
let ranges: Vec<(Style, &str)> = h.highlight(line, &ps);
|
||||
let escaped =
|
||||
as_24_bit_terminal_escaped(&ranges[..], false);
|
||||
print!("{}", escaped);
|
||||
}
|
||||
} else {
|
||||
println!("{}", s);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!("{}", s);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!("{}", s);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("{}", s);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ pub struct Cd;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CdArgs {
|
||||
target: Option<Spanned<PathBuf>>,
|
||||
target: Option<Tagged<PathBuf>>,
|
||||
}
|
||||
|
||||
impl StaticCommand for Cd {
|
||||
@@ -39,7 +39,7 @@ pub fn cd(CdArgs { target }: CdArgs, context: RunnableContext) -> Result<OutputS
|
||||
None => match dirs::home_dir() {
|
||||
Some(o) => o,
|
||||
_ => {
|
||||
return Err(ShellError::maybe_labeled_error(
|
||||
return Err(ShellError::labeled_error(
|
||||
"Can not change to home directory",
|
||||
"can not go to home",
|
||||
context.name,
|
||||
@@ -54,7 +54,7 @@ pub fn cd(CdArgs { target }: CdArgs, context: RunnableContext) -> Result<OutputS
|
||||
return Err(ShellError::labeled_error(
|
||||
"Can not change to directory",
|
||||
"directory not found",
|
||||
v.span.clone(),
|
||||
v.span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -69,13 +69,18 @@ pub fn cd(CdArgs { target }: CdArgs, context: RunnableContext) -> Result<OutputS
|
||||
return Err(ShellError::labeled_error(
|
||||
"Can not change to directory",
|
||||
"directory not found",
|
||||
path.span,
|
||||
path.span(),
|
||||
));
|
||||
} else {
|
||||
return Err(ShellError::string("Can not change to directory"));
|
||||
}
|
||||
}
|
||||
}
|
||||
stream.push_back(ReturnSuccess::change_cwd(path));
|
||||
stream.push_back(ReturnSuccess::change_cwd(
|
||||
path.to_string_lossy().to_string(),
|
||||
));
|
||||
Ok(stream.into())
|
||||
|
||||
// pub fn cd(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
// args.shell_manager.cd(args.call_info, args.input)
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
use crate::commands::Command;
|
||||
use crate::context::SourceMap;
|
||||
use crate::parser::{hir, Span, Spanned, TokenNode};
|
||||
use crate::parser::{hir, TokenNode};
|
||||
use crate::prelude::*;
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use futures::stream::StreamExt;
|
||||
@@ -98,7 +98,7 @@ impl ClassifiedCommand {
|
||||
|
||||
crate struct InternalCommand {
|
||||
crate command: Arc<Command>,
|
||||
crate name_span: Option<Span>,
|
||||
crate name_span: Span,
|
||||
crate source_map: SourceMap,
|
||||
crate args: hir::Call,
|
||||
}
|
||||
@@ -123,7 +123,7 @@ impl InternalCommand {
|
||||
.run_command(
|
||||
self.command,
|
||||
self.name_span.clone(),
|
||||
self.source_map,
|
||||
context.source_map.clone(),
|
||||
self.args,
|
||||
source,
|
||||
objects,
|
||||
@@ -137,12 +137,62 @@ impl InternalCommand {
|
||||
match item? {
|
||||
ReturnSuccess::Action(action) => match action {
|
||||
CommandAction::ChangePath(path) => {
|
||||
context.env.lock().unwrap().path = path;
|
||||
context.shell_manager.set_path(path);
|
||||
}
|
||||
CommandAction::AddSpanSource(uuid, span_source) => {
|
||||
context.add_span_source(uuid, span_source);
|
||||
}
|
||||
CommandAction::Exit => std::process::exit(0),
|
||||
CommandAction::EnterShell(location) => {
|
||||
let path = std::path::Path::new(&location);
|
||||
|
||||
if path.is_dir() {
|
||||
// If it's a directory, add a new filesystem shell
|
||||
context
|
||||
.shell_manager
|
||||
.push(Box::new(FilesystemShell::with_location(location)?));
|
||||
} else {
|
||||
// If it's a file, attempt to open the file as a value and enter it
|
||||
let cwd = context.shell_manager.path();
|
||||
|
||||
let full_path = std::path::PathBuf::from(cwd);
|
||||
|
||||
let (file_extension, contents, contents_tag, _) =
|
||||
crate::commands::open::fetch(
|
||||
&full_path,
|
||||
&location,
|
||||
Span::unknown(),
|
||||
)?;
|
||||
|
||||
match contents {
|
||||
Value::Primitive(Primitive::String(string)) => {
|
||||
let value = crate::commands::open::parse_as_value(
|
||||
file_extension,
|
||||
string,
|
||||
contents_tag,
|
||||
Span::unknown(),
|
||||
)?;
|
||||
|
||||
context.shell_manager.push(Box::new(ValueShell::new(value)));
|
||||
}
|
||||
value => context
|
||||
.shell_manager
|
||||
.push(Box::new(ValueShell::new(value.tagged(Tag::unknown())))),
|
||||
}
|
||||
}
|
||||
}
|
||||
CommandAction::PreviousShell => {
|
||||
context.shell_manager.prev();
|
||||
}
|
||||
CommandAction::NextShell => {
|
||||
context.shell_manager.next();
|
||||
}
|
||||
CommandAction::LeaveShell => {
|
||||
context.shell_manager.pop();
|
||||
if context.shell_manager.is_empty() {
|
||||
std::process::exit(0);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ReturnSuccess::Value(v) => {
|
||||
@@ -158,8 +208,8 @@ impl InternalCommand {
|
||||
crate struct ExternalCommand {
|
||||
crate name: String,
|
||||
#[allow(unused)]
|
||||
crate name_span: Option<Span>,
|
||||
crate args: Vec<Spanned<String>>,
|
||||
crate name_span: Span,
|
||||
crate args: Vec<Tagged<String>>,
|
||||
}
|
||||
|
||||
crate enum StreamNext {
|
||||
@@ -176,7 +226,7 @@ impl ExternalCommand {
|
||||
stream_next: StreamNext,
|
||||
) -> Result<ClassifiedInputStream, ShellError> {
|
||||
let stdin = input.stdin;
|
||||
let inputs: Vec<Spanned<Value>> = input.objects.into_vec().await;
|
||||
let inputs: Vec<Tagged<Value>> = input.objects.into_vec().await;
|
||||
let name_span = self.name_span.clone();
|
||||
|
||||
trace!(target: "nu::run::external", "-> {}", self.name);
|
||||
@@ -201,7 +251,7 @@ impl ExternalCommand {
|
||||
let mut span = None;
|
||||
for arg in &self.args {
|
||||
if arg.item.contains("$it") {
|
||||
span = Some(arg.span);
|
||||
span = Some(arg.span());
|
||||
}
|
||||
}
|
||||
if let Some(span) = span {
|
||||
@@ -231,7 +281,17 @@ impl ExternalCommand {
|
||||
}
|
||||
} else {
|
||||
for arg in &self.args {
|
||||
process = process.arg(arg.item.clone());
|
||||
let arg_chars: Vec<_> = arg.chars().collect();
|
||||
if arg_chars.len() > 1
|
||||
&& arg_chars[0] == '"'
|
||||
&& arg_chars[arg_chars.len() - 1] == '"'
|
||||
{
|
||||
// quoted string
|
||||
let new_arg: String = arg_chars[1..arg_chars.len() - 1].iter().collect();
|
||||
process = process.arg(new_arg);
|
||||
} else {
|
||||
process = process.arg(arg.item.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,13 +303,13 @@ impl ExternalCommand {
|
||||
let mut first = true;
|
||||
for i in &inputs {
|
||||
if i.as_string().is_err() {
|
||||
let mut span = None;
|
||||
let mut span = name_span;
|
||||
for arg in &self.args {
|
||||
if arg.item.contains("$it") {
|
||||
span = Some(arg.span);
|
||||
span = arg.span();
|
||||
}
|
||||
}
|
||||
return Err(ShellError::maybe_labeled_error(
|
||||
return Err(ShellError::labeled_error(
|
||||
"External $it needs string data",
|
||||
"given object instead of string data",
|
||||
span,
|
||||
@@ -280,7 +340,7 @@ impl ExternalCommand {
|
||||
|
||||
process = Exec::shell(new_arg_string);
|
||||
}
|
||||
process = process.cwd(context.env.lock().unwrap().path());
|
||||
process = process.cwd(context.shell_manager.path());
|
||||
|
||||
let mut process = match stream_next {
|
||||
StreamNext::Last => process,
|
||||
@@ -308,10 +368,11 @@ impl ExternalCommand {
|
||||
let stdout = popen.stdout.take().unwrap();
|
||||
let file = futures::io::AllowStdIo::new(stdout);
|
||||
let stream = Framed::new(file, LinesCodec {});
|
||||
let stream =
|
||||
stream.map(move |line| Value::string(line.unwrap()).spanned(name_span));
|
||||
let stream = stream.map(move |line| {
|
||||
Tagged::from_simple_spanned_item(Value::string(line.unwrap()), name_span)
|
||||
});
|
||||
Ok(ClassifiedInputStream::from_input_stream(
|
||||
stream.boxed() as BoxStream<'static, Spanned<Value>>
|
||||
stream.boxed() as BoxStream<'static, Tagged<Value>>
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@@ -33,7 +33,7 @@ pub fn clip(
|
||||
RunnableContext { input, name, .. }: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let stream = async_stream_block! {
|
||||
let values: Vec<Spanned<Value>> = input.values.collect().await;
|
||||
let values: Vec<Tagged<Value>> = input.values.collect().await;
|
||||
|
||||
inner_clip(values, name).await;
|
||||
};
|
||||
@@ -43,7 +43,7 @@ pub fn clip(
|
||||
Ok(OutputStream::from(stream))
|
||||
}
|
||||
|
||||
async fn inner_clip(input: Vec<Spanned<Value>>, name: Option<Span>) -> OutputStream {
|
||||
async fn inner_clip(input: Vec<Tagged<Value>>, name: Span) -> OutputStream {
|
||||
let mut clip_context: ClipboardContext = ClipboardProvider::new().unwrap();
|
||||
let mut new_copy_data = String::new();
|
||||
|
||||
|
@@ -3,7 +3,7 @@ use crate::errors::ShellError;
|
||||
use crate::evaluate::Scope;
|
||||
use crate::object::Value;
|
||||
use crate::parser::hir;
|
||||
use crate::parser::{registry, ConfigDeserializer, Span, Spanned};
|
||||
use crate::parser::{registry, ConfigDeserializer};
|
||||
use crate::prelude::*;
|
||||
use derive_new::new;
|
||||
use getset::Getters;
|
||||
@@ -18,7 +18,7 @@ pub struct UnevaluatedCallInfo {
|
||||
pub args: hir::Call,
|
||||
pub source: Text,
|
||||
pub source_map: SourceMap,
|
||||
pub name_span: Option<Span>,
|
||||
pub name_span: Span,
|
||||
}
|
||||
|
||||
impl ToDebug for UnevaluatedCallInfo {
|
||||
@@ -43,19 +43,22 @@ impl UnevaluatedCallInfo {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct CallInfo {
|
||||
pub args: registry::EvaluatedArgs,
|
||||
pub source_map: SourceMap,
|
||||
pub name_span: Option<Span>,
|
||||
pub name_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Getters)]
|
||||
#[get = "crate"]
|
||||
pub struct CommandArgs {
|
||||
pub host: Arc<Mutex<dyn Host>>,
|
||||
pub env: Arc<Mutex<Environment>>,
|
||||
pub shell_manager: ShellManager,
|
||||
pub call_info: UnevaluatedCallInfo,
|
||||
// pub host: Arc<Mutex<dyn Host + Send>>,
|
||||
// pub shell_manager: ShellManager,
|
||||
// pub call_info: CallInfo,
|
||||
pub input: InputStream,
|
||||
}
|
||||
|
||||
@@ -63,15 +66,15 @@ pub struct CommandArgs {
|
||||
#[get = "crate"]
|
||||
pub struct RawCommandArgs {
|
||||
pub host: Arc<Mutex<dyn Host>>,
|
||||
pub env: Arc<Mutex<Environment>>,
|
||||
pub shell_manager: ShellManager,
|
||||
pub call_info: UnevaluatedCallInfo,
|
||||
}
|
||||
|
||||
impl RawCommandArgs {
|
||||
pub fn with_input(self, input: Vec<Spanned<Value>>) -> CommandArgs {
|
||||
pub fn with_input(self, input: Vec<Tagged<Value>>) -> CommandArgs {
|
||||
CommandArgs {
|
||||
host: self.host,
|
||||
env: self.env,
|
||||
shell_manager: self.shell_manager,
|
||||
call_info: self.call_info,
|
||||
input: input.into(),
|
||||
}
|
||||
@@ -90,14 +93,19 @@ impl CommandArgs {
|
||||
registry: ®istry::CommandRegistry,
|
||||
) -> Result<EvaluatedStaticCommandArgs, ShellError> {
|
||||
let host = self.host.clone();
|
||||
let env = self.env.clone();
|
||||
let shell_manager = self.shell_manager.clone();
|
||||
let input = self.input;
|
||||
let call_info = self.call_info.evaluate(registry, &Scope::empty())?;
|
||||
|
||||
Ok(EvaluatedStaticCommandArgs::new(host, env, call_info, input))
|
||||
Ok(EvaluatedStaticCommandArgs::new(
|
||||
host,
|
||||
shell_manager,
|
||||
call_info,
|
||||
input,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn name_span(&self) -> Option<Span> {
|
||||
pub fn name_span(&self) -> Span {
|
||||
self.call_info.name_span
|
||||
}
|
||||
|
||||
@@ -106,7 +114,7 @@ impl CommandArgs {
|
||||
registry: &CommandRegistry,
|
||||
callback: fn(T, RunnableContext) -> Result<OutputStream, ShellError>,
|
||||
) -> Result<RunnableArgs<T>, ShellError> {
|
||||
let env = self.env.clone();
|
||||
let shell_manager = self.shell_manager.clone();
|
||||
let host = self.host.clone();
|
||||
let args = self.evaluate_once(registry)?;
|
||||
let (input, args) = args.split();
|
||||
@@ -118,7 +126,7 @@ impl CommandArgs {
|
||||
context: RunnableContext {
|
||||
input: input,
|
||||
commands: registry.clone(),
|
||||
env,
|
||||
shell_manager,
|
||||
name: name_span,
|
||||
host,
|
||||
},
|
||||
@@ -133,11 +141,11 @@ impl CommandArgs {
|
||||
) -> Result<RunnableRawArgs<T>, ShellError> {
|
||||
let raw_args = RawCommandArgs {
|
||||
host: self.host.clone(),
|
||||
env: self.env.clone(),
|
||||
shell_manager: self.shell_manager.clone(),
|
||||
call_info: self.call_info.clone(),
|
||||
};
|
||||
|
||||
let env = self.env.clone();
|
||||
let shell_manager = self.shell_manager.clone();
|
||||
let host = self.host.clone();
|
||||
let args = self.evaluate_once(registry)?;
|
||||
let (input, args) = args.split();
|
||||
@@ -149,7 +157,7 @@ impl CommandArgs {
|
||||
context: RunnableContext {
|
||||
input: input,
|
||||
commands: registry.clone(),
|
||||
env,
|
||||
shell_manager,
|
||||
name: name_span,
|
||||
host,
|
||||
},
|
||||
@@ -161,18 +169,15 @@ impl CommandArgs {
|
||||
|
||||
pub struct RunnableContext {
|
||||
pub input: InputStream,
|
||||
pub env: Arc<Mutex<Environment>>,
|
||||
pub shell_manager: ShellManager,
|
||||
pub host: Arc<Mutex<dyn Host>>,
|
||||
pub commands: CommandRegistry,
|
||||
pub name: Option<Span>,
|
||||
pub name: Span,
|
||||
}
|
||||
|
||||
impl RunnableContext {
|
||||
pub fn cwd(&self) -> PathBuf {
|
||||
let env = self.env.clone();
|
||||
let env = env.lock().unwrap();
|
||||
|
||||
env.path.clone()
|
||||
PathBuf::from(self.shell_manager.path())
|
||||
}
|
||||
|
||||
pub fn expect_command(&self, name: &str) -> Arc<Command> {
|
||||
@@ -222,21 +227,21 @@ impl Deref for EvaluatedStaticCommandArgs {
|
||||
impl EvaluatedStaticCommandArgs {
|
||||
pub fn new(
|
||||
host: Arc<Mutex<dyn Host>>,
|
||||
env: Arc<Mutex<Environment>>,
|
||||
shell_manager: ShellManager,
|
||||
call_info: CallInfo,
|
||||
input: impl Into<InputStream>,
|
||||
) -> EvaluatedStaticCommandArgs {
|
||||
EvaluatedStaticCommandArgs {
|
||||
args: EvaluatedCommandArgs {
|
||||
host,
|
||||
env,
|
||||
shell_manager,
|
||||
call_info,
|
||||
},
|
||||
input: input.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name_span(&self) -> Option<Span> {
|
||||
pub fn name_span(&self) -> Span {
|
||||
self.args.call_info.name_span
|
||||
}
|
||||
|
||||
@@ -258,7 +263,7 @@ impl EvaluatedStaticCommandArgs {
|
||||
pub struct EvaluatedFilterCommandArgs {
|
||||
args: EvaluatedCommandArgs,
|
||||
#[allow(unused)]
|
||||
input: Spanned<Value>,
|
||||
input: Tagged<Value>,
|
||||
}
|
||||
|
||||
impl Deref for EvaluatedFilterCommandArgs {
|
||||
@@ -271,14 +276,14 @@ impl Deref for EvaluatedFilterCommandArgs {
|
||||
impl EvaluatedFilterCommandArgs {
|
||||
pub fn new(
|
||||
host: Arc<Mutex<dyn Host>>,
|
||||
env: Arc<Mutex<Environment>>,
|
||||
shell_manager: ShellManager,
|
||||
call_info: CallInfo,
|
||||
input: Spanned<Value>,
|
||||
input: Tagged<Value>,
|
||||
) -> EvaluatedFilterCommandArgs {
|
||||
EvaluatedFilterCommandArgs {
|
||||
args: EvaluatedCommandArgs {
|
||||
host,
|
||||
env,
|
||||
shell_manager,
|
||||
call_info,
|
||||
},
|
||||
input,
|
||||
@@ -290,7 +295,7 @@ impl EvaluatedFilterCommandArgs {
|
||||
#[get = "crate"]
|
||||
pub struct EvaluatedCommandArgs {
|
||||
pub host: Arc<Mutex<dyn Host>>,
|
||||
pub env: Arc<Mutex<Environment>>,
|
||||
pub shell_manager: ShellManager,
|
||||
pub call_info: CallInfo,
|
||||
}
|
||||
|
||||
@@ -299,11 +304,11 @@ impl EvaluatedCommandArgs {
|
||||
&self.call_info.args
|
||||
}
|
||||
|
||||
pub fn nth(&self, pos: usize) -> Option<&Spanned<Value>> {
|
||||
pub fn nth(&self, pos: usize) -> Option<&Tagged<Value>> {
|
||||
self.call_info.args.nth(pos)
|
||||
}
|
||||
|
||||
pub fn expect_nth(&self, pos: usize) -> Result<&Spanned<Value>, ShellError> {
|
||||
pub fn expect_nth(&self, pos: usize) -> Result<&Tagged<Value>, ShellError> {
|
||||
self.call_info.args.expect_nth(pos)
|
||||
}
|
||||
|
||||
@@ -311,11 +316,11 @@ impl EvaluatedCommandArgs {
|
||||
self.call_info.args.len()
|
||||
}
|
||||
|
||||
pub fn get(&self, name: &str) -> Option<&Spanned<Value>> {
|
||||
pub fn get(&self, name: &str) -> Option<&Tagged<Value>> {
|
||||
self.call_info.args.get(name)
|
||||
}
|
||||
|
||||
pub fn slice_from(&self, from: usize) -> Vec<Spanned<Value>> {
|
||||
pub fn slice_from(&self, from: usize) -> Vec<Tagged<Value>> {
|
||||
let positional = &self.call_info.args.positional;
|
||||
|
||||
match positional {
|
||||
@@ -332,31 +337,35 @@ impl EvaluatedCommandArgs {
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum CommandAction {
|
||||
ChangePath(PathBuf),
|
||||
ChangePath(String),
|
||||
AddSpanSource(Uuid, SpanSource),
|
||||
Exit,
|
||||
EnterShell(String),
|
||||
PreviousShell,
|
||||
NextShell,
|
||||
LeaveShell,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum ReturnSuccess {
|
||||
Value(Spanned<Value>),
|
||||
Value(Tagged<Value>),
|
||||
Action(CommandAction),
|
||||
}
|
||||
|
||||
pub type ReturnValue = Result<ReturnSuccess, ShellError>;
|
||||
|
||||
impl From<Spanned<Value>> for ReturnValue {
|
||||
fn from(input: Spanned<Value>) -> ReturnValue {
|
||||
impl From<Tagged<Value>> for ReturnValue {
|
||||
fn from(input: Tagged<Value>) -> ReturnValue {
|
||||
Ok(ReturnSuccess::Value(input))
|
||||
}
|
||||
}
|
||||
|
||||
impl ReturnSuccess {
|
||||
pub fn change_cwd(path: PathBuf) -> ReturnValue {
|
||||
pub fn change_cwd(path: String) -> ReturnValue {
|
||||
Ok(ReturnSuccess::Action(CommandAction::ChangePath(path)))
|
||||
}
|
||||
|
||||
pub fn value(input: impl Into<Spanned<Value>>) -> ReturnValue {
|
||||
pub fn value(input: impl Into<Tagged<Value>>) -> ReturnValue {
|
||||
Ok(ReturnSuccess::Value(input.into()))
|
||||
}
|
||||
|
||||
@@ -365,7 +374,9 @@ impl ReturnSuccess {
|
||||
}
|
||||
|
||||
pub fn spanned_value(input: Value, span: Span) -> ReturnValue {
|
||||
Ok(ReturnSuccess::Value(Spanned::from_item(input, span)))
|
||||
Ok(ReturnSuccess::Value(Tagged::from_simple_spanned_item(
|
||||
input, span,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,13 +446,13 @@ impl StaticCommand for FnFilterCommand {
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let CommandArgs {
|
||||
host,
|
||||
env,
|
||||
shell_manager,
|
||||
call_info,
|
||||
input,
|
||||
} = args;
|
||||
|
||||
let host: Arc<Mutex<dyn Host>> = host.clone();
|
||||
let env: Arc<Mutex<Environment>> = env.clone();
|
||||
let shell_manager = shell_manager.clone();
|
||||
let registry: registry::CommandRegistry = registry.clone();
|
||||
let func = self.func;
|
||||
|
||||
@@ -455,7 +466,8 @@ impl StaticCommand for FnFilterCommand {
|
||||
Ok(args) => args,
|
||||
};
|
||||
|
||||
let args = EvaluatedFilterCommandArgs::new(host.clone(), env.clone(), call_info, it);
|
||||
let args =
|
||||
EvaluatedFilterCommandArgs::new(host.clone(), shell_manager.clone(), call_info, it);
|
||||
|
||||
match func(args) {
|
||||
Err(err) => return OutputStream::from(vec![Err(err)]).values,
|
||||
|
@@ -11,11 +11,11 @@ pub struct Config;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ConfigArgs {
|
||||
set: Option<(Spanned<String>, Spanned<Value>)>,
|
||||
get: Option<Spanned<String>>,
|
||||
clear: Spanned<bool>,
|
||||
remove: Option<Spanned<String>>,
|
||||
path: Spanned<bool>,
|
||||
set: Option<(Tagged<String>, Tagged<Value>)>,
|
||||
get: Option<Tagged<String>>,
|
||||
clear: Tagged<bool>,
|
||||
remove: Option<Tagged<String>>,
|
||||
path: Tagged<bool>,
|
||||
}
|
||||
|
||||
impl StaticCommand for Config {
|
||||
@@ -69,29 +69,41 @@ pub fn config(
|
||||
|
||||
config::write_config(&result)?;
|
||||
|
||||
return Ok(stream![Spanned::from_item(
|
||||
return Ok(stream![Tagged::from_simple_spanned_item(
|
||||
Value::Object(result.into()),
|
||||
value.span()
|
||||
)]
|
||||
.from_input_stream());
|
||||
}
|
||||
|
||||
if let Spanned { item: true, span } = clear {
|
||||
if let Tagged {
|
||||
item: true,
|
||||
tag: Tag { span, .. },
|
||||
} = clear
|
||||
{
|
||||
result.clear();
|
||||
|
||||
config::write_config(&result)?;
|
||||
|
||||
return Ok(
|
||||
stream![Spanned::from_item(Value::Object(result.into()), span)].from_input_stream(),
|
||||
);
|
||||
return Ok(stream![Tagged::from_simple_spanned_item(
|
||||
Value::Object(result.into()),
|
||||
span
|
||||
)]
|
||||
.from_input_stream());
|
||||
}
|
||||
|
||||
if let Spanned { item: true, span } = path {
|
||||
if let Tagged {
|
||||
item: true,
|
||||
tag: Tag { span, .. },
|
||||
} = path
|
||||
{
|
||||
let path = config::config_path()?;
|
||||
|
||||
return Ok(
|
||||
stream![Value::Primitive(Primitive::Path(path)).spanned(span)].from_input_stream(),
|
||||
);
|
||||
return Ok(stream![Tagged::from_simple_spanned_item(
|
||||
Value::Primitive(Primitive::Path(path)),
|
||||
span
|
||||
)]
|
||||
.from_input_stream());
|
||||
}
|
||||
|
||||
if let Some(v) = remove {
|
||||
@@ -106,9 +118,9 @@ pub fn config(
|
||||
)));
|
||||
}
|
||||
|
||||
let obj = VecDeque::from_iter(vec![Value::Object(result.into()).spanned(v.span())]);
|
||||
let obj = VecDeque::from_iter(vec![Value::Object(result.into()).simple_spanned(v.span())]);
|
||||
return Ok(obj.from_input_stream());
|
||||
}
|
||||
|
||||
return Ok(vec![Value::Object(result.into()).spanned(name)].into());
|
||||
return Ok(vec![Value::Object(result.into()).simple_spanned(name)].into());
|
||||
}
|
||||
|
401
src/commands/cp.rs
Normal file
401
src/commands/cp.rs
Normal file
@@ -0,0 +1,401 @@
|
||||
use crate::errors::ShellError;
|
||||
use crate::parser::hir::SyntaxType;
|
||||
use crate::parser::registry::{CommandRegistry, Signature};
|
||||
use crate::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub struct Copycp;
|
||||
|
||||
impl StaticCommand for Copycp {
|
||||
fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
cp(args, registry)
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"cp"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("cp")
|
||||
.named("file", SyntaxType::Any)
|
||||
.switch("recursive")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub struct Res {
|
||||
pub loc: PathBuf,
|
||||
pub at: usize,
|
||||
}
|
||||
|
||||
impl Res {}
|
||||
|
||||
pub struct FileStructure {
|
||||
root: PathBuf,
|
||||
resources: Vec<Res>,
|
||||
}
|
||||
|
||||
impl FileStructure {
|
||||
pub fn new() -> FileStructure {
|
||||
FileStructure {
|
||||
root: PathBuf::new(),
|
||||
resources: Vec::<Res>::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_root(&mut self, path: &Path) {
|
||||
self.root = path.to_path_buf();
|
||||
}
|
||||
|
||||
pub fn paths_applying_with<F>(&mut self, to: F) -> Vec<(PathBuf, PathBuf)>
|
||||
where
|
||||
F: Fn((PathBuf, usize)) -> (PathBuf, PathBuf),
|
||||
{
|
||||
self.resources
|
||||
.iter()
|
||||
.map(|f| (PathBuf::from(&f.loc), f.at))
|
||||
.map(|f| to(f))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn walk_decorate(&mut self, start_path: &Path) {
|
||||
self.set_root(&dunce::canonicalize(start_path).unwrap());
|
||||
self.resources = Vec::<Res>::new();
|
||||
self.build(start_path, 0);
|
||||
self.resources.sort();
|
||||
}
|
||||
|
||||
fn build(&mut self, src: &'a Path, lvl: usize) {
|
||||
let source = dunce::canonicalize(src).unwrap();
|
||||
|
||||
if source.is_dir() {
|
||||
for entry in std::fs::read_dir(&source).unwrap() {
|
||||
let entry = entry.unwrap();
|
||||
let path = entry.path();
|
||||
|
||||
if path.is_dir() {
|
||||
self.build(&path, lvl + 1);
|
||||
}
|
||||
|
||||
self.resources.push(Res {
|
||||
loc: path.to_path_buf(),
|
||||
at: lvl,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
self.resources.push(Res {
|
||||
loc: source,
|
||||
at: lvl,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cp(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let mut source = PathBuf::from(args.shell_manager.path());
|
||||
let mut destination = PathBuf::from(args.shell_manager.path());
|
||||
let name_span = args.call_info.name_span;
|
||||
let args = args.evaluate_once(registry)?;
|
||||
|
||||
match args
|
||||
.nth(0)
|
||||
.ok_or_else(|| ShellError::string(&format!("No file or directory specified")))?
|
||||
.as_string()?
|
||||
.as_str()
|
||||
{
|
||||
file => {
|
||||
source.push(file);
|
||||
}
|
||||
}
|
||||
|
||||
match args
|
||||
.nth(1)
|
||||
.ok_or_else(|| ShellError::string(&format!("No file or directory specified")))?
|
||||
.as_string()?
|
||||
.as_str()
|
||||
{
|
||||
file => {
|
||||
destination.push(file);
|
||||
}
|
||||
}
|
||||
|
||||
let sources = glob::glob(&source.to_string_lossy());
|
||||
|
||||
if sources.is_err() {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Invalid pattern.",
|
||||
"Invalid pattern.",
|
||||
args.nth(0).unwrap().span(),
|
||||
));
|
||||
}
|
||||
|
||||
let sources: Vec<_> = sources.unwrap().collect();
|
||||
|
||||
if sources.len() == 1 {
|
||||
if let Ok(entry) = &sources[0] {
|
||||
if entry.is_dir() && !args.has("recursive") {
|
||||
return Err(ShellError::labeled_error(
|
||||
"is a directory (not copied). Try using \"--recursive\".",
|
||||
"is a directory (not copied). Try using \"--recursive\".",
|
||||
args.nth(0).unwrap().span(),
|
||||
));
|
||||
}
|
||||
|
||||
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()).unwrap();
|
||||
new_dst.push(entry.file_name().unwrap());
|
||||
(source_file, new_dst)
|
||||
} else {
|
||||
(source_file, destination.clone())
|
||||
}
|
||||
};
|
||||
|
||||
for (ref src, ref dst) in sources.paths_applying_with(strategy) {
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
));
|
||||
}
|
||||
Ok(o) => o,
|
||||
};
|
||||
|
||||
let strategy = |(source_file, depth_level)| {
|
||||
let mut new_dst = destination.clone();
|
||||
let path = dunce::canonicalize(&source_file).unwrap();
|
||||
|
||||
let mut comps: Vec<_> = path
|
||||
.components()
|
||||
.map(|fragment| fragment.as_os_str())
|
||||
.rev()
|
||||
.take(1 + depth_level)
|
||||
.collect();
|
||||
|
||||
comps.reverse();
|
||||
|
||||
for fragment in comps.iter() {
|
||||
new_dst.push(fragment);
|
||||
}
|
||||
|
||||
(PathBuf::from(&source_file), PathBuf::from(new_dst))
|
||||
};
|
||||
|
||||
for (ref src, ref dst) in sources.paths_applying_with(strategy) {
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
destination.push(entry.file_name().unwrap());
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
let strategy = |(source_file, depth_level)| {
|
||||
let mut new_dst = dunce::canonicalize(&destination).unwrap();
|
||||
let path = dunce::canonicalize(&source_file).unwrap();
|
||||
|
||||
let mut comps: Vec<_> = path
|
||||
.components()
|
||||
.map(|fragment| fragment.as_os_str())
|
||||
.rev()
|
||||
.take(1 + depth_level)
|
||||
.collect();
|
||||
|
||||
comps.reverse();
|
||||
|
||||
for fragment in comps.iter() {
|
||||
new_dst.push(fragment);
|
||||
}
|
||||
|
||||
(PathBuf::from(&source_file), PathBuf::from(new_dst))
|
||||
};
|
||||
|
||||
for (ref src, ref dst) in sources.paths_applying_with(strategy) {
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if destination.exists() {
|
||||
if !sources.iter().all(|x| (x.as_ref().unwrap()).is_file()) && !args.has("recursive") {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Copy aborted (directories found). Try using \"--recursive\".",
|
||||
"Copy aborted (directories found). Try using \"--recursive\".",
|
||||
args.nth(0).unwrap().span(),
|
||||
));
|
||||
}
|
||||
|
||||
for entry in sources {
|
||||
if let Ok(entry) = entry {
|
||||
let mut to = PathBuf::from(&destination);
|
||||
to.push(&entry.file_name().unwrap());
|
||||
|
||||
match std::fs::copy(&entry, &to) {
|
||||
Err(e) => {
|
||||
return Err(ShellError::labeled_error(
|
||||
e.to_string(),
|
||||
e.to_string(),
|
||||
name_span,
|
||||
));
|
||||
}
|
||||
Ok(o) => o,
|
||||
};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(ShellError::labeled_error(
|
||||
format!(
|
||||
"Copy aborted. (Does {:?} exist?)",
|
||||
&destination.file_name().unwrap()
|
||||
),
|
||||
format!(
|
||||
"Copy aborted. (Does {:?} exist?)",
|
||||
&destination.file_name().unwrap()
|
||||
),
|
||||
args.nth(1).unwrap().span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(OutputStream::empty())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::{FileStructure, Res};
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn fixtures() -> PathBuf {
|
||||
let mut sdx = PathBuf::new();
|
||||
sdx.push("tests");
|
||||
sdx.push("fixtures");
|
||||
sdx.push("formats");
|
||||
dunce::canonicalize(sdx).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prepares_and_decorates_source_files_for_copying() {
|
||||
let mut res = FileStructure::new();
|
||||
res.walk_decorate(fixtures().as_path());
|
||||
|
||||
assert_eq!(
|
||||
res.resources,
|
||||
vec![
|
||||
Res {
|
||||
loc: fixtures().join("appveyor.yml"),
|
||||
at: 0
|
||||
},
|
||||
Res {
|
||||
loc: fixtures().join("caco3_plastics.csv"),
|
||||
at: 0
|
||||
},
|
||||
Res {
|
||||
loc: fixtures().join("cargo_sample.toml"),
|
||||
at: 0
|
||||
},
|
||||
Res {
|
||||
loc: fixtures().join("jonathan.xml"),
|
||||
at: 0
|
||||
},
|
||||
Res {
|
||||
loc: fixtures().join("sample.ini"),
|
||||
at: 0
|
||||
},
|
||||
Res {
|
||||
loc: fixtures().join("sgml_description.json"),
|
||||
at: 0
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
88
src/commands/date.rs
Normal file
88
src/commands/date.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
use crate::errors::ShellError;
|
||||
use crate::object::{Dictionary, Value};
|
||||
use crate::prelude::*;
|
||||
use chrono::{DateTime, Local, Utc};
|
||||
|
||||
use crate::commands::StaticCommand;
|
||||
use crate::parser::registry::Signature;
|
||||
use chrono::{Datelike, TimeZone, Timelike};
|
||||
use core::fmt::Display;
|
||||
use indexmap::IndexMap;
|
||||
|
||||
pub struct Date;
|
||||
|
||||
impl StaticCommand for Date {
|
||||
fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
date(args, registry)
|
||||
}
|
||||
fn name(&self) -> &str {
|
||||
"date"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("mkdir").switch("utc").switch("local")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn date_to_value<T: TimeZone>(dt: DateTime<T>, span: Span) -> Tagged<Value>
|
||||
where
|
||||
T::Offset: Display,
|
||||
{
|
||||
let mut indexmap = IndexMap::new();
|
||||
|
||||
indexmap.insert(
|
||||
"year".to_string(),
|
||||
Tagged::from_simple_spanned_item(Value::int(dt.year()), span),
|
||||
);
|
||||
indexmap.insert(
|
||||
"month".to_string(),
|
||||
Tagged::from_simple_spanned_item(Value::int(dt.month()), span),
|
||||
);
|
||||
indexmap.insert(
|
||||
"day".to_string(),
|
||||
Tagged::from_simple_spanned_item(Value::int(dt.day()), span),
|
||||
);
|
||||
indexmap.insert(
|
||||
"hour".to_string(),
|
||||
Tagged::from_simple_spanned_item(Value::int(dt.hour()), span),
|
||||
);
|
||||
indexmap.insert(
|
||||
"minute".to_string(),
|
||||
Tagged::from_simple_spanned_item(Value::int(dt.minute()), span),
|
||||
);
|
||||
indexmap.insert(
|
||||
"second".to_string(),
|
||||
Tagged::from_simple_spanned_item(Value::int(dt.second()), span),
|
||||
);
|
||||
|
||||
let tz = dt.offset();
|
||||
indexmap.insert(
|
||||
"timezone".to_string(),
|
||||
Tagged::from_simple_spanned_item(Value::string(format!("{}", tz)), span),
|
||||
);
|
||||
|
||||
Tagged::from_simple_spanned_item(Value::Object(Dictionary::from(indexmap)), span)
|
||||
}
|
||||
|
||||
pub fn date(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once(registry)?;
|
||||
|
||||
let mut date_out = VecDeque::new();
|
||||
let span = args.call_info.name_span;
|
||||
|
||||
let value = if args.has("utc") {
|
||||
let utc: DateTime<Utc> = Utc::now();
|
||||
date_to_value(utc, span)
|
||||
} else {
|
||||
let local: DateTime<Local> = Local::now();
|
||||
date_to_value(local, span)
|
||||
};
|
||||
|
||||
date_out.push_back(value);
|
||||
|
||||
Ok(date_out.to_output_stream())
|
||||
}
|
23
src/commands/enter.rs
Normal file
23
src/commands/enter.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use crate::commands::command::CommandAction;
|
||||
use crate::errors::ShellError;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn enter(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once(registry)?;
|
||||
|
||||
//TODO: We could also enter a value in the stream
|
||||
if args.len() == 0 {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Enter requires a path",
|
||||
"needs parameter",
|
||||
args.call_info.name_span,
|
||||
));
|
||||
}
|
||||
|
||||
let location = args.expect_nth(0)?.as_string()?;
|
||||
|
||||
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterShell(
|
||||
location,
|
||||
)))]
|
||||
.into())
|
||||
}
|
@@ -1,7 +1,34 @@
|
||||
use crate::commands::command::CommandAction;
|
||||
use crate::commands::command::{CommandAction, StaticCommand};
|
||||
use crate::errors::ShellError;
|
||||
use crate::parser::registry::{CommandRegistry, Signature};
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn exit(_args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::Exit))].into())
|
||||
pub struct Exit;
|
||||
|
||||
impl StaticCommand for Exit {
|
||||
fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
exit(args, registry)
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"exit"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("exit").switch("now")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exit(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once(registry)?;
|
||||
|
||||
if args.call_info.args.has("now") {
|
||||
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::Exit))].into())
|
||||
} else {
|
||||
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::LeaveShell))].into())
|
||||
}
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ pub fn first(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
|
||||
let args = args.evaluate_once(registry)?;
|
||||
|
||||
if args.len() == 0 {
|
||||
return Err(ShellError::maybe_labeled_error(
|
||||
return Err(ShellError::labeled_error(
|
||||
"First requires an amount",
|
||||
"needs parameter",
|
||||
args.name_span(),
|
||||
@@ -23,7 +23,7 @@ pub fn first(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
|
||||
return Err(ShellError::labeled_error(
|
||||
"Value is not a number",
|
||||
"expected integer",
|
||||
args.expect_nth(0)?.span,
|
||||
args.expect_nth(0)?.span(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
@@ -1,15 +1,15 @@
|
||||
use crate::object::{Primitive, SpannedDictBuilder, Value};
|
||||
use crate::object::{Primitive, TaggedDictBuilder, Value};
|
||||
use crate::prelude::*;
|
||||
use csv::ReaderBuilder;
|
||||
|
||||
pub fn from_csv_string_to_value(
|
||||
s: String,
|
||||
span: impl Into<Span>,
|
||||
) -> Result<Spanned<Value>, Box<dyn std::error::Error>> {
|
||||
tag: impl Into<Tag>,
|
||||
) -> Result<Tagged<Value>, Box<dyn std::error::Error>> {
|
||||
let mut reader = ReaderBuilder::new()
|
||||
.has_headers(false)
|
||||
.from_reader(s.as_bytes());
|
||||
let span = span.into();
|
||||
let tag = tag.into();
|
||||
|
||||
let mut fields: VecDeque<String> = VecDeque::new();
|
||||
let mut iter = reader.records();
|
||||
@@ -27,25 +27,22 @@ pub fn from_csv_string_to_value(
|
||||
if let Some(row_values) = iter.next() {
|
||||
let row_values = row_values?;
|
||||
|
||||
let mut row = SpannedDictBuilder::new(span);
|
||||
let mut row = TaggedDictBuilder::new(tag);
|
||||
|
||||
for (idx, entry) in row_values.iter().enumerate() {
|
||||
row.insert_spanned(
|
||||
row.insert_tagged(
|
||||
fields.get(idx).unwrap(),
|
||||
Value::Primitive(Primitive::String(String::from(entry))).spanned(span),
|
||||
Value::Primitive(Primitive::String(String::from(entry))).tagged(tag),
|
||||
);
|
||||
}
|
||||
|
||||
rows.push(row.into_spanned_value());
|
||||
rows.push(row.into_tagged_value());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Spanned {
|
||||
item: Value::List(rows),
|
||||
span,
|
||||
})
|
||||
Ok(Tagged::from_item(Value::List(rows), tag))
|
||||
}
|
||||
|
||||
pub fn from_csv(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
@@ -55,20 +52,29 @@ pub fn from_csv(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputS
|
||||
|
||||
Ok(out
|
||||
.values
|
||||
.map(move |a| match a.item {
|
||||
Value::Primitive(Primitive::String(s)) => match from_csv_string_to_value(s, span) {
|
||||
Ok(x) => ReturnSuccess::value(x.spanned(a.span)),
|
||||
Err(_) => Err(ShellError::maybe_labeled_error(
|
||||
"Could not parse as CSV",
|
||||
"piped data failed CSV parse",
|
||||
.map(move |a| {
|
||||
let value_tag = a.tag();
|
||||
match a.item {
|
||||
Value::Primitive(Primitive::String(s)) => {
|
||||
match from_csv_string_to_value(s, value_tag) {
|
||||
Ok(x) => ReturnSuccess::value(x),
|
||||
Err(_) => Err(ShellError::labeled_error_with_secondary(
|
||||
"Could not parse as CSV",
|
||||
"input cannot be parsed as CSV",
|
||||
span,
|
||||
"value originates from here",
|
||||
value_tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
span,
|
||||
"value originates from here",
|
||||
a.span(),
|
||||
)),
|
||||
},
|
||||
_ => Err(ShellError::maybe_labeled_error(
|
||||
"Expected string values from pipeline",
|
||||
"expects strings from pipeline",
|
||||
span,
|
||||
)),
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
@@ -1,40 +1,40 @@
|
||||
use crate::object::{Primitive, SpannedDictBuilder, Value};
|
||||
use crate::object::{Primitive, TaggedDictBuilder, Value};
|
||||
use crate::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn convert_ini_second_to_nu_value(
|
||||
v: &HashMap<String, String>,
|
||||
span: impl Into<Span>,
|
||||
) -> Spanned<Value> {
|
||||
let mut second = SpannedDictBuilder::new(span);
|
||||
tag: impl Into<Tag>,
|
||||
) -> Tagged<Value> {
|
||||
let mut second = TaggedDictBuilder::new(tag);
|
||||
|
||||
for (key, value) in v.into_iter() {
|
||||
second.insert(key.clone(), Primitive::String(value.clone()));
|
||||
}
|
||||
|
||||
second.into_spanned_value()
|
||||
second.into_tagged_value()
|
||||
}
|
||||
|
||||
fn convert_ini_top_to_nu_value(
|
||||
v: &HashMap<String, HashMap<String, String>>,
|
||||
span: impl Into<Span>,
|
||||
) -> Spanned<Value> {
|
||||
let span = span.into();
|
||||
let mut top_level = SpannedDictBuilder::new(span);
|
||||
tag: impl Into<Tag>,
|
||||
) -> Tagged<Value> {
|
||||
let tag = tag.into();
|
||||
let mut top_level = TaggedDictBuilder::new(tag);
|
||||
|
||||
for (key, value) in v.iter() {
|
||||
top_level.insert_spanned(key.clone(), convert_ini_second_to_nu_value(value, span));
|
||||
top_level.insert_tagged(key.clone(), convert_ini_second_to_nu_value(value, tag));
|
||||
}
|
||||
|
||||
top_level.into_spanned_value()
|
||||
top_level.into_tagged_value()
|
||||
}
|
||||
|
||||
pub fn from_ini_string_to_value(
|
||||
s: String,
|
||||
span: impl Into<Span>,
|
||||
) -> Result<Spanned<Value>, Box<dyn std::error::Error>> {
|
||||
tag: impl Into<Tag>,
|
||||
) -> Result<Tagged<Value>, Box<dyn std::error::Error>> {
|
||||
let v: HashMap<String, HashMap<String, String>> = serde_ini::from_str(&s)?;
|
||||
Ok(convert_ini_top_to_nu_value(&v, span))
|
||||
Ok(convert_ini_top_to_nu_value(&v, tag))
|
||||
}
|
||||
|
||||
pub fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
@@ -44,20 +44,29 @@ pub fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputS
|
||||
|
||||
Ok(out
|
||||
.values
|
||||
.map(move |a| match a.item {
|
||||
Value::Primitive(Primitive::String(s)) => match from_ini_string_to_value(s, span) {
|
||||
Ok(x) => ReturnSuccess::value(x.spanned(a.span)),
|
||||
Err(e) => Err(ShellError::maybe_labeled_error(
|
||||
"Could not parse as INI",
|
||||
format!("{:#?}", e),
|
||||
.map(move |a| {
|
||||
let value_tag = a.tag();
|
||||
match a.item {
|
||||
Value::Primitive(Primitive::String(s)) => {
|
||||
match from_ini_string_to_value(s, value_tag) {
|
||||
Ok(x) => ReturnSuccess::value(x),
|
||||
Err(_) => Err(ShellError::labeled_error_with_secondary(
|
||||
"Could not parse as INI",
|
||||
"input cannot be parsed as INI",
|
||||
span,
|
||||
"value originates from here",
|
||||
value_tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
span,
|
||||
"value originates from here",
|
||||
a.span(),
|
||||
)),
|
||||
},
|
||||
_ => Err(ShellError::maybe_labeled_error(
|
||||
"Expected string values from pipeline",
|
||||
"expects strings from pipeline",
|
||||
span,
|
||||
)),
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
@@ -1,46 +1,46 @@
|
||||
use crate::object::base::OF64;
|
||||
use crate::object::{Primitive, SpannedDictBuilder, Value};
|
||||
use crate::object::{Primitive, TaggedDictBuilder, Value};
|
||||
use crate::prelude::*;
|
||||
|
||||
fn convert_json_value_to_nu_value(v: &serde_hjson::Value, span: impl Into<Span>) -> Spanned<Value> {
|
||||
let span = span.into();
|
||||
fn convert_json_value_to_nu_value(v: &serde_hjson::Value, tag: impl Into<Tag>) -> Tagged<Value> {
|
||||
let tag = tag.into();
|
||||
|
||||
match v {
|
||||
serde_hjson::Value::Null => {
|
||||
Value::Primitive(Primitive::String(String::from(""))).spanned(span)
|
||||
Value::Primitive(Primitive::String(String::from(""))).tagged(tag)
|
||||
}
|
||||
serde_hjson::Value::Bool(b) => Value::Primitive(Primitive::Boolean(*b)).spanned(span),
|
||||
serde_hjson::Value::Bool(b) => Value::Primitive(Primitive::Boolean(*b)).tagged(tag),
|
||||
serde_hjson::Value::F64(n) => {
|
||||
Value::Primitive(Primitive::Float(OF64::from(*n))).spanned(span)
|
||||
Value::Primitive(Primitive::Float(OF64::from(*n))).tagged(tag)
|
||||
}
|
||||
serde_hjson::Value::U64(n) => Value::Primitive(Primitive::Int(*n as i64)).spanned(span),
|
||||
serde_hjson::Value::I64(n) => Value::Primitive(Primitive::Int(*n as i64)).spanned(span),
|
||||
serde_hjson::Value::U64(n) => Value::Primitive(Primitive::Int(*n as i64)).tagged(tag),
|
||||
serde_hjson::Value::I64(n) => Value::Primitive(Primitive::Int(*n as i64)).tagged(tag),
|
||||
serde_hjson::Value::String(s) => {
|
||||
Value::Primitive(Primitive::String(String::from(s))).spanned(span)
|
||||
Value::Primitive(Primitive::String(String::from(s))).tagged(tag)
|
||||
}
|
||||
serde_hjson::Value::Array(a) => Value::List(
|
||||
a.iter()
|
||||
.map(|x| convert_json_value_to_nu_value(x, span))
|
||||
.map(|x| convert_json_value_to_nu_value(x, tag))
|
||||
.collect(),
|
||||
)
|
||||
.spanned(span),
|
||||
.tagged(tag),
|
||||
serde_hjson::Value::Object(o) => {
|
||||
let mut collected = SpannedDictBuilder::new(span);
|
||||
let mut collected = TaggedDictBuilder::new(tag);
|
||||
for (k, v) in o.iter() {
|
||||
collected.insert_spanned(k.clone(), convert_json_value_to_nu_value(v, span));
|
||||
collected.insert_tagged(k.clone(), convert_json_value_to_nu_value(v, tag));
|
||||
}
|
||||
|
||||
collected.into_spanned_value()
|
||||
collected.into_tagged_value()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_json_string_to_value(
|
||||
s: String,
|
||||
span: impl Into<Span>,
|
||||
) -> serde_hjson::Result<Spanned<Value>> {
|
||||
tag: impl Into<Tag>,
|
||||
) -> serde_hjson::Result<Tagged<Value>> {
|
||||
let v: serde_hjson::Value = serde_hjson::from_str(&s)?;
|
||||
Ok(convert_json_value_to_nu_value(&v, span))
|
||||
Ok(convert_json_value_to_nu_value(&v, tag))
|
||||
}
|
||||
|
||||
pub fn from_json(
|
||||
@@ -53,20 +53,29 @@ pub fn from_json(
|
||||
|
||||
Ok(out
|
||||
.values
|
||||
.map(move |a| match a.item {
|
||||
Value::Primitive(Primitive::String(s)) => match from_json_string_to_value(s, span) {
|
||||
Ok(x) => ReturnSuccess::value(x.spanned(a.span)),
|
||||
Err(_) => Err(ShellError::maybe_labeled_error(
|
||||
"Could not parse as JSON",
|
||||
"piped data failed JSON parse",
|
||||
.map(move |a| {
|
||||
let value_tag = a.tag();
|
||||
match a.item {
|
||||
Value::Primitive(Primitive::String(s)) => {
|
||||
match from_json_string_to_value(s, value_tag) {
|
||||
Ok(x) => ReturnSuccess::value(x),
|
||||
Err(_) => Err(ShellError::labeled_error_with_secondary(
|
||||
"Could not parse as JSON",
|
||||
"input cannot be parsed as JSON",
|
||||
span,
|
||||
"value originates from here",
|
||||
value_tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
span,
|
||||
"value originates from here",
|
||||
a.span(),
|
||||
)),
|
||||
},
|
||||
_ => Err(ShellError::maybe_labeled_error(
|
||||
"Expected string values from pipeline",
|
||||
"expects strings from pipeline",
|
||||
span,
|
||||
)),
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
@@ -1,44 +1,42 @@
|
||||
use crate::object::base::OF64;
|
||||
use crate::object::{Primitive, SpannedDictBuilder, Value};
|
||||
use crate::object::{Primitive, TaggedDictBuilder, Value};
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn convert_toml_value_to_nu_value(v: &toml::Value, span: impl Into<Span>) -> Spanned<Value> {
|
||||
let span = span.into();
|
||||
pub fn convert_toml_value_to_nu_value(v: &toml::Value, tag: impl Into<Tag>) -> Tagged<Value> {
|
||||
let tag = tag.into();
|
||||
|
||||
match v {
|
||||
toml::Value::Boolean(b) => Value::Primitive(Primitive::Boolean(*b)).spanned(span),
|
||||
toml::Value::Integer(n) => Value::Primitive(Primitive::Int(*n)).spanned(span),
|
||||
toml::Value::Float(n) => Value::Primitive(Primitive::Float(OF64::from(*n))).spanned(span),
|
||||
toml::Value::String(s) => {
|
||||
Value::Primitive(Primitive::String(String::from(s))).spanned(span)
|
||||
}
|
||||
toml::Value::Boolean(b) => Value::Primitive(Primitive::Boolean(*b)).tagged(tag),
|
||||
toml::Value::Integer(n) => Value::Primitive(Primitive::Int(*n)).tagged(tag),
|
||||
toml::Value::Float(n) => Value::Primitive(Primitive::Float(OF64::from(*n))).tagged(tag),
|
||||
toml::Value::String(s) => Value::Primitive(Primitive::String(String::from(s))).tagged(tag),
|
||||
toml::Value::Array(a) => Value::List(
|
||||
a.iter()
|
||||
.map(|x| convert_toml_value_to_nu_value(x, span))
|
||||
.map(|x| convert_toml_value_to_nu_value(x, tag))
|
||||
.collect(),
|
||||
)
|
||||
.spanned(span),
|
||||
.tagged(tag),
|
||||
toml::Value::Datetime(dt) => {
|
||||
Value::Primitive(Primitive::String(dt.to_string())).spanned(span)
|
||||
Value::Primitive(Primitive::String(dt.to_string())).tagged(tag)
|
||||
}
|
||||
toml::Value::Table(t) => {
|
||||
let mut collected = SpannedDictBuilder::new(span);
|
||||
let mut collected = TaggedDictBuilder::new(tag);
|
||||
|
||||
for (k, v) in t.iter() {
|
||||
collected.insert_spanned(k.clone(), convert_toml_value_to_nu_value(v, span));
|
||||
collected.insert_tagged(k.clone(), convert_toml_value_to_nu_value(v, tag));
|
||||
}
|
||||
|
||||
collected.into_spanned_value()
|
||||
collected.into_tagged_value()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_toml_string_to_value(
|
||||
s: String,
|
||||
span: impl Into<Span>,
|
||||
) -> Result<Spanned<Value>, Box<dyn std::error::Error>> {
|
||||
tag: impl Into<Tag>,
|
||||
) -> Result<Tagged<Value>, Box<dyn std::error::Error>> {
|
||||
let v: toml::Value = s.parse::<toml::Value>()?;
|
||||
Ok(convert_toml_value_to_nu_value(&v, span))
|
||||
Ok(convert_toml_value_to_nu_value(&v, tag))
|
||||
}
|
||||
|
||||
pub fn from_toml(
|
||||
@@ -51,20 +49,29 @@ pub fn from_toml(
|
||||
|
||||
Ok(out
|
||||
.values
|
||||
.map(move |a| match a.item {
|
||||
Value::Primitive(Primitive::String(s)) => match from_toml_string_to_value(s, span) {
|
||||
Ok(x) => ReturnSuccess::value(x.spanned(a.span)),
|
||||
Err(_) => Err(ShellError::maybe_labeled_error(
|
||||
"Could not parse as TOML",
|
||||
"piped data failed TOML parse",
|
||||
.map(move |a| {
|
||||
let value_tag = a.tag();
|
||||
match a.item {
|
||||
Value::Primitive(Primitive::String(s)) => {
|
||||
match from_toml_string_to_value(s, value_tag) {
|
||||
Ok(x) => ReturnSuccess::value(x),
|
||||
Err(_) => Err(ShellError::labeled_error_with_secondary(
|
||||
"Could not parse as TOML",
|
||||
"input cannot be parsed as TOML",
|
||||
span,
|
||||
"value originates from here",
|
||||
value_tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
x => Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
span,
|
||||
format!("{} originates from here", x.type_name()),
|
||||
value_tag.span,
|
||||
)),
|
||||
},
|
||||
_ => Err(ShellError::maybe_labeled_error(
|
||||
"Expected string values from pipeline",
|
||||
"expects strings from pipeline",
|
||||
span,
|
||||
)),
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
@@ -1,24 +1,21 @@
|
||||
use crate::object::{Primitive, SpannedDictBuilder, Value};
|
||||
use crate::object::{Primitive, TaggedDictBuilder, Value};
|
||||
use crate::prelude::*;
|
||||
|
||||
fn from_node_to_value<'a, 'd>(
|
||||
n: &roxmltree::Node<'a, 'd>,
|
||||
span: impl Into<Span>,
|
||||
) -> Spanned<Value> {
|
||||
let span = span.into();
|
||||
fn from_node_to_value<'a, 'd>(n: &roxmltree::Node<'a, 'd>, tag: impl Into<Tag>) -> Tagged<Value> {
|
||||
let tag = tag.into();
|
||||
|
||||
if n.is_element() {
|
||||
let name = n.tag_name().name().trim().to_string();
|
||||
|
||||
let mut children_values = vec![];
|
||||
for c in n.children() {
|
||||
children_values.push(from_node_to_value(&c, span));
|
||||
children_values.push(from_node_to_value(&c, tag));
|
||||
}
|
||||
|
||||
let children_values: Vec<Spanned<Value>> = children_values
|
||||
let children_values: Vec<Tagged<Value>> = children_values
|
||||
.into_iter()
|
||||
.filter(|x| match x {
|
||||
Spanned {
|
||||
Tagged {
|
||||
item: Value::Primitive(Primitive::String(f)),
|
||||
..
|
||||
} => {
|
||||
@@ -32,31 +29,31 @@ fn from_node_to_value<'a, 'd>(
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut collected = SpannedDictBuilder::new(span);
|
||||
let mut collected = TaggedDictBuilder::new(tag);
|
||||
collected.insert(name.clone(), Value::List(children_values));
|
||||
|
||||
collected.into_spanned_value()
|
||||
collected.into_tagged_value()
|
||||
} else if n.is_comment() {
|
||||
Value::string("<comment>").spanned(span)
|
||||
Value::string("<comment>").tagged(tag)
|
||||
} else if n.is_pi() {
|
||||
Value::string("<processing_instruction>").spanned(span)
|
||||
Value::string("<processing_instruction>").tagged(tag)
|
||||
} else if n.is_text() {
|
||||
Value::string(n.text().unwrap()).spanned(span)
|
||||
Value::string(n.text().unwrap()).tagged(tag)
|
||||
} else {
|
||||
Value::string("<unknown>").spanned(span)
|
||||
Value::string("<unknown>").tagged(tag)
|
||||
}
|
||||
}
|
||||
|
||||
fn from_document_to_value(d: &roxmltree::Document, span: impl Into<Span>) -> Spanned<Value> {
|
||||
from_node_to_value(&d.root_element(), span)
|
||||
fn from_document_to_value(d: &roxmltree::Document, tag: impl Into<Tag>) -> Tagged<Value> {
|
||||
from_node_to_value(&d.root_element(), tag)
|
||||
}
|
||||
|
||||
pub fn from_xml_string_to_value(
|
||||
s: String,
|
||||
span: impl Into<Span>,
|
||||
) -> Result<Spanned<Value>, Box<dyn std::error::Error>> {
|
||||
tag: impl Into<Tag>,
|
||||
) -> Result<Tagged<Value>, Box<dyn std::error::Error>> {
|
||||
let parsed = roxmltree::Document::parse(&s)?;
|
||||
Ok(from_document_to_value(&parsed, span))
|
||||
Ok(from_document_to_value(&parsed, tag))
|
||||
}
|
||||
|
||||
pub fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
@@ -65,20 +62,29 @@ pub fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputS
|
||||
let out = args.input;
|
||||
Ok(out
|
||||
.values
|
||||
.map(move |a| match a.item {
|
||||
Value::Primitive(Primitive::String(s)) => match from_xml_string_to_value(s, span) {
|
||||
Ok(x) => ReturnSuccess::value(x.spanned(a.span)),
|
||||
Err(_) => Err(ShellError::maybe_labeled_error(
|
||||
"Could not parse as XML",
|
||||
"piped data failed XML parse",
|
||||
.map(move |a| {
|
||||
let value_tag = a.tag();
|
||||
match a.item {
|
||||
Value::Primitive(Primitive::String(s)) => {
|
||||
match from_xml_string_to_value(s, value_tag) {
|
||||
Ok(x) => ReturnSuccess::value(x),
|
||||
Err(_) => Err(ShellError::labeled_error_with_secondary(
|
||||
"Could not parse as XML",
|
||||
"input cannot be parsed as XML",
|
||||
span,
|
||||
"value originates from here",
|
||||
value_tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
span,
|
||||
"value originates from here",
|
||||
a.span(),
|
||||
)),
|
||||
},
|
||||
_ => Err(ShellError::maybe_labeled_error(
|
||||
"Expected string values from pipeline",
|
||||
"expects strings from pipeline",
|
||||
span,
|
||||
)),
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
@@ -1,51 +1,50 @@
|
||||
use crate::object::base::OF64;
|
||||
use crate::object::{Primitive, SpannedDictBuilder, Value};
|
||||
use crate::object::{Primitive, TaggedDictBuilder, Value};
|
||||
use crate::prelude::*;
|
||||
|
||||
fn convert_yaml_value_to_nu_value(v: &serde_yaml::Value, span: impl Into<Span>) -> Spanned<Value> {
|
||||
let span = span.into();
|
||||
fn convert_yaml_value_to_nu_value(v: &serde_yaml::Value, tag: impl Into<Tag>) -> Tagged<Value> {
|
||||
let tag = tag.into();
|
||||
|
||||
match v {
|
||||
serde_yaml::Value::Bool(b) => Value::Primitive(Primitive::Boolean(*b)).spanned(span),
|
||||
serde_yaml::Value::Bool(b) => Value::Primitive(Primitive::Boolean(*b)).tagged(tag),
|
||||
serde_yaml::Value::Number(n) if n.is_i64() => {
|
||||
Value::Primitive(Primitive::Int(n.as_i64().unwrap())).spanned(span)
|
||||
Value::Primitive(Primitive::Int(n.as_i64().unwrap())).tagged(tag)
|
||||
}
|
||||
serde_yaml::Value::Number(n) if n.is_f64() => {
|
||||
Value::Primitive(Primitive::Float(OF64::from(n.as_f64().unwrap()))).spanned(span)
|
||||
Value::Primitive(Primitive::Float(OF64::from(n.as_f64().unwrap()))).tagged(tag)
|
||||
}
|
||||
serde_yaml::Value::String(s) => Value::string(s).spanned(span),
|
||||
serde_yaml::Value::String(s) => Value::string(s).tagged(tag),
|
||||
serde_yaml::Value::Sequence(a) => Value::List(
|
||||
a.iter()
|
||||
.map(|x| convert_yaml_value_to_nu_value(x, span))
|
||||
.map(|x| convert_yaml_value_to_nu_value(x, tag))
|
||||
.collect(),
|
||||
)
|
||||
.spanned(span),
|
||||
.tagged(tag),
|
||||
serde_yaml::Value::Mapping(t) => {
|
||||
let mut collected = SpannedDictBuilder::new(span);
|
||||
let mut collected = TaggedDictBuilder::new(tag);
|
||||
|
||||
for (k, v) in t.iter() {
|
||||
match k {
|
||||
serde_yaml::Value::String(k) => {
|
||||
collected
|
||||
.insert_spanned(k.clone(), convert_yaml_value_to_nu_value(v, span));
|
||||
collected.insert_tagged(k.clone(), convert_yaml_value_to_nu_value(v, tag));
|
||||
}
|
||||
_ => unimplemented!("Unknown key type"),
|
||||
}
|
||||
}
|
||||
|
||||
collected.into_spanned_value()
|
||||
collected.into_tagged_value()
|
||||
}
|
||||
serde_yaml::Value::Null => Value::Primitive(Primitive::Nothing).spanned(span),
|
||||
serde_yaml::Value::Null => Value::Primitive(Primitive::Nothing).tagged(tag),
|
||||
x => unimplemented!("Unsupported yaml case: {:?}", x),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_yaml_string_to_value(
|
||||
s: String,
|
||||
span: impl Into<Span>,
|
||||
) -> serde_yaml::Result<Spanned<Value>> {
|
||||
tag: impl Into<Tag>,
|
||||
) -> serde_yaml::Result<Tagged<Value>> {
|
||||
let v: serde_yaml::Value = serde_yaml::from_str(&s)?;
|
||||
Ok(convert_yaml_value_to_nu_value(&v, span))
|
||||
Ok(convert_yaml_value_to_nu_value(&v, tag))
|
||||
}
|
||||
|
||||
pub fn from_yaml(
|
||||
@@ -57,20 +56,29 @@ pub fn from_yaml(
|
||||
|
||||
Ok(out
|
||||
.values
|
||||
.map(move |a| match a.item {
|
||||
Value::Primitive(Primitive::String(s)) => match from_yaml_string_to_value(s, span) {
|
||||
Ok(x) => ReturnSuccess::value(x.spanned(a.span)),
|
||||
Err(_) => Err(ShellError::maybe_labeled_error(
|
||||
"Could not parse as YAML",
|
||||
"piped data failed YAML parse",
|
||||
.map(move |a| {
|
||||
let value_tag = a.tag();
|
||||
match a.item {
|
||||
Value::Primitive(Primitive::String(s)) => {
|
||||
match from_yaml_string_to_value(s, value_tag) {
|
||||
Ok(x) => ReturnSuccess::value(x),
|
||||
Err(_) => Err(ShellError::labeled_error_with_secondary(
|
||||
"Could not parse as YAML",
|
||||
"input cannot be parsed as YAML",
|
||||
span,
|
||||
"value originates from here",
|
||||
value_tag.span,
|
||||
)),
|
||||
}
|
||||
}
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
span,
|
||||
"value originates from here",
|
||||
a.span(),
|
||||
)),
|
||||
},
|
||||
_ => Err(ShellError::maybe_labeled_error(
|
||||
"Expected string values from pipeline",
|
||||
"expects strings from pipeline",
|
||||
span,
|
||||
)),
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ pub struct Get;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GetArgs {
|
||||
rest: Vec<Spanned<String>>,
|
||||
rest: Vec<Tagged<String>>,
|
||||
}
|
||||
|
||||
impl StaticCommand for Get {
|
||||
@@ -27,7 +27,7 @@ impl StaticCommand for Get {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_member(path: &Spanned<String>, obj: &Spanned<Value>) -> Result<Spanned<Value>, ShellError> {
|
||||
fn get_member(path: &Tagged<String>, obj: &Tagged<Value>) -> Result<Tagged<Value>, ShellError> {
|
||||
let mut current = obj;
|
||||
for p in path.split(".") {
|
||||
match current.get_data_by_key(p) {
|
||||
@@ -36,7 +36,7 @@ fn get_member(path: &Spanned<String>, obj: &Spanned<Value>) -> Result<Spanned<Va
|
||||
return Err(ShellError::labeled_error(
|
||||
"Unknown field",
|
||||
"object missing field",
|
||||
path.span,
|
||||
path.span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ pub fn get(
|
||||
let mut result = VecDeque::new();
|
||||
for field in &fields {
|
||||
match get_member(field, &item) {
|
||||
Ok(Spanned {
|
||||
Ok(Tagged {
|
||||
item: Value::List(l),
|
||||
..
|
||||
}) => {
|
||||
|
@@ -23,17 +23,19 @@ pub fn lines(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
|
||||
let mut result = VecDeque::new();
|
||||
for s in split_result {
|
||||
result.push_back(ReturnSuccess::value(
|
||||
Value::Primitive(Primitive::String(s.into())).spanned_unknown(),
|
||||
Value::Primitive(Primitive::String(s.into())).tagged_unknown(),
|
||||
));
|
||||
}
|
||||
result
|
||||
}
|
||||
_ => {
|
||||
let mut result = VecDeque::new();
|
||||
result.push_back(Err(ShellError::maybe_labeled_error(
|
||||
"Expected string values from pipeline",
|
||||
"expects strings from pipeline",
|
||||
result.push_back(Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
span,
|
||||
"value originates from here",
|
||||
v.span(),
|
||||
)));
|
||||
result
|
||||
}
|
||||
|
@@ -1,17 +1,14 @@
|
||||
use crate::errors::ShellError;
|
||||
use crate::object::{dir_entry_dict, Primitive, Value};
|
||||
use crate::parser::Spanned;
|
||||
use crate::object::dir_entry_dict;
|
||||
use crate::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub fn ls(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let env = args.env.clone();
|
||||
let env = env.lock().unwrap();
|
||||
let args = args.evaluate_once(registry)?;
|
||||
let path = env.path.to_path_buf();
|
||||
let path = PathBuf::from(args.shell_manager.path());
|
||||
let mut full_path = PathBuf::from(path);
|
||||
match &args.nth(0) {
|
||||
Some(Spanned {
|
||||
Some(Tagged {
|
||||
item: Value::Primitive(Primitive::String(s)),
|
||||
..
|
||||
}) => full_path.push(Path::new(&s)),
|
||||
@@ -26,10 +23,10 @@ pub fn ls(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream,
|
||||
return Err(ShellError::labeled_error(
|
||||
e.to_string(),
|
||||
e.to_string(),
|
||||
s.span,
|
||||
s.span(),
|
||||
));
|
||||
} else {
|
||||
return Err(ShellError::maybe_labeled_error(
|
||||
return Err(ShellError::labeled_error(
|
||||
e.to_string(),
|
||||
e.to_string(),
|
||||
args.name_span(),
|
||||
@@ -42,8 +39,19 @@ pub fn ls(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream,
|
||||
let mut shell_entries = VecDeque::new();
|
||||
|
||||
for entry in entries {
|
||||
let value = dir_entry_dict(&entry?, args.name_span())?;
|
||||
let entry = entry?;
|
||||
let filepath = entry.path();
|
||||
let filename = filepath.strip_prefix(&full_path).unwrap();
|
||||
let value = dir_entry_dict(
|
||||
filename,
|
||||
&entry.metadata()?,
|
||||
Tag::unknown_origin(args.call_info.name_span),
|
||||
)?;
|
||||
shell_entries.push_back(ReturnSuccess::value(value))
|
||||
}
|
||||
Ok(shell_entries.to_output_stream())
|
||||
|
||||
// pub fn ls(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
// let args = args.evaluate_once(registry)?;
|
||||
// args.shell_manager.ls(args, args.input)
|
||||
}
|
||||
|
45
src/commands/mkdir.rs
Normal file
45
src/commands/mkdir.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use crate::errors::ShellError;
|
||||
use crate::parser::hir::SyntaxType;
|
||||
use crate::parser::registry::{CommandRegistry, Signature};
|
||||
use crate::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub struct Mkdir;
|
||||
|
||||
impl StaticCommand for Mkdir {
|
||||
fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
mkdir(args, registry)
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"mkdir"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("mkdir").named("file", SyntaxType::Any)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mkdir(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once(registry)?;
|
||||
|
||||
let mut full_path = PathBuf::from(args.shell_manager.path());
|
||||
|
||||
match &args.nth(0) {
|
||||
Some(Tagged { item: value, .. }) => full_path.push(Path::new(&value.as_string()?)),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match std::fs::create_dir_all(full_path) {
|
||||
Err(reason) => Err(ShellError::labeled_error(
|
||||
reason.to_string(),
|
||||
reason.to_string(),
|
||||
args.nth(0).unwrap().span(),
|
||||
)),
|
||||
Ok(_) => Ok(OutputStream::empty()),
|
||||
}
|
||||
}
|
7
src/commands/next.rs
Normal file
7
src/commands/next.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
use crate::commands::command::CommandAction;
|
||||
use crate::errors::ShellError;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn next(_args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::NextShell))].into())
|
||||
}
|
@@ -3,7 +3,6 @@ use crate::context::SpanSource;
|
||||
use crate::errors::ShellError;
|
||||
use crate::object::{Primitive, Value};
|
||||
use crate::parser::hir::SyntaxType;
|
||||
use crate::parser::parse::span::Span;
|
||||
use crate::parser::registry::{self, Signature};
|
||||
use crate::prelude::*;
|
||||
use mime::Mime;
|
||||
@@ -15,7 +14,7 @@ pub struct Open;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct OpenArgs {
|
||||
path: Spanned<PathBuf>,
|
||||
path: Tagged<PathBuf>,
|
||||
raw: bool,
|
||||
}
|
||||
|
||||
@@ -41,24 +40,28 @@ impl StaticCommand for Open {
|
||||
|
||||
fn run(
|
||||
OpenArgs { raw, path }: OpenArgs,
|
||||
RunnableContext { env, name, .. }: RunnableContext,
|
||||
RunnableContext {
|
||||
shell_manager,
|
||||
name,
|
||||
..
|
||||
}: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let cwd = env.lock().unwrap().path().to_path_buf();
|
||||
let cwd = PathBuf::from(shell_manager.path());
|
||||
let full_path = PathBuf::from(cwd);
|
||||
|
||||
let path_str = path.to_str().ok_or(ShellError::type_error(
|
||||
"Path",
|
||||
"invalid path".spanned(path.span),
|
||||
"invalid path".tagged(path.tag()),
|
||||
))?;
|
||||
|
||||
let (file_extension, contents, contents_span, span_source) =
|
||||
fetch(&full_path, path_str, path.span)?;
|
||||
let (file_extension, contents, contents_tag, span_source) =
|
||||
fetch(&full_path, path_str, path.span())?;
|
||||
|
||||
let file_extension = if raw { None } else { file_extension };
|
||||
|
||||
let mut stream = VecDeque::new();
|
||||
|
||||
if let Some(uuid) = contents_span.source {
|
||||
if let Some(uuid) = contents_tag.origin {
|
||||
// If we have loaded something, track its source
|
||||
stream.push_back(ReturnSuccess::action(CommandAction::AddSpanSource(
|
||||
uuid,
|
||||
@@ -68,10 +71,10 @@ fn run(
|
||||
|
||||
match contents {
|
||||
Value::Primitive(Primitive::String(string)) => {
|
||||
let value = parse_as_value(file_extension, string, contents_span, name)?;
|
||||
let value = parse_as_value(file_extension, string, contents_tag, name)?;
|
||||
|
||||
match value {
|
||||
Spanned {
|
||||
Tagged {
|
||||
item: Value::List(list),
|
||||
..
|
||||
} => {
|
||||
@@ -83,7 +86,7 @@ fn run(
|
||||
}
|
||||
}
|
||||
|
||||
other => stream.push_back(ReturnSuccess::value(other.spanned(contents_span))),
|
||||
other => stream.push_back(ReturnSuccess::value(other.tagged(contents_tag))),
|
||||
};
|
||||
|
||||
Ok(stream.boxed().to_output_stream())
|
||||
@@ -149,7 +152,7 @@ pub fn fetch(
|
||||
cwd: &PathBuf,
|
||||
location: &str,
|
||||
span: Span,
|
||||
) -> Result<(Option<String>, Value, Span, SpanSource), ShellError> {
|
||||
) -> Result<(Option<String>, Value, Tag, SpanSource), ShellError> {
|
||||
let mut cwd = cwd.clone();
|
||||
if location.starts_with("http:") || location.starts_with("https:") {
|
||||
let response = reqwest::get(location);
|
||||
@@ -161,13 +164,19 @@ pub fn fetch(
|
||||
(mime::APPLICATION, mime::XML) => Ok((
|
||||
Some("xml".to_string()),
|
||||
Value::string(r.text().unwrap()),
|
||||
Span::unknown_with_uuid(Uuid::new_v4()),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
},
|
||||
SpanSource::Url(r.url().to_string()),
|
||||
)),
|
||||
(mime::APPLICATION, mime::JSON) => Ok((
|
||||
Some("json".to_string()),
|
||||
Value::string(r.text().unwrap()),
|
||||
Span::unknown_with_uuid(Uuid::new_v4()),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
},
|
||||
SpanSource::Url(r.url().to_string()),
|
||||
)),
|
||||
(mime::APPLICATION, mime::OCTET_STREAM) => {
|
||||
@@ -182,7 +191,10 @@ pub fn fetch(
|
||||
Ok((
|
||||
None,
|
||||
Value::Binary(buf),
|
||||
Span::unknown_with_uuid(Uuid::new_v4()),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
},
|
||||
SpanSource::Url(r.url().to_string()),
|
||||
))
|
||||
}
|
||||
@@ -198,14 +210,20 @@ pub fn fetch(
|
||||
Ok((
|
||||
Some(image_ty.to_string()),
|
||||
Value::Binary(buf),
|
||||
Span::unknown_with_uuid(Uuid::new_v4()),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
},
|
||||
SpanSource::Url(r.url().to_string()),
|
||||
))
|
||||
}
|
||||
(mime::TEXT, mime::HTML) => Ok((
|
||||
Some("html".to_string()),
|
||||
Value::string(r.text().unwrap()),
|
||||
Span::unknown_with_uuid(Uuid::new_v4()),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
},
|
||||
SpanSource::Url(r.url().to_string()),
|
||||
)),
|
||||
(mime::TEXT, mime::PLAIN) => {
|
||||
@@ -223,14 +241,23 @@ pub fn fetch(
|
||||
Ok((
|
||||
path_extension,
|
||||
Value::string(r.text().unwrap()),
|
||||
Span::unknown_with_uuid(Uuid::new_v4()),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
},
|
||||
SpanSource::Url(r.url().to_string()),
|
||||
))
|
||||
}
|
||||
(ty, sub_ty) => Ok((
|
||||
None,
|
||||
Value::string(format!("Not yet support MIME type: {} {}", ty, sub_ty)),
|
||||
Span::unknown_with_uuid(Uuid::new_v4()),
|
||||
Value::string(format!(
|
||||
"Not yet supported MIME type: {} {}",
|
||||
ty, sub_ty
|
||||
)),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
},
|
||||
SpanSource::Url(r.url().to_string()),
|
||||
)),
|
||||
}
|
||||
@@ -238,7 +265,10 @@ pub fn fetch(
|
||||
None => Ok((
|
||||
None,
|
||||
Value::string(format!("No content type found")),
|
||||
Span::unknown_with_uuid(Uuid::new_v4()),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
},
|
||||
SpanSource::Url(r.url().to_string()),
|
||||
)),
|
||||
},
|
||||
@@ -258,13 +288,19 @@ pub fn fetch(
|
||||
cwd.extension()
|
||||
.map(|name| name.to_string_lossy().to_string()),
|
||||
Value::string(s),
|
||||
Span::unknown_with_uuid(Uuid::new_v4()),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
},
|
||||
SpanSource::File(cwd.to_string_lossy().to_string()),
|
||||
)),
|
||||
Err(_) => Ok((
|
||||
None,
|
||||
Value::Binary(bytes),
|
||||
Span::unknown_with_uuid(Uuid::new_v4()),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
},
|
||||
SpanSource::File(cwd.to_string_lossy().to_string()),
|
||||
)),
|
||||
},
|
||||
@@ -282,69 +318,57 @@ pub fn fetch(
|
||||
pub fn parse_as_value(
|
||||
extension: Option<String>,
|
||||
contents: String,
|
||||
contents_span: Span,
|
||||
name_span: Option<Span>,
|
||||
) -> Result<Spanned<Value>, ShellError> {
|
||||
contents_tag: Tag,
|
||||
name_span: Span,
|
||||
) -> Result<Tagged<Value>, ShellError> {
|
||||
match extension {
|
||||
Some(x) if x == "csv" => {
|
||||
crate::commands::from_csv::from_csv_string_to_value(contents, contents_span)
|
||||
.map(|c| c.spanned(contents_span))
|
||||
.map_err(move |_| {
|
||||
ShellError::maybe_labeled_error(
|
||||
"Could not open as CSV",
|
||||
"could not open as CSV",
|
||||
name_span,
|
||||
)
|
||||
})
|
||||
}
|
||||
Some(x) if x == "csv" => crate::commands::from_csv::from_csv_string_to_value(
|
||||
contents,
|
||||
contents_tag,
|
||||
)
|
||||
.map_err(move |_| {
|
||||
ShellError::labeled_error("Could not open as CSV", "could not open as CSV", name_span)
|
||||
}),
|
||||
Some(x) if x == "toml" => {
|
||||
crate::commands::from_toml::from_toml_string_to_value(contents, contents_span)
|
||||
.map(|c| c.spanned(contents_span))
|
||||
.map_err(move |_| {
|
||||
ShellError::maybe_labeled_error(
|
||||
crate::commands::from_toml::from_toml_string_to_value(contents, contents_tag).map_err(
|
||||
move |_| {
|
||||
ShellError::labeled_error(
|
||||
"Could not open as TOML",
|
||||
"could not open as TOML",
|
||||
name_span,
|
||||
)
|
||||
})
|
||||
}
|
||||
Some(x) if x == "json" => {
|
||||
crate::commands::from_json::from_json_string_to_value(contents, contents_span)
|
||||
.map(|c| c.spanned(contents_span))
|
||||
.map_err(move |_| {
|
||||
ShellError::maybe_labeled_error(
|
||||
"Could not open as JSON",
|
||||
"could not open as JSON",
|
||||
name_span,
|
||||
)
|
||||
})
|
||||
}
|
||||
Some(x) if x == "ini" => {
|
||||
crate::commands::from_ini::from_ini_string_to_value(contents, contents_span)
|
||||
.map(|c| c.spanned(contents_span))
|
||||
.map_err(move |_| {
|
||||
ShellError::maybe_labeled_error(
|
||||
"Could not open as INI",
|
||||
"could not open as INI",
|
||||
name_span,
|
||||
)
|
||||
})
|
||||
}
|
||||
Some(x) if x == "xml" => {
|
||||
crate::commands::from_xml::from_xml_string_to_value(contents, contents_span).map_err(
|
||||
move |_| {
|
||||
ShellError::maybe_labeled_error(
|
||||
"Could not open as XML",
|
||||
"could not open as XML",
|
||||
name_span,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
Some(x) if x == "yml" => {
|
||||
crate::commands::from_yaml::from_yaml_string_to_value(contents, contents_span).map_err(
|
||||
Some(x) if x == "json" => {
|
||||
crate::commands::from_json::from_json_string_to_value(contents, contents_tag).map_err(
|
||||
move |_| {
|
||||
ShellError::maybe_labeled_error(
|
||||
ShellError::labeled_error(
|
||||
"Could not open as JSON",
|
||||
"could not open as JSON",
|
||||
name_span,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
Some(x) if x == "ini" => crate::commands::from_ini::from_ini_string_to_value(
|
||||
contents,
|
||||
contents_tag,
|
||||
)
|
||||
.map_err(move |_| {
|
||||
ShellError::labeled_error("Could not open as INI", "could not open as INI", name_span)
|
||||
}),
|
||||
Some(x) if x == "xml" => crate::commands::from_xml::from_xml_string_to_value(
|
||||
contents,
|
||||
contents_tag,
|
||||
)
|
||||
.map_err(move |_| {
|
||||
ShellError::labeled_error("Could not open as XML", "could not open as XML", name_span)
|
||||
}),
|
||||
Some(x) if x == "yml" => {
|
||||
crate::commands::from_yaml::from_yaml_string_to_value(contents, contents_tag).map_err(
|
||||
move |_| {
|
||||
ShellError::labeled_error(
|
||||
"Could not open as YAML",
|
||||
"could not open as YAML",
|
||||
name_span,
|
||||
@@ -353,9 +377,9 @@ pub fn parse_as_value(
|
||||
)
|
||||
}
|
||||
Some(x) if x == "yaml" => {
|
||||
crate::commands::from_yaml::from_yaml_string_to_value(contents, contents_span).map_err(
|
||||
crate::commands::from_yaml::from_yaml_string_to_value(contents, contents_tag).map_err(
|
||||
move |_| {
|
||||
ShellError::maybe_labeled_error(
|
||||
ShellError::labeled_error(
|
||||
"Could not open as YAML",
|
||||
"could not open as YAML",
|
||||
name_span,
|
||||
@@ -363,6 +387,6 @@ pub fn parse_as_value(
|
||||
},
|
||||
)
|
||||
}
|
||||
_ => Ok(Value::string(contents).spanned(contents_span)),
|
||||
_ => Ok(Value::string(contents).tagged(contents_tag)),
|
||||
}
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@ pub fn pick(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStrea
|
||||
let (input, args) = args.parts();
|
||||
|
||||
if len == 0 {
|
||||
return Err(ShellError::maybe_labeled_error(
|
||||
return Err(ShellError::labeled_error(
|
||||
"Pick requires fields",
|
||||
"needs parameter",
|
||||
span,
|
||||
@@ -28,7 +28,7 @@ pub fn pick(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStrea
|
||||
|
||||
let objects = input
|
||||
.values
|
||||
.map(move |value| select_fields(&value.item, &fields, value.span));
|
||||
.map(move |value| select_fields(&value.item, &fields, value.tag()));
|
||||
|
||||
Ok(objects.from_input_stream())
|
||||
}
|
||||
|
@@ -72,6 +72,7 @@ pub fn filter_plugin(
|
||||
.spawn()
|
||||
.expect("Failed to spawn child process");
|
||||
|
||||
/*
|
||||
{
|
||||
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
|
||||
let stdout = child.stdout.as_mut().expect("Failed to open stdout");
|
||||
@@ -90,41 +91,117 @@ pub fn filter_plugin(
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
*/
|
||||
let mut bos: VecDeque<Tagged<Value>> = VecDeque::new();
|
||||
bos.push_back(Value::Primitive(Primitive::BeginningOfStream).tagged_unknown());
|
||||
|
||||
let mut eos: VecDeque<Tagged<Value>> = VecDeque::new();
|
||||
eos.push_back(Value::Primitive(Primitive::EndOfStream).tagged_unknown());
|
||||
|
||||
let call_info = args.call_info.clone();
|
||||
|
||||
let stream = bos
|
||||
.chain(args.input.values)
|
||||
.chain(eos)
|
||||
.map(move |v| match v {
|
||||
Tagged {
|
||||
item: Value::Primitive(Primitive::BeginningOfStream),
|
||||
..
|
||||
} => {
|
||||
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
|
||||
let stdout = child.stdout.as_mut().expect("Failed to open stdout");
|
||||
|
||||
let mut reader = BufReader::new(stdout);
|
||||
|
||||
let request = JsonRpc::new("begin_filter", call_info.clone());
|
||||
let request_raw = serde_json::to_string(&request).unwrap();
|
||||
let _ = stdin.write(format!("{}\n", request_raw).as_bytes()); // TODO: Handle error
|
||||
|
||||
let mut input = String::new();
|
||||
match reader.read_line(&mut input) {
|
||||
Ok(_) => {
|
||||
let response = serde_json::from_str::<NuResult>(&input);
|
||||
match response {
|
||||
Ok(NuResult::response { params }) => match params {
|
||||
Ok(params) => params,
|
||||
Err(e) => {
|
||||
let mut result = VecDeque::new();
|
||||
result.push_back(ReturnValue::Err(e));
|
||||
result
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
let mut result = VecDeque::new();
|
||||
result.push_back(Err(ShellError::string(format!(
|
||||
"Error while processing begin_filter response: {:?} {}",
|
||||
e, input
|
||||
))));
|
||||
result
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(ShellError::string(format!(
|
||||
"Error while processing input: {:?} {}",
|
||||
e, input
|
||||
)));
|
||||
let mut result = VecDeque::new();
|
||||
result.push_back(Err(ShellError::string(format!(
|
||||
"Error while reading begin_filter response: {:?}",
|
||||
e
|
||||
))));
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut eos: VecDeque<Spanned<Value>> = VecDeque::new();
|
||||
eos.push_back(Value::Primitive(Primitive::EndOfStream).spanned_unknown());
|
||||
|
||||
let stream = args
|
||||
.input
|
||||
.values
|
||||
.chain(eos)
|
||||
.map(move |v| match v {
|
||||
Spanned {
|
||||
Tagged {
|
||||
item: Value::Primitive(Primitive::EndOfStream),
|
||||
..
|
||||
} => {
|
||||
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
|
||||
let stdout = child.stdout.as_mut().expect("Failed to open stdout");
|
||||
|
||||
let _ = BufReader::new(stdout);
|
||||
let request: JsonRpc<std::vec::Vec<Value>> = JsonRpc::new("quit", vec![]);
|
||||
let mut reader = BufReader::new(stdout);
|
||||
|
||||
let request: JsonRpc<std::vec::Vec<Value>> = JsonRpc::new("end_filter", vec![]);
|
||||
let request_raw = serde_json::to_string(&request).unwrap();
|
||||
let _ = stdin.write(format!("{}\n", request_raw).as_bytes()); // TODO: Handle error
|
||||
|
||||
VecDeque::new()
|
||||
let mut input = String::new();
|
||||
match reader.read_line(&mut input) {
|
||||
Ok(_) => {
|
||||
let response = serde_json::from_str::<NuResult>(&input);
|
||||
match response {
|
||||
Ok(NuResult::response { params }) => match params {
|
||||
Ok(params) => {
|
||||
let request: JsonRpc<std::vec::Vec<Value>> =
|
||||
JsonRpc::new("quit", vec![]);
|
||||
let request_raw = serde_json::to_string(&request).unwrap();
|
||||
let _ = stdin.write(format!("{}\n", request_raw).as_bytes()); // TODO: Handle error
|
||||
|
||||
params
|
||||
}
|
||||
Err(e) => {
|
||||
let mut result = VecDeque::new();
|
||||
result.push_back(ReturnValue::Err(e));
|
||||
result
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
let mut result = VecDeque::new();
|
||||
result.push_back(Err(ShellError::string(format!(
|
||||
"Error while processing end_filter response: {:?} {}",
|
||||
e, input
|
||||
))));
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
let mut result = VecDeque::new();
|
||||
result.push_back(Err(ShellError::string(format!(
|
||||
"Error while reading end_filter: {:?}",
|
||||
e
|
||||
))));
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
|
||||
@@ -152,7 +229,7 @@ pub fn filter_plugin(
|
||||
Err(e) => {
|
||||
let mut result = VecDeque::new();
|
||||
result.push_back(Err(ShellError::string(format!(
|
||||
"Error while processing input: {:?} {}",
|
||||
"Error while processing filter response: {:?} {}",
|
||||
e, input
|
||||
))));
|
||||
result
|
||||
@@ -162,7 +239,7 @@ pub fn filter_plugin(
|
||||
Err(e) => {
|
||||
let mut result = VecDeque::new();
|
||||
result.push_back(Err(ShellError::string(format!(
|
||||
"Error while processing input: {:?}",
|
||||
"Error while reading filter response: {:?}",
|
||||
e
|
||||
))));
|
||||
result
|
||||
|
7
src/commands/prev.rs
Normal file
7
src/commands/prev.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
use crate::commands::command::CommandAction;
|
||||
use crate::errors::ShellError;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn prev(_args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
Ok(vec![Ok(ReturnSuccess::Action(CommandAction::PreviousShell))].into())
|
||||
}
|
@@ -10,7 +10,7 @@ pub fn ps(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream
|
||||
|
||||
let list = list
|
||||
.into_iter()
|
||||
.map(|(_, process)| process_dict(process, args.name_span()))
|
||||
.map(|(_, process)| process_dict(process, Tag::unknown_origin(args.call_info.name_span)))
|
||||
.collect::<VecDeque<_>>();
|
||||
|
||||
Ok(list.from_input_stream())
|
||||
|
@@ -10,7 +10,7 @@ pub fn reject(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStr
|
||||
let (input, args) = args.parts();
|
||||
|
||||
if len == 0 {
|
||||
return Err(ShellError::maybe_labeled_error(
|
||||
return Err(ShellError::labeled_error(
|
||||
"Reject requires fields",
|
||||
"needs parameter",
|
||||
span,
|
||||
@@ -26,11 +26,9 @@ pub fn reject(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStr
|
||||
|
||||
let fields = fields?;
|
||||
|
||||
let stream = input.values.map(move |item| {
|
||||
reject_fields(&item, &fields, item.span)
|
||||
.into_spanned_value()
|
||||
.spanned(name_span)
|
||||
});
|
||||
let stream = input
|
||||
.values
|
||||
.map(move |item| reject_fields(&item, &fields, item.tag()).into_tagged_value());
|
||||
|
||||
Ok(stream.from_input_stream())
|
||||
}
|
||||
|
@@ -2,13 +2,15 @@ use crate::commands::StaticCommand;
|
||||
use crate::errors::ShellError;
|
||||
use crate::parser::hir::SyntaxType;
|
||||
use crate::prelude::*;
|
||||
|
||||
use glob::glob;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct Remove;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct RemoveArgs {
|
||||
path: Spanned<PathBuf>,
|
||||
path: Tagged<PathBuf>,
|
||||
recursive: bool,
|
||||
}
|
||||
|
||||
@@ -43,17 +45,32 @@ pub fn rm(
|
||||
file => full_path.push(file),
|
||||
}
|
||||
|
||||
if full_path.is_dir() {
|
||||
if !recursive {
|
||||
return Err(ShellError::maybe_labeled_error(
|
||||
"is a directory",
|
||||
"",
|
||||
context.name,
|
||||
));
|
||||
let entries = glob(&full_path.to_string_lossy());
|
||||
|
||||
if entries.is_err() {
|
||||
return Err(ShellError::string("Invalid pattern."));
|
||||
}
|
||||
|
||||
let entries = entries.unwrap();
|
||||
|
||||
for entry in entries {
|
||||
match entry {
|
||||
Ok(path) => {
|
||||
if path.is_dir() {
|
||||
if !recursive {
|
||||
return Err(ShellError::string(
|
||||
"is a directory",
|
||||
// "is a directory",
|
||||
// args.call_info.name_span,
|
||||
));
|
||||
}
|
||||
std::fs::remove_dir_all(&path).expect("can not remove directory");
|
||||
} else if path.is_file() {
|
||||
std::fs::remove_file(&path).expect("can not remove file");
|
||||
}
|
||||
}
|
||||
Err(e) => return Err(ShellError::string(&format!("{:?}", e))),
|
||||
}
|
||||
std::fs::remove_dir_all(&full_path).expect("can not remove directory");
|
||||
} else if full_path.is_file() {
|
||||
std::fs::remove_file(&full_path).expect("can not remove file");
|
||||
}
|
||||
|
||||
Ok(OutputStream::empty())
|
||||
|
@@ -5,7 +5,6 @@ use crate::commands::to_yaml::value_to_yaml_value;
|
||||
use crate::commands::StaticCommand;
|
||||
use crate::errors::ShellError;
|
||||
use crate::object::Value;
|
||||
use crate::parser::Spanned;
|
||||
use crate::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -13,7 +12,7 @@ pub struct Save;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SaveArgs {
|
||||
path: Spanned<PathBuf>,
|
||||
path: Tagged<PathBuf>,
|
||||
raw: bool,
|
||||
}
|
||||
|
||||
@@ -48,7 +47,7 @@ pub fn save(
|
||||
full_path.push(path.item());
|
||||
|
||||
let stream = async_stream_block! {
|
||||
let input: Vec<Spanned<Value>> = context.input.values.collect().await;
|
||||
let input: Vec<Tagged<Value>> = context.input.values.collect().await;
|
||||
|
||||
let contents = match full_path.extension() {
|
||||
Some(x) if x == "csv" && !save_raw => {
|
||||
|
18
src/commands/shells.rs
Normal file
18
src/commands/shells.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use crate::errors::ShellError;
|
||||
use crate::object::TaggedDictBuilder;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn shells(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let mut shells_out = VecDeque::new();
|
||||
let span = args.call_info.name_span;
|
||||
|
||||
for shell in args.shell_manager.shells.lock().unwrap().iter() {
|
||||
let mut dict = TaggedDictBuilder::new(Tag::unknown_origin(span));
|
||||
dict.insert("name", shell.name());
|
||||
dict.insert("path", shell.path());
|
||||
|
||||
shells_out.push_back(dict.into_tagged_value());
|
||||
}
|
||||
|
||||
Ok(shells_out.to_output_stream())
|
||||
}
|
@@ -1,23 +1,26 @@
|
||||
use crate::errors::ShellError;
|
||||
use crate::object::{SpannedDictBuilder, Value};
|
||||
use crate::object::{TaggedDictBuilder, Value};
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn size(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let input = args.input;
|
||||
let span = args.call_info.name_span;
|
||||
Ok(input
|
||||
.values
|
||||
.map(move |v| match v.item {
|
||||
Value::Primitive(Primitive::String(s)) => ReturnSuccess::value(count(&s, v.span)),
|
||||
_ => Err(ShellError::maybe_labeled_error(
|
||||
"Expected string values from pipeline",
|
||||
"expects strings from pipeline",
|
||||
Some(v.span),
|
||||
Value::Primitive(Primitive::String(ref s)) => ReturnSuccess::value(count(s, v.tag())),
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
span,
|
||||
"value originates from here",
|
||||
v.span(),
|
||||
)),
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
||||
fn count(contents: &str, span: impl Into<Span>) -> Spanned<Value> {
|
||||
fn count(contents: &str, tag: impl Into<Tag>) -> Tagged<Value> {
|
||||
let mut lines: i64 = 0;
|
||||
let mut words: i64 = 0;
|
||||
let mut chars: i64 = 0;
|
||||
@@ -42,7 +45,7 @@ fn count(contents: &str, span: impl Into<Span>) -> Spanned<Value> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut dict = SpannedDictBuilder::new(span);
|
||||
let mut dict = TaggedDictBuilder::new(tag);
|
||||
//TODO: add back in name when we have it in the span
|
||||
//dict.insert("name", Value::string(name));
|
||||
dict.insert("lines", Value::int(lines));
|
||||
@@ -50,5 +53,5 @@ fn count(contents: &str, span: impl Into<Span>) -> Spanned<Value> {
|
||||
dict.insert("chars", Value::int(chars));
|
||||
dict.insert("max length", Value::int(bytes));
|
||||
|
||||
dict.into_spanned_value()
|
||||
dict.into_tagged_value()
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@ pub fn sort_by(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputSt
|
||||
fields
|
||||
.iter()
|
||||
.map(|f| item.get_data_by_key(f).map(|i| i.clone()))
|
||||
.collect::<Vec<Option<Spanned<Value>>>>()
|
||||
.collect::<Vec<Option<Tagged<Value>>>>()
|
||||
});
|
||||
|
||||
vec.into_iter().collect::<VecDeque<_>>()
|
||||
|
@@ -1,5 +1,5 @@
|
||||
use crate::errors::ShellError;
|
||||
use crate::object::{Primitive, SpannedDictBuilder, Value};
|
||||
use crate::object::{Primitive, TaggedDictBuilder, Value};
|
||||
use crate::prelude::*;
|
||||
use log::trace;
|
||||
|
||||
@@ -14,7 +14,7 @@ pub fn split_column(
|
||||
let positional: Vec<_> = args.positional.iter().flatten().cloned().collect();
|
||||
|
||||
if positional.len() == 0 {
|
||||
return Err(ShellError::maybe_labeled_error(
|
||||
return Err(ShellError::labeled_error(
|
||||
"Split-column needs more information",
|
||||
"needs parameter (eg split-column \",\")",
|
||||
span,
|
||||
@@ -24,7 +24,7 @@ pub fn split_column(
|
||||
Ok(input
|
||||
.values
|
||||
.map(move |v| match v.item {
|
||||
Value::Primitive(Primitive::String(s)) => {
|
||||
Value::Primitive(Primitive::String(ref s)) => {
|
||||
let splitter = positional[0].as_string().unwrap().replace("\\n", "\n");
|
||||
trace!("splitting with {:?}", splitter);
|
||||
let split_result: Vec<_> = s.split(&splitter).filter(|s| s.trim() != "").collect();
|
||||
@@ -38,33 +38,35 @@ pub fn split_column(
|
||||
gen_columns.push(format!("Column{}", i + 1));
|
||||
}
|
||||
|
||||
let mut dict = SpannedDictBuilder::new(v.span);
|
||||
let mut dict = TaggedDictBuilder::new(v.tag());
|
||||
for (&k, v) in split_result.iter().zip(gen_columns.iter()) {
|
||||
dict.insert(v.clone(), Primitive::String(k.into()));
|
||||
}
|
||||
|
||||
ReturnSuccess::value(dict.into_spanned_value())
|
||||
ReturnSuccess::value(dict.into_tagged_value())
|
||||
} else if split_result.len() == (positional.len() - 1) {
|
||||
let mut dict = SpannedDictBuilder::new(v.span);
|
||||
let mut dict = TaggedDictBuilder::new(v.tag());
|
||||
for (&k, v) in split_result.iter().zip(positional.iter().skip(1)) {
|
||||
dict.insert(
|
||||
v.as_string().unwrap(),
|
||||
Value::Primitive(Primitive::String(k.into())),
|
||||
);
|
||||
}
|
||||
ReturnSuccess::value(dict.into_spanned_value())
|
||||
ReturnSuccess::value(dict.into_tagged_value())
|
||||
} else {
|
||||
let mut dict = SpannedDictBuilder::new(v.span);
|
||||
let mut dict = TaggedDictBuilder::new(v.tag());
|
||||
for k in positional.iter().skip(1) {
|
||||
dict.insert(k.as_string().unwrap().trim(), Primitive::String("".into()));
|
||||
}
|
||||
ReturnSuccess::value(dict.into_spanned_value())
|
||||
ReturnSuccess::value(dict.into_tagged_value())
|
||||
}
|
||||
}
|
||||
_ => Err(ShellError::maybe_labeled_error(
|
||||
"Expected string values from pipeline",
|
||||
"expects strings from pipeline",
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
span,
|
||||
"value originates from here",
|
||||
v.span(),
|
||||
)),
|
||||
})
|
||||
.to_output_stream())
|
||||
|
@@ -1,6 +1,5 @@
|
||||
use crate::errors::ShellError;
|
||||
use crate::object::{Primitive, Value};
|
||||
use crate::parser::Spanned;
|
||||
use crate::prelude::*;
|
||||
use log::trace;
|
||||
|
||||
@@ -13,10 +12,10 @@ pub fn split_row(
|
||||
let len = args.len();
|
||||
let (input, args) = args.parts();
|
||||
|
||||
let positional: Vec<Spanned<Value>> = args.positional.iter().flatten().cloned().collect();
|
||||
let positional: Vec<Tagged<Value>> = args.positional.iter().flatten().cloned().collect();
|
||||
|
||||
if len == 0 {
|
||||
return Err(ShellError::maybe_labeled_error(
|
||||
return Err(ShellError::labeled_error(
|
||||
"Split-row needs more information",
|
||||
"needs parameter (eg split-row \"\\n\")",
|
||||
span,
|
||||
@@ -26,7 +25,7 @@ pub fn split_row(
|
||||
let stream = input
|
||||
.values
|
||||
.map(move |v| match v.item {
|
||||
Value::Primitive(Primitive::String(s)) => {
|
||||
Value::Primitive(Primitive::String(ref s)) => {
|
||||
let splitter = positional[0].as_string().unwrap().replace("\\n", "\n");
|
||||
trace!("splitting with {:?}", splitter);
|
||||
let split_result: Vec<_> = s.split(&splitter).filter(|s| s.trim() != "").collect();
|
||||
@@ -36,17 +35,19 @@ pub fn split_row(
|
||||
let mut result = VecDeque::new();
|
||||
for s in split_result {
|
||||
result.push_back(ReturnSuccess::value(
|
||||
Value::Primitive(Primitive::String(s.into())).spanned(v.span),
|
||||
Value::Primitive(Primitive::String(s.into())).tagged(v.tag()),
|
||||
));
|
||||
}
|
||||
result
|
||||
}
|
||||
_ => {
|
||||
let mut result = VecDeque::new();
|
||||
result.push_back(Err(ShellError::maybe_labeled_error(
|
||||
"Expected string values from pipeline",
|
||||
"expects strings from pipeline",
|
||||
result.push_back(Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
span,
|
||||
"value originates from here",
|
||||
v.span(),
|
||||
)));
|
||||
result
|
||||
}
|
||||
|
@@ -1,133 +0,0 @@
|
||||
use crate::errors::ShellError;
|
||||
use crate::object::base::OF64;
|
||||
use crate::object::SpannedDictBuilder;
|
||||
use crate::object::{Primitive, Value};
|
||||
use crate::prelude::*;
|
||||
use sys_info::*;
|
||||
use sysinfo::{ComponentExt, DiskExt, NetworkExt, RefreshKind, SystemExt};
|
||||
|
||||
pub fn sysinfo(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let name_span = args.name_span();
|
||||
let mut idx = SpannedDictBuilder::new(name_span);
|
||||
|
||||
if let (Ok(name), Ok(version)) = (os_type(), os_release()) {
|
||||
let mut os_idx = SpannedDictBuilder::new(name_span);
|
||||
os_idx.insert("name", Primitive::String(name));
|
||||
os_idx.insert("version", Primitive::String(version));
|
||||
|
||||
idx.insert_spanned("os", os_idx.into_spanned_value());
|
||||
}
|
||||
|
||||
if let (Ok(num_cpu), Ok(cpu_speed)) = (cpu_num(), cpu_speed()) {
|
||||
let mut cpu_idx = SpannedDictBuilder::new(name_span);
|
||||
cpu_idx.insert("num", Primitive::Int(num_cpu as i64));
|
||||
cpu_idx.insert("speed", Primitive::Int(cpu_speed as i64));
|
||||
|
||||
idx.insert_spanned("cpu", cpu_idx);
|
||||
}
|
||||
|
||||
if let Ok(x) = loadavg() {
|
||||
let mut load_idx = SpannedDictBuilder::new(name_span);
|
||||
|
||||
load_idx.insert("1min", Primitive::Float(OF64::from(x.one)));
|
||||
load_idx.insert("5min", Primitive::Float(OF64::from(x.five)));
|
||||
load_idx.insert("15min", Primitive::Float(OF64::from(x.fifteen)));
|
||||
|
||||
idx.insert_spanned("load avg", load_idx);
|
||||
}
|
||||
|
||||
if let Ok(x) = mem_info() {
|
||||
let mut mem_idx = SpannedDictBuilder::new(name_span);
|
||||
|
||||
mem_idx.insert("total", Primitive::Bytes(x.total as u64 * 1024));
|
||||
mem_idx.insert("free", Primitive::Bytes(x.free as u64 * 1024));
|
||||
mem_idx.insert("avail", Primitive::Bytes(x.avail as u64 * 1024));
|
||||
mem_idx.insert("buffers", Primitive::Bytes(x.buffers as u64 * 1024));
|
||||
mem_idx.insert("cached", Primitive::Bytes(x.cached as u64 * 1024));
|
||||
mem_idx.insert("swap total", Primitive::Bytes(x.swap_total as u64 * 1024));
|
||||
mem_idx.insert("swap free", Primitive::Bytes(x.swap_free as u64 * 1024));
|
||||
|
||||
idx.insert_spanned("mem", mem_idx);
|
||||
}
|
||||
|
||||
/*
|
||||
if let Ok(x) = disk_info() {
|
||||
let mut disk_idx = indexmap::IndexMap::new();
|
||||
disk_idx.insert(
|
||||
"total".to_string(),
|
||||
Value::Primitive(Primitive::Bytes(x.total as u128 * 1024)),
|
||||
);
|
||||
disk_idx.insert(
|
||||
"free".to_string(),
|
||||
Value::Primitive(Primitive::Bytes(x.free as u128 * 1024)),
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
if let Ok(x) = hostname() {
|
||||
idx.insert("hostname", Primitive::String(x));
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
if let Ok(x) = boottime() {
|
||||
let mut boottime_idx = SpannedDictBuilder::new(name_span);
|
||||
boottime_idx.insert("days", Primitive::Int(x.tv_sec / (24 * 3600)));
|
||||
boottime_idx.insert("hours", Primitive::Int((x.tv_sec / 3600) % 24));
|
||||
boottime_idx.insert("mins", Primitive::Int((x.tv_sec / 60) % 60));
|
||||
|
||||
idx.insert_spanned("uptime", boottime_idx);
|
||||
}
|
||||
}
|
||||
|
||||
let system = sysinfo::System::new_with_specifics(RefreshKind::everything().without_processes());
|
||||
let components_list = system.get_components_list();
|
||||
if components_list.len() > 0 {
|
||||
let mut v: Vec<Spanned<Value>> = vec![];
|
||||
for component in components_list {
|
||||
let mut component_idx = SpannedDictBuilder::new(name_span);
|
||||
component_idx.insert("name", Primitive::String(component.get_label().to_string()));
|
||||
component_idx.insert(
|
||||
"temp",
|
||||
Primitive::Float(OF64::from(component.get_temperature() as f64)),
|
||||
);
|
||||
component_idx.insert(
|
||||
"max",
|
||||
Primitive::Float(OF64::from(component.get_max() as f64)),
|
||||
);
|
||||
if let Some(critical) = component.get_critical() {
|
||||
component_idx.insert("critical", Primitive::Float(OF64::from(critical as f64)));
|
||||
}
|
||||
v.push(component_idx.into());
|
||||
}
|
||||
idx.insert("temps", Value::List(v));
|
||||
}
|
||||
|
||||
let disks = system.get_disks();
|
||||
if disks.len() > 0 {
|
||||
let mut v = vec![];
|
||||
|
||||
for disk in disks {
|
||||
let mut disk_idx = SpannedDictBuilder::new(name_span);
|
||||
disk_idx.insert("name", Value::string(disk.get_name().to_string_lossy()));
|
||||
disk_idx.insert("available", Value::bytes(disk.get_available_space()));
|
||||
disk_idx.insert("total", Value::bytes(disk.get_total_space()));
|
||||
v.push(disk_idx.into());
|
||||
}
|
||||
|
||||
idx.insert("disks", Value::List(v));
|
||||
}
|
||||
|
||||
let network = system.get_network();
|
||||
let incoming = network.get_income();
|
||||
let outgoing = network.get_outcome();
|
||||
|
||||
let mut network_idx = SpannedDictBuilder::new(name_span);
|
||||
network_idx.insert("incoming", Value::bytes(incoming));
|
||||
network_idx.insert("outgoing", Value::bytes(outgoing));
|
||||
idx.insert_spanned("network", network_idx);
|
||||
|
||||
let stream = stream![idx.into_spanned_value()];
|
||||
|
||||
Ok(stream.from_input_stream())
|
||||
}
|
@@ -27,7 +27,7 @@ impl StaticCommand for Table {
|
||||
|
||||
pub fn table(_args: TableArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
let stream = async_stream_block! {
|
||||
let input: Vec<Spanned<Value>> = context.input.into_vec().await;
|
||||
let input: Vec<Tagged<Value>> = context.input.into_vec().await;
|
||||
if input.len() > 0 {
|
||||
let mut host = context.host.lock().unwrap();
|
||||
let view = TableView::from_list(&input);
|
||||
|
33
src/commands/tags.rs
Normal file
33
src/commands/tags.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use crate::errors::ShellError;
|
||||
use crate::object::{TaggedDictBuilder, Value};
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn tags(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let source_map = args.call_info.source_map.clone();
|
||||
Ok(args
|
||||
.input
|
||||
.values
|
||||
.map(move |v| {
|
||||
let mut tags = TaggedDictBuilder::new(v.tag());
|
||||
{
|
||||
let origin = v.origin();
|
||||
let span = v.span();
|
||||
let mut dict = TaggedDictBuilder::new(v.tag());
|
||||
dict.insert("start", Value::int(span.start as i64));
|
||||
dict.insert("end", Value::int(span.end as i64));
|
||||
match origin.map(|x| source_map.get(&x)).flatten() {
|
||||
Some(SpanSource::File(source)) => {
|
||||
dict.insert("origin", Value::string(source));
|
||||
}
|
||||
Some(SpanSource::Url(source)) => {
|
||||
dict.insert("origin", Value::string(source));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
tags.insert_tagged("span", dict.into_tagged_value());
|
||||
}
|
||||
|
||||
tags.into_tagged_value()
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
@@ -8,7 +8,7 @@ pub fn to_array(
|
||||
let out = args.input.values.collect();
|
||||
|
||||
Ok(out
|
||||
.map(|vec: Vec<_>| stream![Value::List(vec).spanned_unknown()]) // TODO: args.input should have a span
|
||||
.map(|vec: Vec<_>| stream![Value::List(vec).tagged_unknown()]) // TODO: args.input should have a span
|
||||
.flatten_stream()
|
||||
.from_input_stream())
|
||||
}
|
||||
|
@@ -1,11 +1,8 @@
|
||||
use crate::object::{Primitive, Value};
|
||||
use crate::prelude::*;
|
||||
use csv::WriterBuilder;
|
||||
use log::debug;
|
||||
|
||||
pub fn value_to_csv_value(v: &Value) -> Value {
|
||||
debug!("value_to_csv_value(Value::Object(v)) where v = {:?}", v);
|
||||
|
||||
match v {
|
||||
Value::Primitive(Primitive::String(s)) => Value::Primitive(Primitive::String(s.clone())),
|
||||
Value::Primitive(Primitive::Nothing) => Value::Primitive(Primitive::Nothing),
|
||||
@@ -20,8 +17,6 @@ pub fn to_string(v: &Value) -> Result<String, Box<dyn std::error::Error>> {
|
||||
match v {
|
||||
Value::List(_l) => return Ok(String::from("[list list]")),
|
||||
Value::Object(o) => {
|
||||
debug!("to_csv:to_string(Value::Object(v)) where v = {:?}", v);
|
||||
|
||||
let mut wtr = WriterBuilder::new().from_writer(vec![]);
|
||||
let mut fields: VecDeque<String> = VecDeque::new();
|
||||
let mut values: VecDeque<String> = VecDeque::new();
|
||||
@@ -49,13 +44,15 @@ pub fn to_csv(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStr
|
||||
Ok(out
|
||||
.values
|
||||
.map(move |a| match to_string(&value_to_csv_value(&a.item)) {
|
||||
Ok(x) => {
|
||||
ReturnSuccess::value(Value::Primitive(Primitive::String(x)).spanned(name_span))
|
||||
}
|
||||
Err(_) => Err(ShellError::maybe_labeled_error(
|
||||
"Can not convert to CSV string",
|
||||
"can not convert piped data to CSV string",
|
||||
Ok(x) => ReturnSuccess::value(
|
||||
Value::Primitive(Primitive::String(x)).simple_spanned(name_span),
|
||||
),
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected an object with CSV-compatible structure from pipeline",
|
||||
"requires CSV-compatible input",
|
||||
name_span,
|
||||
format!("{} originates from here", a.item.type_name()),
|
||||
a.span(),
|
||||
)),
|
||||
})
|
||||
.to_output_stream())
|
||||
|
@@ -9,6 +9,7 @@ pub fn value_to_json_value(v: &Value) -> serde_json::Value {
|
||||
}
|
||||
Value::Primitive(Primitive::Date(d)) => serde_json::Value::String(d.to_string()),
|
||||
Value::Primitive(Primitive::EndOfStream) => serde_json::Value::Null,
|
||||
Value::Primitive(Primitive::BeginningOfStream) => serde_json::Value::Null,
|
||||
Value::Primitive(Primitive::Float(f)) => {
|
||||
serde_json::Value::Number(serde_json::Number::from_f64(f.into_inner()).unwrap())
|
||||
}
|
||||
@@ -49,13 +50,15 @@ pub fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputSt
|
||||
.values
|
||||
.map(
|
||||
move |a| match serde_json::to_string(&value_to_json_value(&a)) {
|
||||
Ok(x) => {
|
||||
ReturnSuccess::value(Value::Primitive(Primitive::String(x)).spanned(name_span))
|
||||
}
|
||||
Err(_) => Err(ShellError::maybe_labeled_error(
|
||||
"Can not convert to JSON string",
|
||||
"can not convert piped data to JSON string",
|
||||
Ok(x) => ReturnSuccess::value(
|
||||
Value::Primitive(Primitive::String(x)).simple_spanned(name_span),
|
||||
),
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected an object with JSON-compatible structure from pipeline",
|
||||
"requires JSON-compatible input",
|
||||
name_span,
|
||||
format!("{} originates from here", a.item.type_name()),
|
||||
a.span(),
|
||||
)),
|
||||
},
|
||||
)
|
||||
|
@@ -9,6 +9,9 @@ pub fn value_to_toml_value(v: &Value) -> toml::Value {
|
||||
Value::Primitive(Primitive::EndOfStream) => {
|
||||
toml::Value::String("<End of Stream>".to_string())
|
||||
}
|
||||
Value::Primitive(Primitive::BeginningOfStream) => {
|
||||
toml::Value::String("<Beginning of Stream>".to_string())
|
||||
}
|
||||
Value::Primitive(Primitive::Float(f)) => toml::Value::Float(f.into_inner()),
|
||||
Value::Primitive(Primitive::Int(i)) => toml::Value::Integer(*i),
|
||||
Value::Primitive(Primitive::Nothing) => toml::Value::String("<Nothing>".to_string()),
|
||||
@@ -40,13 +43,15 @@ pub fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputSt
|
||||
.map(move |a| match toml::to_string(&value_to_toml_value(&a)) {
|
||||
Ok(val) => {
|
||||
return ReturnSuccess::value(
|
||||
Value::Primitive(Primitive::String(val)).spanned(name_span),
|
||||
Value::Primitive(Primitive::String(val)).simple_spanned(name_span),
|
||||
)
|
||||
}
|
||||
|
||||
Err(err) => Err(ShellError::type_error(
|
||||
"Can not convert to a TOML string",
|
||||
format!("{:?} - {:?}", a.type_name(), err).spanned(name_span),
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected an object with TOML-compatible structure from pipeline",
|
||||
"requires TOML-compatible input",
|
||||
name_span,
|
||||
format!("{} originates from here", a.item.type_name()),
|
||||
a.span(),
|
||||
)),
|
||||
})
|
||||
.to_output_stream())
|
||||
|
@@ -9,6 +9,7 @@ pub fn value_to_yaml_value(v: &Value) -> serde_yaml::Value {
|
||||
}
|
||||
Value::Primitive(Primitive::Date(d)) => serde_yaml::Value::String(d.to_string()),
|
||||
Value::Primitive(Primitive::EndOfStream) => serde_yaml::Value::Null,
|
||||
Value::Primitive(Primitive::BeginningOfStream) => serde_yaml::Value::Null,
|
||||
Value::Primitive(Primitive::Float(f)) => {
|
||||
serde_yaml::Value::Number(serde_yaml::Number::from(f.into_inner()))
|
||||
}
|
||||
@@ -46,13 +47,15 @@ pub fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputSt
|
||||
.values
|
||||
.map(
|
||||
move |a| match serde_yaml::to_string(&value_to_yaml_value(&a)) {
|
||||
Ok(x) => {
|
||||
ReturnSuccess::value(Value::Primitive(Primitive::String(x)).spanned(name_span))
|
||||
}
|
||||
Err(_) => Err(ShellError::maybe_labeled_error(
|
||||
"Can not convert to YAML string",
|
||||
"can not convert piped data to YAML string",
|
||||
Ok(x) => ReturnSuccess::value(
|
||||
Value::Primitive(Primitive::String(x)).simple_spanned(name_span),
|
||||
),
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected an object with YAML-compatible structure from pipeline",
|
||||
"requires YAML-compatible input",
|
||||
name_span,
|
||||
format!("{} originates from here", a.item.type_name()),
|
||||
a.span(),
|
||||
)),
|
||||
},
|
||||
)
|
||||
|
@@ -9,7 +9,7 @@ pub fn trim(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStre
|
||||
.values
|
||||
.map(move |v| {
|
||||
let string = String::extract(&v)?;
|
||||
ReturnSuccess::value(Value::string(string.trim()).spanned(v.span))
|
||||
ReturnSuccess::value(Value::string(string.trim()).simple_spanned(v.span()))
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
Reference in New Issue
Block a user