Merge master

This commit is contained in:
Jonathan Turner 2019-08-09 16:51:21 +12:00
commit aadacc2d36
115 changed files with 5328 additions and 2164 deletions

1596
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -13,7 +13,7 @@ homepage = "https://github.com/nushell/nushell"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rustyline = "5.0.0"
rustyline = "5.0.1"
sysinfo = "0.9"
chrono = { version = "0.4.7", features = ["serde"] }
chrono-tz = "0.5.1"
@ -25,19 +25,19 @@ nom = "5.0.0"
dunce = "1.0.0"
indexmap = { version = "1.0.2", features = ["serde-1"] }
chrono-humanize = "0.0.11"
byte-unit = "2.1.0"
byte-unit = "3.0.1"
ordered-float = {version = "1.0.2", features = ["serde"]}
prettyprint = "0.7.0"
futures-preview = { version = "=0.3.0-alpha.17", features = ["compat", "io-compat"] }
futures-sink-preview = "=0.3.0-alpha.17"
futures-async-stream = "0.1.0-alpha.1"
async-trait = ""
async-trait = "0.1.7"
futures_codec = "0.2.5"
term = "0.5.2"
bytes = "0.4.12"
log = "0.4.7"
log = "0.4.8"
pretty_env_logger = "0.3.0"
serde = { version = "1.0.94", features = ["derive"] }
serde = { version = "1.0.98", features = ["derive"] }
serde_json = "1.0.40"
serde-hjson = "0.9.0"
serde_yaml = "0.8"
@ -54,8 +54,9 @@ clap = "2.33.0"
enum_derive = "0.1.7"
adhoc_derive = "0.1.2"
lazy_static = "1.3.0"
git2 = "0.9.1"
dirs = "2.0.1"
git2 = "0.9.2"
dirs = "2.0.2"
glob = "0.3.0"
ctrlc = "3.1.3"
ptree = "0.2"
clipboard = "0.5"
@ -64,21 +65,24 @@ roxmltree = "0.6.1"
nom5_locate = "0.1.1"
derive_more = "0.15.0"
enum-utils = "0.1.1"
unicode-xid = "0.1.0"
unicode-xid = "0.2.0"
serde_ini = "0.2.0"
subprocess = "0.1.18"
sys-info = "0.5.7"
mime = "0.3.13"
regex = "1.1.9"
regex = "1.2.1"
pretty-hex = "0.1.0"
neso = "0.5.0"
rawkey = "0.1.1"
crossterm = "0.9.6"
rawkey = "0.1.2"
crossterm = "0.10.2"
tempfile = "3.1.0"
image = "0.21.2"
image = "0.22.1"
semver = "0.9.0"
uuid = {version = "0.7.4", features = [ "v4", "serde" ]}
syntect = "3.2.0"
strip-ansi-escapes = "0.1.0"
onig_sys = "=69.1"
heim = "0.0.6"
[dev-dependencies]
pretty_assertions = "0.6.1"
@ -92,6 +96,10 @@ path = "src/lib.rs"
name = "nu_plugin_inc"
path = "src/plugins/inc.rs"
[[bin]]
name = "nu_plugin_sum"
path = "src/plugins/sum.rs"
[[bin]]
name = "nu_plugin_add"
path = "src/plugins/add.rs"
@ -100,10 +108,18 @@ path = "src/plugins/add.rs"
name = "nu_plugin_edit"
path = "src/plugins/edit.rs"
[[bin]]
name = "nu_plugin_str"
path = "src/plugins/str.rs"
[[bin]]
name = "nu_plugin_skip"
path = "src/plugins/skip.rs"
[[bin]]
name = "nu_plugin_sys"
path = "src/plugins/sys.rs"
[[bin]]
name = "nu_plugin_tree"
path = "src/plugins/tree.rs"
@ -112,6 +128,10 @@ path = "src/plugins/tree.rs"
name = "nu_plugin_binaryview"
path = "src/plugins/binaryview.rs"
[[bin]]
name = "nu_plugin_textview"
path = "src/plugins/textview.rs"
[[bin]]
name = "nu"
path = "src/main.rs"

View file

@ -79,7 +79,7 @@ We can pipeline this into a command that gets the contents of one of the columns
-------------+----------------------------+---------+---------+------+---------
authors | description | edition | license | name | version
-------------+----------------------------+---------+---------+------+---------
[list List] | A shell for the GitHub era | 2018 | MIT | nu | 0.1.2
[list List] | A shell for the GitHub era | 2018 | MIT | nu | 0.1.3
-------------+----------------------------+---------+---------+------+---------
```
@ -87,11 +87,18 @@ Finally, we can use commands outside of Nu once we have the data we want:
```
/home/jonathan/Source/nushell(master)> open Cargo.toml | get package.version | echo $it
0.1.2
0.1.3
```
Here we use the variable `$it` to refer to the value being piped to the external command.
## Shells
By default, Nu will work inside of a single directory and allow you to navigate around your filesystem. Sometimes, you'll want to work in multiple directories at the same time. For this, Nu offers a way of adding additional working directories that you can jump between.
To do so, use the `enter` command, which will allow you create a new "shell" and enter it at the specified path. You can toggle between this new shell and the original shell with the `p` (for previous) and `n` (for next), allowing you to navigate around a ring buffer of shells. Once you're done with a shell, you can `exit` it and remove it from the ring buffer.
Finally, to get a list of all the current shells, you can use the `shells` command.
## Plugins
@ -120,12 +127,19 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat
| command | description |
| ------------- | ------------- |
| cd path | Change to a new path |
| cp source path | Copy files |
| ls (path) | View the contents of the current or given path |
| mkdir path | Make directories, creates intermediary directories as required. |
| date (--utc) | Get the current datetime |
| ps | View current processes |
| sysinfo | View information about the current system |
| sys | View information about the current system |
| open {filename or url} | Load a file into a cell, convert to table if possible (avoid by appending '--raw') |
| rm {file or directory} | Remove a file, (for removing directory append '--recursive') |
| exit | Exit the shell |
| exit (--now) | Exit the current shell (or all shells) |
| enter (path) | Create a new shell and begin at this path |
| p | Go to previous shell |
| n | Go to next shell |
| shells | Display the list of current shells |
## Filters on tables (structured data)
| command | description |
@ -137,13 +151,17 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat
| where condition | Filter table to match the condition |
| inc (field) | Increment a value or version. Optional use the field of a table |
| add field value | Add a new field to the table |
| sum | Sum a column of values |
| edit field value | Edit an existing field to have a new value |
| skip amount | Skip a number of rows |
| first amount | Show only the first number of rows |
| str (field) | Apply string function. Optional use the field of a table |
| tags | Read the tags (metadata) for values |
| to-array | Collapse rows into a single list |
| to-json | Convert table into .json text |
| to-toml | Convert table into .toml text |
| to-yaml | Convert table into .yaml text |
| to-csv | Convert table into .csv text |
## Filters on text (unstructured data)
| command | description |

View file

@ -5,12 +5,11 @@ use crate::commands::classified::{
};
use crate::commands::plugin::JsonRpc;
use crate::commands::plugin::PluginCommand;
use crate::commands::{static_command, Command};
use crate::commands::static_command;
use crate::context::Context;
crate use crate::errors::ShellError;
use crate::git::current_branch;
use crate::object::Value;
use crate::parser::parse::span::Spanned;
use crate::parser::registry::Signature;
use crate::parser::{hir, Pipeline, PipelineElement, TokenNode};
use crate::prelude::*;
@ -150,11 +149,14 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
command("from-xml", Box::new(from_xml::from_xml)),
command("ps", Box::new(ps::ps)),
command("ls", Box::new(ls::ls)),
command("sysinfo", Box::new(sysinfo::sysinfo)),
command("size", Box::new(size::size)),
command("from-yaml", Box::new(from_yaml::from_yaml)),
command("exit", Box::new(exit::exit)),
command("enter", Box::new(enter::enter)),
command("n", Box::new(next::next)),
command("p", Box::new(prev::prev)),
command("lines", Box::new(lines::lines)),
command("pick", Box::new(pick::pick)),
command("shells", Box::new(shells::shells)),
command("split-column", Box::new(split_column::split_column)),
command("split-row", Box::new(split_row::split_row)),
command("lines", Box::new(lines::lines)),
@ -166,6 +168,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
command("to-toml", Box::new(to_toml::to_toml)),
command("to-yaml", Box::new(to_yaml::to_yaml)),
command("sort-by", Box::new(sort_by::sort_by)),
command("tags", Box::new(tags::tags)),
static_command(Get),
static_command(Cd),
static_command(Remove),
@ -173,8 +176,12 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
static_command(Where),
static_command(Config),
static_command(SkipWhile),
static_command(Exit),
static_command(Clip),
static_command(Autoview),
static_command(Copycp),
static_command(Date),
static_command(Mkdir),
static_command(Save),
static_command(Table),
static_command(VTable),
@ -183,15 +190,15 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
let _ = load_plugins(&mut context);
let config = Config::builder().color_mode(ColorMode::Forced).build();
let h = crate::shell::Helper::new(context.clone_commands());
let mut rl: Editor<crate::shell::Helper> = Editor::with_config(config);
//let h = crate::shell::Helper::new(context.clone_commands());
let mut rl: Editor<_> = Editor::with_config(config);
#[cfg(windows)]
{
let _ = ansi_term::enable_ansi_support();
}
rl.set_helper(Some(h));
//rl.set_helper(Some(h));
let _ = rl.load_history("history.txt");
let ctrl_c = Arc::new(AtomicBool::new(false));
@ -207,10 +214,12 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
continue;
}
let cwd = {
let env = context.env.lock().unwrap();
env.path().display().to_string()
};
let cwd = context.shell_manager.path();
rl.set_helper(Some(crate::shell::Helper::new(
context.shell_manager.clone(),
)));
let readline = rl.readline(&format!(
"{}{}> ",
cwd,
@ -336,7 +345,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
.commands
.push(ClassifiedCommand::Internal(InternalCommand {
command: static_command(autoview::Autoview),
name_span: None,
name_span: Span::unknown(),
source_map: ctx.source_map.clone(),
args: hir::Call::new(
Box::new(hir::Expression::synthetic_string("autoview")),
@ -473,19 +482,19 @@ fn classify_command(
Ok(ClassifiedCommand::Internal(InternalCommand {
command,
name_span: Some(head.span().clone()),
name_span: head.span().clone(),
source_map: context.source_map.clone(),
args,
}))
}
false => {
let arg_list_strings: Vec<Spanned<String>> = match call.children() {
let arg_list_strings: Vec<Tagged<String>> = match call.children() {
//Some(args) => args.iter().map(|i| i.as_external_arg(source)).collect(),
Some(args) => args
.iter()
.filter_map(|i| match i {
TokenNode::Whitespace(_) => None,
other => Some(Spanned::from_item(
other => Some(Tagged::from_simple_spanned_item(
other.as_external_arg(source),
other.span(),
)),
@ -496,7 +505,7 @@ fn classify_command(
Ok(ClassifiedCommand::External(ExternalCommand {
name: name.to_string(),
name_span: Some(head.span().clone()),
name_span: head.span().clone(),
args: arg_list_strings,
}))
}

View file

@ -8,6 +8,9 @@ crate mod classified;
crate mod clip;
crate mod command;
crate mod config;
crate mod cp;
crate mod date;
crate mod enter;
crate mod exit;
crate mod first;
crate mod from_csv;
@ -19,20 +22,24 @@ crate mod from_yaml;
crate mod get;
crate mod lines;
crate mod ls;
crate mod mkdir;
crate mod next;
crate mod open;
crate mod pick;
crate mod plugin;
crate mod prev;
crate mod ps;
crate mod reject;
crate mod rm;
crate mod save;
crate mod shells;
crate mod size;
crate mod skip_while;
crate mod sort_by;
crate mod split_column;
crate mod split_row;
crate mod sysinfo;
crate mod table;
crate mod tags;
crate mod to_array;
crate mod to_csv;
crate mod to_json;
@ -50,7 +57,11 @@ crate use command::{
UnevaluatedCallInfo,
};
crate use config::Config;
crate use cp::Copycp;
crate use date::Date;
crate use exit::Exit;
crate use get::Get;
crate use mkdir::Mkdir;
crate use open::Open;
crate use rm::Remove;
crate use save::Save;

View file

@ -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);
}
}
_ => {}
}
}

View file

@ -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)
}

View file

@ -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>>
))
}
}

View file

@ -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();

View file

@ -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: &registry::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,

View file

@ -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
View 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
View 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
View 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())
}

View file

@ -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())
}
}

View file

@ -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(),
))
}
};

View file

@ -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())
}

View file

@ -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())
}

View file

@ -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())
}

View file

@ -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())
}

View file

@ -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())
}

View file

@ -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())
}

View file

@ -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),
..
}) => {

View file

@ -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
}

View file

@ -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
View 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
View 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())
}

View file

@ -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)),
}
}

View file

@ -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())
}

View file

@ -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
View 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())
}

View file

@ -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())

View file

@ -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())
}

View file

@ -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())

View file

@ -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
View 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())
}

View file

@ -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()
}

View file

@ -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<_>>()

View file

@ -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())

View file

@ -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
}

View file

@ -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())
}

View file

@ -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
View 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())
}

View file

@ -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())
}

View file

@ -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())

View file

@ -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(),
)),
},
)

View file

@ -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())

View file

@ -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(),
)),
},
)

View file

@ -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())
}

View file

@ -1,5 +1,5 @@
use crate::commands::{Command, UnevaluatedCallInfo};
use crate::parser::{hir, Span};
use crate::parser::hir;
use crate::prelude::*;
use derive_new::new;
@ -75,7 +75,7 @@ pub struct Context {
registry: CommandRegistry,
crate source_map: SourceMap,
crate host: Arc<Mutex<dyn Host + Send>>,
crate env: Arc<Mutex<Environment>>,
crate shell_manager: ShellManager,
}
impl Context {
@ -88,7 +88,7 @@ impl Context {
registry: CommandRegistry::new(),
source_map: SourceMap::new(),
host: Arc::new(Mutex::new(crate::env::host::BasicHost)),
env: Arc::new(Mutex::new(Environment::basic()?)),
shell_manager: ShellManager::basic()?,
})
}
@ -117,13 +117,25 @@ impl Context {
crate async fn run_command(
&mut self,
command: Arc<Command>,
name_span: Option<Span>,
name_span: Span,
source_map: SourceMap,
args: hir::Call,
source: Text,
input: InputStream,
) -> Result<OutputStream, ShellError> {
let command_args = self.command_args(args, input, source, source_map, name_span);
/*
let command_args = CommandArgs {
host: self.host.clone(),
shell_manager: self.shell_manager.clone(),
call_info: CallInfo {
name_span,
source_map,
args,
},
input,
};
*/
command.run(command_args, self.registry()).await
}
@ -133,7 +145,7 @@ impl Context {
args: hir::Call,
source: Text,
source_map: SourceMap,
name_span: Option<Span>,
name_span: Span,
) -> UnevaluatedCallInfo {
UnevaluatedCallInfo {
args,
@ -149,11 +161,11 @@ impl Context {
input: InputStream,
source: Text,
source_map: SourceMap,
name_span: Option<Span>,
name_span: Span,
) -> CommandArgs {
CommandArgs {
host: self.host.clone(),
env: self.env.clone(),
shell_manager: self.shell_manager.clone(),
call_info: self.call_info(args, source, source_map, name_span),
input,
}

View file

@ -1,5 +1,3 @@
crate mod environment;
crate mod host;
crate use self::environment::Environment;
crate use self::host::Host;

View file

@ -1,18 +0,0 @@
use std::path::{Path, PathBuf};
#[derive(Debug, Clone)]
pub struct Environment {
crate path: PathBuf,
}
impl Environment {
pub fn basic() -> Result<Environment, std::io::Error> {
let path = std::env::current_dir()?;
Ok(Environment { path })
}
pub fn path(&self) -> &Path {
self.path.as_path()
}
}

View file

@ -1,7 +1,6 @@
#[allow(unused)]
use crate::prelude::*;
use crate::parser::{Span, Spanned};
use ansi_term::Color;
use derive_new::new;
use language_reporting::{Diagnostic, Label, Severity};
@ -9,23 +8,18 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)]
pub enum Description {
Source(Spanned<String>),
Source(Tagged<String>),
Synthetic(String),
}
impl Description {
pub fn from(item: Spanned<impl Into<String>>) -> Description {
match item {
Spanned {
span:
Span {
start: 0,
end: 0,
source: None,
},
item,
} => Description::Synthetic(item.into()),
Spanned { span, item } => Description::Source(Spanned::from_item(item.into(), span)),
pub fn from(value: Tagged<impl Into<String>>) -> Description {
let value_span = value.span();
let value_tag = value.tag();
match value_span {
Span { start: 0, end: 0 } => Description::Synthetic(value.item.into()),
_ => Description::Source(Tagged::from_item(value.item.into(), value_tag)),
}
}
}
@ -33,7 +27,7 @@ impl Description {
impl Description {
fn into_label(self) -> Result<Label<Span>, String> {
match self {
Description::Source(s) => Ok(Label::new_primary(s.span).with_message(s.item)),
Description::Source(s) => Ok(Label::new_primary(s.span()).with_message(s.item)),
Description::Synthetic(s) => Err(s),
}
}
@ -47,13 +41,13 @@ pub enum ArgumentError {
}
pub fn labelled(
span: impl Into<Option<Span>>,
span: impl Into<Span>,
heading: &'a str,
span_message: &'a str,
) -> impl FnOnce(ShellError) -> ShellError + 'a {
let span = span.into();
move |_| ShellError::maybe_labeled_error(heading, span_message, span)
move |_| ShellError::labeled_error(heading, span_message, span)
}
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)]
@ -74,7 +68,7 @@ impl serde::de::Error for ShellError {
impl ShellError {
crate fn type_error(
expected: impl Into<String>,
actual: Spanned<impl Into<String>>,
actual: Tagged<impl Into<String>>,
) -> ShellError {
ProximateShellError::TypeError {
expected: expected.into(),
@ -84,8 +78,8 @@ impl ShellError {
}
crate fn coerce_error(
left: Spanned<impl Into<String>>,
right: Spanned<impl Into<String>>,
left: Tagged<impl Into<String>>,
right: Tagged<impl Into<String>>,
) -> ShellError {
ProximateShellError::CoerceError {
left: left.map(|l| l.into()),
@ -175,9 +169,9 @@ impl ShellError {
ProximateShellError::TypeError {
expected,
actual:
Spanned {
Tagged {
item: Some(actual),
span,
tag: Tag { span, .. },
},
} => Diagnostic::new(Severity::Error, "Type Error").with_label(
Label::new_primary(span)
@ -186,7 +180,11 @@ impl ShellError {
ProximateShellError::TypeError {
expected,
actual: Spanned { item: None, span },
actual:
Tagged {
item: None,
tag: Tag { span, .. },
},
} => Diagnostic::new(Severity::Error, "Type Error")
.with_label(Label::new_primary(span).with_message(expected)),
@ -211,8 +209,8 @@ impl ShellError {
ProximateShellError::Diagnostic(diag) => diag.diagnostic,
ProximateShellError::CoerceError { left, right } => {
Diagnostic::new(Severity::Error, "Coercion error")
.with_label(Label::new_primary(left.span).with_message(left.item))
.with_label(Label::new_secondary(right.span).with_message(right.item))
.with_label(Label::new_primary(left.span()).with_message(left.item))
.with_label(Label::new_secondary(right.span()).with_message(right.item))
}
}
}
@ -228,18 +226,20 @@ impl ShellError {
)
}
pub fn maybe_labeled_error(
pub fn labeled_error_with_secondary(
msg: impl Into<String>,
label: impl Into<String>,
span: Option<Span>,
primary_label: impl Into<String>,
primary_span: Span,
secondary_label: impl Into<String>,
secondary_span: Span,
) -> ShellError {
match span {
Some(span) => ShellError::diagnostic(
Diagnostic::new(Severity::Error, msg.into())
.with_label(Label::new_primary(span).with_message(label.into())),
),
None => ShellError::string(msg),
}
ShellError::diagnostic(
Diagnostic::new_error(msg.into())
.with_label(Label::new_primary(primary_span).with_message(primary_label.into()))
.with_label(
Label::new_secondary(secondary_span).with_message(secondary_label.into()),
),
)
}
pub fn string(title: impl Into<String>) -> ShellError {
@ -260,7 +260,7 @@ pub enum ProximateShellError {
String(StringError),
TypeError {
expected: String,
actual: Spanned<Option<String>>,
actual: Tagged<Option<String>>,
},
MissingProperty {
subpath: Description,
@ -273,8 +273,8 @@ pub enum ProximateShellError {
},
Diagnostic(ShellDiagnostic),
CoerceError {
left: Spanned<String>,
right: Spanned<String>,
left: Tagged<String>,
right: Tagged<String>,
},
}
impl ProximateShellError {

View file

@ -2,7 +2,7 @@ use crate::errors::Description;
use crate::object::base::Block;
use crate::parser::{
hir::{self, Expression, RawExpression},
CommandRegistry, Spanned, Text,
CommandRegistry, Text,
};
use crate::prelude::*;
use derive_new::new;
@ -10,20 +10,20 @@ use indexmap::IndexMap;
#[derive(new)]
pub struct Scope {
it: Spanned<Value>,
it: Tagged<Value>,
#[new(default)]
vars: IndexMap<String, Spanned<Value>>,
vars: IndexMap<String, Tagged<Value>>,
}
impl Scope {
crate fn empty() -> Scope {
Scope {
it: Value::nothing().spanned_unknown(),
it: Value::nothing().tagged_unknown(),
vars: IndexMap::new(),
}
}
crate fn it_value(value: Spanned<Value>) -> Scope {
crate fn it_value(value: Tagged<Value>) -> Scope {
Scope {
it: value,
vars: IndexMap::new(),
@ -36,19 +36,20 @@ crate fn evaluate_baseline_expr(
registry: &CommandRegistry,
scope: &Scope,
source: &Text,
) -> Result<Spanned<Value>, ShellError> {
) -> Result<Tagged<Value>, ShellError> {
match &expr.item {
RawExpression::Literal(literal) => Ok(evaluate_literal(expr.copy_span(*literal), source)),
RawExpression::Synthetic(hir::Synthetic::String(s)) => {
Ok(Value::string(s).spanned_unknown())
}
RawExpression::Synthetic(hir::Synthetic::String(s)) => Ok(Value::string(s).tagged_unknown()),
RawExpression::Variable(var) => evaluate_reference(var, scope, source),
RawExpression::Binary(binary) => {
let left = evaluate_baseline_expr(binary.left(), registry, scope, source)?;
let right = evaluate_baseline_expr(binary.right(), registry, scope, source)?;
match left.compare(binary.op(), &*right) {
Ok(result) => Ok(Spanned::from_item(Value::boolean(result), *expr.span())),
Ok(result) => Ok(Tagged::from_simple_spanned_item(
Value::boolean(result),
expr.span(),
)),
Err((left_type, right_type)) => Err(ShellError::coerce_error(
binary.left().copy_span(left_type),
binary.right().copy_span(right_type),
@ -63,10 +64,10 @@ crate fn evaluate_baseline_expr(
exprs.push(expr);
}
Ok(Value::List(exprs).spanned(expr.span()))
Ok(Value::List(exprs).tagged(Tag::unknown_origin(expr.span())))
}
RawExpression::Block(block) => Ok(Spanned::from_item(
Value::Block(Block::new(block.clone(), source.clone(), *expr.span())),
RawExpression::Block(block) => Ok(Tagged::from_simple_spanned_item(
Value::Block(Block::new(block.clone(), source.clone(), expr.span())),
expr.span(),
)),
RawExpression::Path(path) => {
@ -79,12 +80,12 @@ crate fn evaluate_baseline_expr(
match next {
None => {
return Err(ShellError::missing_property(
Description::from(item.spanned_type_name()),
Description::from(item.tagged_type_name()),
Description::from(name.clone()),
))
}
Some(next) => {
item = Spanned::from_item(
item = Tagged::from_simple_spanned_item(
next.clone().item,
(expr.span().start, name.span().end),
)
@ -92,13 +93,16 @@ crate fn evaluate_baseline_expr(
};
}
Ok(Spanned::from_item(item.item().clone(), expr.span()))
Ok(Tagged::from_simple_spanned_item(
item.item().clone(),
expr.span(),
))
}
RawExpression::Boolean(_boolean) => unimplemented!(),
}
}
fn evaluate_literal(literal: Spanned<hir::Literal>, source: &Text) -> Spanned<Value> {
fn evaluate_literal(literal: Tagged<hir::Literal>, source: &Text) -> Tagged<Value> {
let result = match literal.item {
hir::Literal::Integer(int) => Value::int(int),
hir::Literal::Size(int, unit) => unit.compute(int),
@ -113,13 +117,13 @@ fn evaluate_reference(
name: &hir::Variable,
scope: &Scope,
source: &Text,
) -> Result<Spanned<Value>, ShellError> {
) -> Result<Tagged<Value>, ShellError> {
match name {
hir::Variable::It(span) => Ok(Spanned::from_item(scope.it.item.clone(), span)),
hir::Variable::It(span) => Ok(scope.it.item.clone().simple_spanned(span)),
hir::Variable::Other(span) => Ok(scope
.vars
.get(span.slice(source))
.map(|v| v.clone())
.unwrap_or_else(|| Value::nothing().spanned(span))),
.unwrap_or_else(|| Value::nothing().simple_spanned(span))),
}
}

View file

@ -14,7 +14,7 @@ pub struct TableView {
}
impl TableView {
fn merge_descriptors(values: &[Spanned<Value>]) -> Vec<String> {
fn merge_descriptors(values: &[Tagged<Value>]) -> Vec<String> {
let mut ret = vec![];
for value in values {
for desc in value.data_descriptors() {
@ -26,7 +26,7 @@ impl TableView {
ret
}
pub fn from_list(values: &[Spanned<Value>]) -> Option<TableView> {
pub fn from_list(values: &[Tagged<Value>]) -> Option<TableView> {
if values.len() == 0 {
return None;
}

View file

@ -12,7 +12,7 @@ pub struct VTableView {
}
impl VTableView {
pub fn from_list(values: &[Spanned<Value>]) -> Option<VTableView> {
pub fn from_list(values: &[Tagged<Value>]) -> Option<VTableView> {
if values.len() == 0 {
return None;
}

View file

@ -31,14 +31,15 @@ mod traits;
mod utils;
pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue};
pub use crate::context::SpanSource;
pub use crate::context::{SourceMap, SpanSource};
pub use crate::env::host::BasicHost;
pub use crate::parser::parse::span::SpannedItem;
pub use crate::parser::Spanned;
pub use crate::object::base::OF64;
pub use crate::plugin::{serve_plugin, Plugin};
pub use crate::utils::{AbsolutePath, RelativePath};
pub use cli::cli;
pub use errors::ShellError;
pub use object::base::{Primitive, Value};
pub use object::dict::{Dictionary, TaggedDictBuilder};
pub use object::meta::{Span, Tag, Tagged, TaggedItem};
pub use parser::parse::text::Text;
pub use parser::registry::{EvaluatedArgs, NamedType, PositionalType, Signature};

View file

@ -3,10 +3,11 @@ crate mod config;
crate mod dict;
crate mod files;
crate mod into;
crate mod meta;
crate mod process;
crate mod types;
#[allow(unused)]
crate use base::{Block, Primitive, Switch, Value};
crate use dict::{Dictionary, SpannedDictBuilder};
crate use dict::{Dictionary, TaggedDictBuilder};
crate use files::dir_entry_dict;

View file

@ -1,8 +1,8 @@
use crate::context::CommandRegistry;
use crate::errors::ShellError;
use crate::evaluate::{evaluate_baseline_expr, Scope};
use crate::object::SpannedDictBuilder;
use crate::parser::{hir, Operator, Span, Spanned};
use crate::object::TaggedDictBuilder;
use crate::parser::{hir, Operator};
use crate::prelude::*;
use crate::Text;
use ansi_term::Color;
@ -44,6 +44,8 @@ pub enum Primitive {
Date(DateTime<Utc>),
Path(PathBuf),
// Stream markers (used as bookend markers rather than actual values)
BeginningOfStream,
EndOfStream,
}
@ -53,6 +55,7 @@ impl Primitive {
match self {
Nothing => "nothing",
BeginningOfStream => "beginning-of-stream",
EndOfStream => "end-of-stream",
Path(_) => "path",
Int(_) => "int",
@ -70,6 +73,7 @@ impl Primitive {
match self {
Nothing => write!(f, "Nothing"),
BeginningOfStream => write!(f, "BeginningOfStream"),
EndOfStream => write!(f, "EndOfStream"),
Int(int) => write!(f, "{}", int),
Path(path) => write!(f, "{}", path.display()),
@ -84,6 +88,7 @@ impl Primitive {
pub fn format(&self, field_name: Option<&String>) -> String {
match self {
Primitive::Nothing => format!("{}", Color::Black.bold().paint("-")),
Primitive::BeginningOfStream => format!("{}", Color::Black.bold().paint("-")),
Primitive::EndOfStream => format!("{}", Color::Black.bold().paint("-")),
Primitive::Path(p) => format!("{}", p.display()),
Primitive::Bytes(b) => {
@ -131,11 +136,11 @@ pub struct Block {
}
impl Block {
pub fn invoke(&self, value: &Spanned<Value>) -> Result<Spanned<Value>, ShellError> {
pub fn invoke(&self, value: &Tagged<Value>) -> Result<Tagged<Value>, ShellError> {
let scope = Scope::new(value.clone());
if self.expressions.len() == 0 {
return Ok(Spanned::from_item(Value::nothing(), self.span));
return Ok(Value::nothing().simple_spanned(self.span));
}
let mut last = None;
@ -159,17 +164,17 @@ pub enum Value {
Object(crate::object::Dictionary),
#[serde(with = "serde_bytes")]
Binary(Vec<u8>),
List(Vec<Spanned<Value>>),
List(Vec<Tagged<Value>>),
#[allow(unused)]
Block(Block),
}
pub fn debug_list(values: &'a Vec<Spanned<Value>>) -> ValuesDebug<'a> {
pub fn debug_list(values: &'a Vec<Tagged<Value>>) -> ValuesDebug<'a> {
ValuesDebug { values }
}
pub struct ValuesDebug<'a> {
values: &'a Vec<Spanned<Value>>,
values: &'a Vec<Tagged<Value>>,
}
impl fmt::Debug for ValuesDebug<'a> {
@ -181,7 +186,7 @@ impl fmt::Debug for ValuesDebug<'a> {
}
pub struct ValueDebug<'a> {
value: &'a Spanned<Value>,
value: &'a Tagged<Value>,
}
impl fmt::Debug for ValueDebug<'a> {
@ -196,17 +201,17 @@ impl fmt::Debug for ValueDebug<'a> {
}
}
impl Spanned<Value> {
crate fn spanned_type_name(&self) -> Spanned<String> {
impl Tagged<Value> {
crate fn tagged_type_name(&self) -> Tagged<String> {
let name = self.type_name();
Spanned::from_item(name, self.span)
Tagged::from_simple_spanned_item(name, self.span())
}
}
impl std::convert::TryFrom<&'a Spanned<Value>> for Block {
impl std::convert::TryFrom<&'a Tagged<Value>> for Block {
type Error = ShellError;
fn try_from(value: &'a Spanned<Value>) -> Result<Block, ShellError> {
fn try_from(value: &'a Tagged<Value>) -> Result<Block, ShellError> {
match value.item() {
Value::Block(block) => Ok(block.clone()),
v => Err(ShellError::type_error(
@ -217,10 +222,10 @@ impl std::convert::TryFrom<&'a Spanned<Value>> for Block {
}
}
impl std::convert::TryFrom<&'a Spanned<Value>> for i64 {
impl std::convert::TryFrom<&'a Tagged<Value>> for i64 {
type Error = ShellError;
fn try_from(value: &'a Spanned<Value>) -> Result<i64, ShellError> {
fn try_from(value: &'a Tagged<Value>) -> Result<i64, ShellError> {
match value.item() {
Value::Primitive(Primitive::Int(int)) => Ok(*int),
v => Err(ShellError::type_error(
@ -247,10 +252,10 @@ impl Switch {
}
}
impl std::convert::TryFrom<Option<&'a Spanned<Value>>> for Switch {
impl std::convert::TryFrom<Option<&'a Tagged<Value>>> for Switch {
type Error = ShellError;
fn try_from(value: Option<&'a Spanned<Value>>) -> Result<Switch, ShellError> {
fn try_from(value: Option<&'a Tagged<Value>>) -> Result<Switch, ShellError> {
match value {
None => Ok(Switch::Absent),
Some(value) => match value.item() {
@ -264,7 +269,7 @@ impl std::convert::TryFrom<Option<&'a Spanned<Value>>> for Switch {
}
}
impl Spanned<Value> {
impl Tagged<Value> {
crate fn debug(&'a self) -> ValueDebug<'a> {
ValueDebug { value: self }
}
@ -296,13 +301,13 @@ impl Value {
}
}
crate fn get_data_by_key(&'a self, name: &str) -> Option<&Spanned<Value>> {
crate fn get_data_by_key(&'a self, name: &str) -> Option<&Tagged<Value>> {
match self {
Value::Object(o) => o.get_data_by_key(name),
Value::List(l) => {
for item in l {
match item {
Spanned {
Tagged {
item: Value::Object(o),
..
} => match o.get_data_by_key(name) {
@ -319,14 +324,14 @@ impl Value {
}
#[allow(unused)]
crate fn get_data_by_index(&'a self, idx: usize) -> Option<&Spanned<Value>> {
crate fn get_data_by_index(&'a self, idx: usize) -> Option<&Tagged<Value>> {
match self {
Value::List(l) => l.iter().nth(idx),
_ => None,
}
}
pub fn get_data_by_path(&'a self, span: Span, path: &str) -> Option<Spanned<&Value>> {
pub fn get_data_by_path(&'a self, tag: Tag, path: &str) -> Option<Tagged<&Value>> {
let mut current = self;
for p in path.split(".") {
match current.get_data_by_key(p) {
@ -335,18 +340,15 @@ impl Value {
}
}
Some(Spanned {
item: current,
span,
})
Some(Tagged::from_item(current, tag))
}
pub fn insert_data_at_path(
&'a self,
span: Span,
tag: Tag,
path: &str,
new_value: Value,
) -> Option<Spanned<Value>> {
) -> Option<Tagged<Value>> {
let mut new_obj = self.clone();
let split_path: Vec<_> = path.split(".").collect();
@ -361,19 +363,13 @@ impl Value {
Value::Object(o) => {
o.entries.insert(
split_path[idx + 1].to_string(),
Spanned {
item: new_value,
span,
},
Tagged::from_item(new_value, tag),
);
}
_ => {}
}
return Some(Spanned {
item: new_obj,
span,
});
return Some(Tagged::from_item(new_obj, tag));
} else {
match next.item {
Value::Object(ref mut o) => {
@ -393,10 +389,10 @@ impl Value {
pub fn replace_data_at_path(
&'a self,
span: Span,
tag: Tag,
path: &str,
replaced_value: Value,
) -> Option<Spanned<Value>> {
) -> Option<Tagged<Value>> {
let mut new_obj = self.clone();
let split_path: Vec<_> = path.split(".").collect();
@ -407,14 +403,8 @@ impl Value {
match current.entries.get_mut(split_path[idx]) {
Some(next) => {
if idx == (split_path.len() - 1) {
*next = Spanned {
item: replaced_value,
span,
};
return Some(Spanned {
item: new_obj,
span,
});
*next = Tagged::from_item(replaced_value, tag);
return Some(Tagged::from_item(new_obj, tag));
} else {
match next.item {
Value::Object(ref mut o) => {
@ -451,8 +441,12 @@ impl Value {
.map(|e| e.source(&b.source).to_string()),
"; ",
),
Value::Object(_) => format!("[object Object]"),
Value::List(_) => format!("[list List]"),
Value::Object(_) => format!("[{}]", self.type_name()),
Value::List(l) => format!(
"[{} {}]",
l.len(),
if l.len() == 1 { "item" } else { "items" }
),
Value::Binary(_) => format!("<binary>"),
}
}
@ -492,6 +486,16 @@ impl Value {
}
}
crate fn as_pair(&self) -> Result<(Tagged<Value>, Tagged<Value>), ShellError> {
match self {
Value::List(list) if list.len() == 2 => Ok((list[0].clone(), list[1].clone())),
other => Err(ShellError::string(format!(
"Expected pair, got {:?}",
other
))),
}
}
crate fn as_string(&self) -> Result<String, ShellError> {
match self {
Value::Primitive(Primitive::String(s)) => Ok(s.clone()),
@ -565,8 +569,8 @@ impl Value {
}
}
crate fn select_fields(obj: &Value, fields: &[String], span: impl Into<Span>) -> Spanned<Value> {
let mut out = SpannedDictBuilder::new(span);
crate fn select_fields(obj: &Value, fields: &[String], tag: impl Into<Tag>) -> Tagged<Value> {
let mut out = TaggedDictBuilder::new(tag);
let descs = obj.data_descriptors();
@ -577,11 +581,11 @@ crate fn select_fields(obj: &Value, fields: &[String], span: impl Into<Span>) ->
}
}
out.into_spanned_value()
out.into_tagged_value()
}
crate fn reject_fields(obj: &Value, fields: &[String], span: impl Into<Span>) -> Spanned<Value> {
let mut out = SpannedDictBuilder::new(span);
crate fn reject_fields(obj: &Value, fields: &[String], tag: impl Into<Tag>) -> Tagged<Value> {
let mut out = TaggedDictBuilder::new(tag);
let descs = obj.data_descriptors();
@ -592,7 +596,7 @@ crate fn reject_fields(obj: &Value, fields: &[String], span: impl Into<Span>) ->
}
}
out.into_spanned_value()
out.into_tagged_value()
}
#[allow(unused)]

View file

@ -19,7 +19,7 @@ const APP_INFO: AppInfo = AppInfo {
#[derive(Deserialize, Serialize)]
struct Config {
#[serde(flatten)]
extra: IndexMap<String, Value>,
extra: IndexMap<String, Tagged<Value>>,
}
crate fn config_path() -> Result<PathBuf, ShellError> {
@ -29,7 +29,7 @@ crate fn config_path() -> Result<PathBuf, ShellError> {
Ok(location.join("config.toml"))
}
crate fn write_config(config: &IndexMap<String, Spanned<Value>>) -> Result<(), ShellError> {
crate fn write_config(config: &IndexMap<String, Tagged<Value>>) -> Result<(), ShellError> {
let location = app_root(AppDataType::UserConfig, &APP_INFO)
.map_err(|err| ShellError::string(&format!("Couldn't open config file:\n{}", err)))?;
@ -45,7 +45,7 @@ crate fn write_config(config: &IndexMap<String, Spanned<Value>>) -> Result<(), S
Ok(())
}
crate fn config(span: impl Into<Span>) -> Result<IndexMap<String, Spanned<Value>>, ShellError> {
crate fn config(span: impl Into<Span>) -> Result<IndexMap<String, Tagged<Value>>, ShellError> {
let span = span.into();
let location = app_root(AppDataType::UserConfig, &APP_INFO)
@ -57,19 +57,19 @@ crate fn config(span: impl Into<Span>) -> Result<IndexMap<String, Spanned<Value>
trace!("config file = {}", filename.display());
let contents = fs::read_to_string(filename)
.map(|v| v.spanned(span))
.map(|v| v.simple_spanned(span))
.map_err(|err| ShellError::string(&format!("Couldn't read config file:\n{}", err)))?;
let parsed: toml::Value = toml::from_str(&contents)
.map_err(|err| ShellError::string(&format!("Couldn't parse config file:\n{}", err)))?;
let value = convert_toml_value_to_nu_value(&parsed, span);
let value = convert_toml_value_to_nu_value(&parsed, Tag::unknown_origin(span));
let tag = value.tag();
match value.item {
Value::Object(Dictionary { entries }) => Ok(entries),
other => Err(ShellError::type_error(
"Dictionary",
other.type_name().spanned(value.span),
other.type_name().tagged(tag),
)),
}
}

View file

@ -9,7 +9,7 @@ use std::fmt;
#[derive(Debug, Default, Eq, PartialEq, Serialize, Deserialize, Clone, new)]
pub struct Dictionary {
pub entries: IndexMap<String, Spanned<Value>>,
pub entries: IndexMap<String, Tagged<Value>>,
}
impl PartialOrd for Dictionary {
@ -28,8 +28,8 @@ impl PartialOrd for Dictionary {
}
}
impl From<IndexMap<String, Spanned<Value>>> for Dictionary {
fn from(input: IndexMap<String, Spanned<Value>>) -> Dictionary {
impl From<IndexMap<String, Tagged<Value>>> for Dictionary {
fn from(input: IndexMap<String, Tagged<Value>>) -> Dictionary {
let mut out = IndexMap::default();
for (key, value) in input {
@ -79,7 +79,7 @@ impl Dictionary {
}
}
crate fn get_data_by_key(&self, name: &str) -> Option<&Spanned<Value>> {
crate fn get_data_by_key(&self, name: &str) -> Option<&Tagged<Value>> {
match self
.entries
.iter()
@ -101,72 +101,71 @@ impl Dictionary {
}
}
pub struct SpannedListBuilder {
span: Span,
list: Vec<Spanned<Value>>,
pub struct TaggedListBuilder {
tag: Tag,
list: Vec<Tagged<Value>>,
}
impl SpannedListBuilder {
pub fn new(span: impl Into<Span>) -> SpannedListBuilder {
SpannedListBuilder {
span: span.into(),
impl TaggedListBuilder {
pub fn new(tag: impl Into<Tag>) -> TaggedListBuilder {
TaggedListBuilder {
tag: tag.into(),
list: vec![],
}
}
pub fn push(&mut self, value: impl Into<Value>) {
self.list.push(value.into().spanned(self.span));
self.list.push(value.into().tagged(self.tag));
}
pub fn insert_spanned(&mut self, value: impl Into<Spanned<Value>>) {
pub fn insert_tagged(&mut self, value: impl Into<Tagged<Value>>) {
self.list.push(value.into());
}
pub fn into_spanned_value(self) -> Spanned<Value> {
Value::List(self.list).spanned(self.span)
pub fn into_tagged_value(self) -> Tagged<Value> {
Value::List(self.list).tagged(self.tag)
}
}
impl From<SpannedListBuilder> for Spanned<Value> {
fn from(input: SpannedListBuilder) -> Spanned<Value> {
input.into_spanned_value()
impl From<TaggedListBuilder> for Tagged<Value> {
fn from(input: TaggedListBuilder) -> Tagged<Value> {
input.into_tagged_value()
}
}
#[derive(Debug)]
pub struct SpannedDictBuilder {
span: Span,
dict: IndexMap<String, Spanned<Value>>,
pub struct TaggedDictBuilder {
tag: Tag,
dict: IndexMap<String, Tagged<Value>>,
}
impl SpannedDictBuilder {
pub fn new(span: impl Into<Span>) -> SpannedDictBuilder {
SpannedDictBuilder {
span: span.into(),
impl TaggedDictBuilder {
pub fn new(tag: impl Into<Tag>) -> TaggedDictBuilder {
TaggedDictBuilder {
tag: tag.into(),
dict: IndexMap::default(),
}
}
pub fn insert(&mut self, key: impl Into<String>, value: impl Into<Value>) {
self.dict
.insert(key.into(), value.into().spanned(self.span));
self.dict.insert(key.into(), value.into().tagged(self.tag));
}
pub fn insert_spanned(&mut self, key: impl Into<String>, value: impl Into<Spanned<Value>>) {
pub fn insert_tagged(&mut self, key: impl Into<String>, value: impl Into<Tagged<Value>>) {
self.dict.insert(key.into(), value.into());
}
pub fn into_spanned_value(self) -> Spanned<Value> {
self.into_spanned_dict().map(Value::Object)
pub fn into_tagged_value(self) -> Tagged<Value> {
self.into_tagged_dict().map(Value::Object)
}
pub fn into_spanned_dict(self) -> Spanned<Dictionary> {
Dictionary { entries: self.dict }.spanned(self.span)
pub fn into_tagged_dict(self) -> Tagged<Dictionary> {
Dictionary { entries: self.dict }.tagged(self.tag)
}
}
impl From<SpannedDictBuilder> for Spanned<Value> {
fn from(input: SpannedDictBuilder) -> Spanned<Value> {
input.into_spanned_value()
impl From<TaggedDictBuilder> for Tagged<Value> {
fn from(input: TaggedDictBuilder) -> Tagged<Value> {
input.into_tagged_value()
}
}

View file

@ -1,5 +1,5 @@
use crate::errors::ShellError;
use crate::object::{SpannedDictBuilder, Value};
use crate::object::{TaggedDictBuilder, Value};
use crate::prelude::*;
#[derive(Debug)]
@ -10,15 +10,13 @@ pub enum FileType {
}
crate fn dir_entry_dict(
entry: &std::fs::DirEntry,
span: impl Into<Span>,
) -> Result<Spanned<Value>, ShellError> {
let mut dict = SpannedDictBuilder::new(span);
let filename = entry.file_name();
filename: &std::path::Path,
metadata: &std::fs::Metadata,
tag: impl Into<Tag>,
) -> Result<Tagged<Value>, ShellError> {
let mut dict = TaggedDictBuilder::new(tag);
dict.insert("name", Value::string(filename.to_string_lossy()));
let metadata = entry.metadata()?;
let kind = if metadata.is_dir() {
FileType::Directory
} else if metadata.is_file() {
@ -50,5 +48,5 @@ crate fn dir_entry_dict(
Err(_) => {}
}
Ok(dict.into_spanned_value())
Ok(dict.into_tagged_value())
}

View file

@ -13,11 +13,10 @@ impl From<String> for Value {
}
}
impl<T: Into<Value>> Spanned<T> {
pub fn into_spanned_value(self) -> Spanned<Value> {
let Spanned { item, span } = self;
let value = item.into();
value.spanned(span)
impl<T: Into<Value>> Tagged<T> {
pub fn into_tagged_value(self) -> Tagged<Value> {
let value_span = self.span();
let value = self.item.into();
value.simple_spanned(value_span)
}
}

View file

@ -6,43 +6,44 @@ use serde::Deserialize;
use serde::Serialize;
use uuid::Uuid;
#[derive(
new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash, Getters,
)]
#[get = "crate"]
pub struct Spanned<T> {
pub span: Span,
#[derive(new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
pub struct Tagged<T> {
pub tag: Tag,
pub item: T,
}
impl<T> HasSpan for Spanned<T> {
impl<T> HasSpan for Tagged<T> {
fn span(&self) -> Span {
self.span
self.tag.span
}
}
impl<T> Spanned<T> {
pub fn spanned(self, span: impl Into<Span>) -> Spanned<T> {
Spanned::from_item(self.item, span.into())
pub trait TaggedItem: Sized {
fn tagged(self, tag: impl Into<Tag>) -> Tagged<Self> {
Tagged::from_item(self, tag.into())
}
}
pub trait SpannedItem: Sized {
fn spanned(self, span: impl Into<Span>) -> Spanned<Self> {
Spanned::from_item(self, span.into())
fn simple_spanned(self, span: impl Into<Span>) -> Tagged<Self> {
Tagged::from_simple_spanned_item(self, span.into())
}
// For now, this is a temporary facility. In many cases, there are other useful spans that we
// could be using, such as the original source spans of JSON or Toml files, but we don't yet
// have the infrastructure to make that work.
fn spanned_unknown(self) -> Spanned<Self> {
Spanned::from_item(self, (0, 0))
fn tagged_unknown(self) -> Tagged<Self> {
Tagged::from_item(
self,
Tag {
span: Span::unknown(),
origin: None,
},
)
}
}
impl<T> SpannedItem for T {}
impl<T> TaggedItem for T {}
impl<T> std::ops::Deref for Spanned<T> {
impl<T> std::ops::Deref for Tagged<T> {
type Target = T;
fn deref(&self) -> &T {
@ -50,58 +51,71 @@ impl<T> std::ops::Deref for Spanned<T> {
}
}
impl<T> Spanned<T> {
crate fn from_item(item: T, span: impl Into<Span>) -> Spanned<T> {
Spanned {
span: span.into(),
impl<T> Tagged<T> {
pub fn spanned(self, span: impl Into<Span>) -> Tagged<T> {
Tagged::from_item(
self.item,
Tag {
span: span.into(),
origin: None,
},
)
}
pub fn from_item(item: T, tag: impl Into<Tag>) -> Tagged<T> {
Tagged {
item,
tag: tag.into(),
}
}
pub fn map<U>(self, input: impl FnOnce(T) -> U) -> Spanned<U> {
let Spanned { span, item } = self;
let mapped = input(item);
Spanned { span, item: mapped }
pub fn from_simple_spanned_item(item: T, span: impl Into<Span>) -> Tagged<T> {
Tagged::from_item(
item,
Tag {
span: span.into(),
origin: None,
},
)
}
crate fn copy_span<U>(&self, output: U) -> Spanned<U> {
let Spanned { span, .. } = self;
pub fn map<U>(self, input: impl FnOnce(T) -> U) -> Tagged<U> {
let tag = self.tag();
Spanned {
span: *span,
item: output,
}
let mapped = input(self.item);
Tagged::from_item(mapped, tag.clone())
}
crate fn copy_span<U>(&self, output: U) -> Tagged<U> {
let span = self.span();
Tagged::from_simple_spanned_item(output, span)
}
pub fn source(&self, source: &Text) -> Text {
Text::from(self.span().slice(source))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
pub struct Span {
crate start: usize,
crate end: usize,
pub source: Option<Uuid>,
}
pub fn span(&self) -> Span {
self.tag.span
}
impl From<Option<Span>> for Span {
fn from(input: Option<Span>) -> Span {
match input {
None => Span {
start: 0,
end: 0,
source: None,
},
Some(span) => span,
}
pub fn tag(&self) -> Tag {
self.tag
}
pub fn origin(&self) -> Option<uuid::Uuid> {
self.tag.origin
}
pub fn item(&self) -> &T {
&self.item
}
}
impl<T> From<&Spanned<T>> for Span {
fn from(input: &Spanned<T>) -> Span {
input.span
impl<T> From<&Tagged<T>> for Span {
fn from(input: &Tagged<T>) -> Span {
input.span()
}
}
@ -116,7 +130,6 @@ impl From<nom5_locate::LocatedSpan<&str>> for Span {
Span {
start: input.offset,
end: input.offset + input.fragment.len(),
source: None,
}
}
}
@ -126,7 +139,6 @@ impl<T> From<(nom5_locate::LocatedSpan<T>, nom5_locate::LocatedSpan<T>)> for Spa
Span {
start: input.0.offset,
end: input.1.offset,
source: None,
}
}
}
@ -136,7 +148,6 @@ impl From<(usize, usize)> for Span {
Span {
start: input.0,
end: input.1,
source: None,
}
}
}
@ -146,20 +157,52 @@ impl From<&std::ops::Range<usize>> for Span {
Span {
start: input.start,
end: input.end,
source: None,
}
}
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash, Getters,
)]
pub struct Tag {
pub origin: Option<Uuid>,
pub span: Span,
}
impl Tag {
pub fn unknown_origin(span: Span) -> Tag {
Tag { origin: None, span }
}
pub fn unknown() -> Tag {
Tag {
origin: None,
span: Span::unknown(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
pub struct Span {
crate start: usize,
crate end: usize,
}
impl From<Option<Span>> for Span {
fn from(input: Option<Span>) -> Span {
match input {
None => Span { start: 0, end: 0 },
Some(span) => span,
}
}
}
impl Span {
pub fn unknown() -> Span {
Span {
start: 0,
end: 0,
source: None,
}
Span { start: 0, end: 0 }
}
/*
pub fn unknown_with_uuid(uuid: Uuid) -> Span {
Span {
start: 0,
@ -167,6 +210,7 @@ impl Span {
source: Some(uuid),
}
}
*/
pub fn is_unknown(&self) -> bool {
self.start == 0 && self.end == 0
@ -182,7 +226,6 @@ impl language_reporting::ReportingSpan for Span {
Span {
start,
end: self.end,
source: None,
}
}
@ -190,7 +233,6 @@ impl language_reporting::ReportingSpan for Span {
Span {
start: self.start,
end,
source: None,
}
}

View file

@ -1,11 +1,10 @@
use crate::object::{SpannedDictBuilder, Value};
use crate::object::{TaggedDictBuilder, Value};
use crate::prelude::*;
use itertools::join;
use sysinfo::ProcessExt;
crate fn process_dict(proc: &sysinfo::Process, span: impl Into<Span>) -> Spanned<Value> {
let mut dict = SpannedDictBuilder::new(span);
dict.insert("name", Value::string(proc.name()));
crate fn process_dict(proc: &sysinfo::Process, tag: impl Into<Tag>) -> Tagged<Value> {
let mut dict = TaggedDictBuilder::new(tag);
let cmd = proc.cmd();
@ -15,10 +14,16 @@ crate fn process_dict(proc: &sysinfo::Process, span: impl Into<Span>) -> Spanned
Value::string(join(cmd, ""))
};
dict.insert("cmd", cmd_value);
dict.insert("cpu", Value::float(proc.cpu_usage() as f64));
dict.insert("pid", Value::int(proc.pid() as i64));
dict.insert("status", Value::string(proc.status().to_string()));
dict.insert("cpu", Value::float(proc.cpu_usage() as f64));
//dict.insert("name", Value::string(proc.name()));
match cmd_value {
Value::Primitive(Primitive::Nothing) => {
dict.insert("name", Value::string(proc.name()));
}
_ => dict.insert("name", cmd_value),
}
dict.into_spanned_value()
dict.into_tagged_value()
}

View file

@ -5,15 +5,15 @@ use log::trace;
use std::path::PathBuf;
pub trait ExtractType: Sized {
fn extract(value: &Spanned<Value>) -> Result<Self, ShellError>;
fn check(value: &'value Spanned<Value>) -> Result<&'value Spanned<Value>, ShellError>;
fn extract(value: &Tagged<Value>) -> Result<Self, ShellError>;
fn check(value: &'value Tagged<Value>) -> Result<&'value Tagged<Value>, ShellError>;
fn syntax_type() -> hir::SyntaxType {
hir::SyntaxType::Any
}
}
impl<T> ExtractType for T {
default fn extract(_value: &Spanned<Value>) -> Result<T, ShellError> {
default fn extract(_value: &Tagged<Value>) -> Result<T, ShellError> {
let name = std::intrinsics::type_name::<T>();
Err(ShellError::unimplemented(format!(
"<T> ExtractType for {}",
@ -21,7 +21,7 @@ impl<T> ExtractType for T {
)))
}
default fn check(_value: &'value Spanned<Value>) -> Result<&'value Spanned<Value>, ShellError> {
default fn check(_value: &'value Tagged<Value>) -> Result<&'value Tagged<Value>, ShellError> {
Err(ShellError::unimplemented("ExtractType for T"))
}
@ -30,8 +30,8 @@ impl<T> ExtractType for T {
}
}
impl<T: ExtractType> ExtractType for Vec<Spanned<T>> {
fn extract(value: &Spanned<Value>) -> Result<Self, ShellError> {
impl<T: ExtractType> ExtractType for Vec<Tagged<T>> {
fn extract(value: &Tagged<Value>) -> Result<Self, ShellError> {
let name = std::intrinsics::type_name::<T>();
trace!("<Vec> Extracting {:?} for Vec<{}>", value, name);
@ -40,24 +40,24 @@ impl<T: ExtractType> ExtractType for Vec<Spanned<T>> {
let mut out = vec![];
for item in items {
out.push(T::extract(item)?.spanned(item.span));
out.push(T::extract(item)?.tagged(item.tag()));
}
Ok(out)
}
other => Err(ShellError::type_error(
"Vec",
other.type_name().spanned(value.span),
other.type_name().tagged(value.tag()),
)),
}
}
fn check(value: &'value Spanned<Value>) -> Result<&'value Spanned<Value>, ShellError> {
fn check(value: &'value Tagged<Value>) -> Result<&'value Tagged<Value>, ShellError> {
match value.item() {
Value::List(_) => Ok(value),
other => Err(ShellError::type_error(
"Vec",
other.type_name().spanned(value.span),
other.type_name().tagged(value.tag()),
)),
}
}
@ -68,7 +68,7 @@ impl<T: ExtractType> ExtractType for Vec<Spanned<T>> {
}
impl<T: ExtractType, U: ExtractType> ExtractType for (T, U) {
fn extract(value: &Spanned<Value>) -> Result<(T, U), ShellError> {
fn extract(value: &Tagged<Value>) -> Result<(T, U), ShellError> {
let t_name = std::intrinsics::type_name::<T>();
let u_name = std::intrinsics::type_name::<U>();
@ -84,20 +84,20 @@ impl<T: ExtractType, U: ExtractType> ExtractType for (T, U) {
} else {
Err(ShellError::type_error(
"two-element-tuple",
"not-two".spanned(value.span),
"not-two".tagged(value.tag()),
))
}
}
other => Err(ShellError::type_error(
"two-element-tuple",
other.type_name().spanned(value.span),
other.type_name().tagged(value.tag()),
)),
}
}
}
impl<T: ExtractType> ExtractType for Option<T> {
fn extract(value: &Spanned<Value>) -> Result<Option<T>, ShellError> {
fn extract(value: &Tagged<Value>) -> Result<Option<T>, ShellError> {
let name = std::intrinsics::type_name::<T>();
trace!("<Option> Extracting {:?} for Option<{}>", value, name);
@ -109,7 +109,7 @@ impl<T: ExtractType> ExtractType for Option<T> {
Ok(result)
}
fn check(value: &'value Spanned<Value>) -> Result<&'value Spanned<Value>, ShellError> {
fn check(value: &'value Tagged<Value>) -> Result<&'value Tagged<Value>, ShellError> {
match value.item() {
Value::Primitive(Primitive::Nothing) => Ok(value),
_ => T::check(value),
@ -121,15 +121,15 @@ impl<T: ExtractType> ExtractType for Option<T> {
}
}
impl<T: ExtractType> ExtractType for Spanned<T> {
fn extract(value: &Spanned<Value>) -> Result<Spanned<T>, ShellError> {
impl<T: ExtractType> ExtractType for Tagged<T> {
fn extract(value: &Tagged<Value>) -> Result<Tagged<T>, ShellError> {
let name = std::intrinsics::type_name::<T>();
trace!("<Spanned> Extracting {:?} for Spanned<{}>", value, name);
trace!("<Tagged> Extracting {:?} for Tagged<{}>", value, name);
Ok(T::extract(value)?.spanned(value.span))
Ok(T::extract(value)?.tagged(value.tag()))
}
fn check(value: &'value Spanned<Value>) -> Result<&'value Spanned<Value>, ShellError> {
fn check(value: &'value Tagged<Value>) -> Result<&'value Tagged<Value>, ShellError> {
T::check(value)
}
@ -139,13 +139,13 @@ impl<T: ExtractType> ExtractType for Spanned<T> {
}
impl ExtractType for Value {
fn extract(value: &Spanned<Value>) -> Result<Value, ShellError> {
trace!("<Spanned> Extracting {:?} for Value", value);
fn extract(value: &Tagged<Value>) -> Result<Value, ShellError> {
trace!("<Tagged> Extracting {:?} for Value", value);
Ok(value.item().clone())
}
fn check(value: &'value Spanned<Value>) -> Result<&'value Spanned<Value>, ShellError> {
fn check(value: &'value Tagged<Value>) -> Result<&'value Tagged<Value>, ShellError> {
Ok(value)
}
@ -159,29 +159,29 @@ impl ExtractType for bool {
hir::SyntaxType::Boolean
}
fn extract(value: &'a Spanned<Value>) -> Result<bool, ShellError> {
fn extract(value: &'a Tagged<Value>) -> Result<bool, ShellError> {
trace!("Extracting {:?} for bool", value);
match &value {
Spanned {
Tagged {
item: Value::Primitive(Primitive::Boolean(b)),
..
} => Ok(*b),
Spanned {
Tagged {
item: Value::Primitive(Primitive::Nothing),
..
} => Ok(false),
other => Err(ShellError::type_error("Boolean", other.spanned_type_name())),
other => Err(ShellError::type_error("Boolean", other.tagged_type_name())),
}
}
fn check(value: &'value Spanned<Value>) -> Result<&'value Spanned<Value>, ShellError> {
fn check(value: &'value Tagged<Value>) -> Result<&'value Tagged<Value>, ShellError> {
match &value {
value @ Spanned {
value @ Tagged {
item: Value::Primitive(Primitive::Boolean(_)),
..
} => Ok(value),
other => Err(ShellError::type_error("Boolean", other.spanned_type_name())),
other => Err(ShellError::type_error("Boolean", other.tagged_type_name())),
}
}
}
@ -191,97 +191,97 @@ impl ExtractType for std::path::PathBuf {
hir::SyntaxType::Path
}
fn extract(value: &'a Spanned<Value>) -> Result<std::path::PathBuf, ShellError> {
fn extract(value: &'a Tagged<Value>) -> Result<std::path::PathBuf, ShellError> {
trace!("Extracting {:?} for PathBuf", value);
match &value {
Spanned {
Tagged {
item: Value::Primitive(Primitive::String(p)),
..
} => Ok(PathBuf::from(p)),
other => Err(ShellError::type_error("Path", other.spanned_type_name())),
other => Err(ShellError::type_error("Path", other.tagged_type_name())),
}
}
fn check(value: &'value Spanned<Value>) -> Result<&'value Spanned<Value>, ShellError> {
fn check(value: &'value Tagged<Value>) -> Result<&'value Tagged<Value>, ShellError> {
match &value {
v @ Spanned {
v @ Tagged {
item: Value::Primitive(Primitive::Path(_)),
..
} => Ok(v),
other => Err(ShellError::type_error("Path", other.spanned_type_name())),
other => Err(ShellError::type_error("Path", other.tagged_type_name())),
}
}
}
impl ExtractType for i64 {
fn extract(value: &Spanned<Value>) -> Result<i64, ShellError> {
fn extract(value: &Tagged<Value>) -> Result<i64, ShellError> {
trace!("Extracting {:?} for i64", value);
match value {
&Spanned {
&Tagged {
item: Value::Primitive(Primitive::Int(int)),
..
} => Ok(int),
other => Err(ShellError::type_error("Integer", other.spanned_type_name())),
other => Err(ShellError::type_error("Integer", other.tagged_type_name())),
}
}
fn check(value: &'value Spanned<Value>) -> Result<&'value Spanned<Value>, ShellError> {
fn check(value: &'value Tagged<Value>) -> Result<&'value Tagged<Value>, ShellError> {
match value {
v @ Spanned {
v @ Tagged {
item: Value::Primitive(Primitive::Int(_)),
..
} => Ok(v),
other => Err(ShellError::type_error("Integer", other.spanned_type_name())),
other => Err(ShellError::type_error("Integer", other.tagged_type_name())),
}
}
}
impl ExtractType for String {
fn extract(value: &Spanned<Value>) -> Result<String, ShellError> {
fn extract(value: &Tagged<Value>) -> Result<String, ShellError> {
trace!("Extracting {:?} for String", value);
match value {
Spanned {
Tagged {
item: Value::Primitive(Primitive::String(string)),
..
} => Ok(string.clone()),
other => Err(ShellError::type_error("String", other.spanned_type_name())),
other => Err(ShellError::type_error("String", other.tagged_type_name())),
}
}
fn check(value: &'value Spanned<Value>) -> Result<&'value Spanned<Value>, ShellError> {
fn check(value: &'value Tagged<Value>) -> Result<&'value Tagged<Value>, ShellError> {
match value {
v @ Spanned {
v @ Tagged {
item: Value::Primitive(Primitive::String(_)),
..
} => Ok(v),
other => Err(ShellError::type_error("String", other.spanned_type_name())),
other => Err(ShellError::type_error("String", other.tagged_type_name())),
}
}
}
impl ExtractType for value::Block {
fn check(value: &'value Spanned<Value>) -> Result<&'value Spanned<Value>, ShellError> {
fn check(value: &'value Tagged<Value>) -> Result<&'value Tagged<Value>, ShellError> {
trace!("Extracting {:?} for Block", value);
match value {
v @ Spanned {
v @ Tagged {
item: Value::Block(_),
..
} => Ok(v),
other => Err(ShellError::type_error("Block", other.spanned_type_name())),
other => Err(ShellError::type_error("Block", other.tagged_type_name())),
}
}
fn extract(value: &Spanned<Value>) -> Result<value::Block, ShellError> {
fn extract(value: &Tagged<Value>) -> Result<value::Block, ShellError> {
match value {
Spanned {
Tagged {
item: Value::Block(block),
..
} => Ok(block.clone()),
other => Err(ShellError::type_error("Block", other.spanned_type_name())),
other => Err(ShellError::type_error("Block", other.tagged_type_name())),
}
}
}

View file

@ -14,7 +14,6 @@ crate use parse::flag::Flag;
crate use parse::operator::Operator;
crate use parse::parser::{nom_input, pipeline};
crate use parse::pipeline::{Pipeline, PipelineElement};
pub use parse::span::{Span, Spanned, SpannedItem};
crate use parse::text::Text;
crate use parse::token_tree::{DelimitedNode, Delimiter, PathNode, TokenNode};
crate use parse::tokens::{RawToken, Token};

View file

@ -7,7 +7,7 @@ use serde::{de, forward_to_deserialize_any};
pub struct DeserializerItem<'de> {
key: String,
struct_field: &'de str,
val: Spanned<Value>,
val: Tagged<Value>,
}
pub struct ConfigDeserializer<'de> {
@ -28,10 +28,10 @@ impl ConfigDeserializer<'de> {
}
pub fn push(&mut self, name: &'static str) -> Result<(), ShellError> {
let value: Option<Spanned<Value>> = if name == "rest" {
let value: Option<Tagged<Value>> = if name == "rest" {
let positional = self.args.slice_from(self.position);
self.position += positional.len();
Some(Value::List(positional).spanned_unknown()) // TODO: correct span
Some(Value::List(positional).tagged_unknown()) // TODO: correct span
} else {
if self.args.has(name) {
self.args.get(name).map(|x| x.clone())
@ -47,7 +47,9 @@ impl ConfigDeserializer<'de> {
self.stack.push(DeserializerItem {
key: name.to_string(),
struct_field: name,
val: value.unwrap_or_else(|| Value::nothing().spanned(self.args.call_info.name_span)),
val: value.unwrap_or_else(|| {
Value::nothing().tagged(Tag::unknown_origin(self.args.call_info.name_span))
}),
});
Ok(())

View file

@ -5,7 +5,7 @@ crate mod named;
crate mod path;
use crate::evaluate::Scope;
use crate::parser::{registry, Span, Spanned, Unit};
use crate::parser::{registry, Unit};
use crate::prelude::*;
use derive_new::new;
use getset::Getters;
@ -18,7 +18,7 @@ crate use binary::Binary;
crate use named::NamedArguments;
crate use path::Path;
pub fn path(head: impl Into<Expression>, tail: Vec<Spanned<impl Into<String>>>) -> Path {
pub fn path(head: impl Into<Expression>, tail: Vec<Tagged<impl Into<String>>>) -> Path {
Path::new(
head.into(),
tail.into_iter()
@ -111,44 +111,44 @@ impl RawExpression {
}
}
pub type Expression = Spanned<RawExpression>;
pub type Expression = Tagged<RawExpression>;
impl Expression {
crate fn int(i: impl Into<i64>, span: impl Into<Span>) -> Expression {
Spanned::from_item(RawExpression::Literal(Literal::Integer(i.into())), span)
Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Integer(i.into())), span)
}
crate fn size(i: impl Into<i64>, unit: impl Into<Unit>, span: impl Into<Span>) -> Expression {
Spanned::from_item(
Tagged::from_simple_spanned_item(
RawExpression::Literal(Literal::Size(i.into(), unit.into())),
span,
)
}
crate fn synthetic_string(s: impl Into<String>) -> Expression {
RawExpression::Synthetic(Synthetic::String(s.into())).spanned_unknown()
RawExpression::Synthetic(Synthetic::String(s.into())).tagged_unknown()
}
crate fn string(inner: impl Into<Span>, outer: impl Into<Span>) -> Expression {
Spanned::from_item(
Tagged::from_simple_spanned_item(
RawExpression::Literal(Literal::String(inner.into())),
outer.into(),
)
}
crate fn bare(span: impl Into<Span>) -> Expression {
Spanned::from_item(RawExpression::Literal(Literal::Bare), span.into())
Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Bare), span.into())
}
crate fn variable(inner: impl Into<Span>, outer: impl Into<Span>) -> Expression {
Spanned::from_item(
Tagged::from_simple_spanned_item(
RawExpression::Variable(Variable::Other(inner.into())),
outer.into(),
)
}
crate fn it_variable(inner: impl Into<Span>, outer: impl Into<Span>) -> Expression {
Spanned::from_item(
Tagged::from_simple_spanned_item(
RawExpression::Variable(Variable::It(inner.into())),
outer.into(),
)
@ -158,7 +158,7 @@ impl Expression {
impl ToDebug for Expression {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result {
match self.item() {
RawExpression::Literal(l) => write!(f, "{}", l.spanned(self.span()).debug(source)),
RawExpression::Literal(l) => write!(f, "{:?}", l),
RawExpression::Synthetic(Synthetic::String(s)) => write!(f, "{:?}", s),
RawExpression::Variable(Variable::It(_)) => write!(f, "$it"),
RawExpression::Variable(Variable::Other(s)) => write!(f, "${}", s.slice(source)),
@ -188,8 +188,8 @@ impl ToDebug for Expression {
}
}
impl From<Spanned<Path>> for Expression {
fn from(path: Spanned<Path>) -> Expression {
impl From<Tagged<Path>> for Expression {
fn from(path: Tagged<Path>) -> Expression {
path.map(|p| RawExpression::Path(Box::new(p)))
}
}
@ -202,7 +202,7 @@ pub enum Literal {
Bare,
}
impl ToDebug for Spanned<&Literal> {
impl ToDebug for Tagged<&Literal> {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result {
match self.item() {
Literal::Integer(int) => write!(f, "{}", *int),

View file

@ -3,26 +3,26 @@ use crate::Text;
pub fn baseline_parse_single_token(token: &Token, source: &Text) -> hir::Expression {
match *token.item() {
RawToken::Integer(int) => hir::Expression::int(int, token.span),
RawToken::Size(int, unit) => hir::Expression::size(int, unit, token.span),
RawToken::String(span) => hir::Expression::string(span, token.span),
RawToken::Integer(int) => hir::Expression::int(int, token.span()),
RawToken::Size(int, unit) => hir::Expression::size(int, unit, token.span()),
RawToken::String(span) => hir::Expression::string(span, token.span()),
RawToken::Variable(span) if span.slice(source) == "it" => {
hir::Expression::it_variable(span, token.span)
hir::Expression::it_variable(span, token.span())
}
RawToken::Variable(span) => hir::Expression::variable(span, token.span),
RawToken::Bare => hir::Expression::bare(token.span),
RawToken::Variable(span) => hir::Expression::variable(span, token.span()),
RawToken::Bare => hir::Expression::bare(token.span()),
}
}
pub fn baseline_parse_token_as_string(token: &Token, source: &Text) -> hir::Expression {
match *token.item() {
RawToken::Variable(span) if span.slice(source) == "it" => {
hir::Expression::it_variable(span, token.span)
hir::Expression::it_variable(span, token.span())
}
RawToken::Variable(span) => hir::Expression::variable(span, token.span),
RawToken::Integer(_) => hir::Expression::bare(token.span),
RawToken::Size(_, _) => hir::Expression::bare(token.span),
RawToken::Bare => hir::Expression::bare(token.span),
RawToken::String(span) => hir::Expression::string(span, token.span),
RawToken::Variable(span) => hir::Expression::variable(span, token.span()),
RawToken::Integer(_) => hir::Expression::bare(token.span()),
RawToken::Size(_, _) => hir::Expression::bare(token.span()),
RawToken::Bare => hir::Expression::bare(token.span()),
RawToken::String(span) => hir::Expression::string(span, token.span()),
}
}

View file

@ -3,9 +3,9 @@ use crate::parser::registry::CommandRegistry;
use crate::parser::{
hir,
hir::{baseline_parse_single_token, baseline_parse_token_as_string},
DelimitedNode, Delimiter, PathNode, RawToken, Span, Spanned, TokenNode,
DelimitedNode, Delimiter, PathNode, RawToken, TokenNode,
};
use crate::{SpannedItem, Text};
use crate::{Span, Tag, Tagged, TaggedItem, Text};
use derive_new::new;
use log::trace;
use serde::{Deserialize, Serialize};
@ -62,7 +62,7 @@ pub fn baseline_parse_next_expr(
(SyntaxType::Path, token) => {
return Err(ShellError::type_error(
"Path",
token.type_name().spanned(token.span()),
token.type_name().simple_spanned(token.span()),
))
}
@ -82,10 +82,10 @@ pub fn baseline_parse_next_expr(
let second = match tokens.next() {
None => {
return Err(ShellError::maybe_labeled_error(
return Err(ShellError::labeled_error(
"Expected something after an operator",
"operator",
Some(op.span),
op.span(),
))
}
Some(token) => baseline_parse_semantic_token(token, registry, source)?,
@ -95,25 +95,26 @@ pub fn baseline_parse_next_expr(
match syntax_type {
SyntaxType::Any => {
let span = (first.span.start, second.span.end);
let span = (first.span().start, second.span().end);
let binary = hir::Binary::new(first, op, second);
let binary = hir::RawExpression::Binary(Box::new(binary));
let binary = Spanned::from_item(binary, span);
let binary = Tagged::from_simple_spanned_item(binary, span);
Ok(binary)
}
SyntaxType::Block => {
let span = (first.span.start, second.span.end);
let span = (first.span().start, second.span().end);
let path: Spanned<hir::RawExpression> = match first {
Spanned {
let path: Tagged<hir::RawExpression> = match first {
Tagged {
item: hir::RawExpression::Literal(hir::Literal::Bare),
span,
tag: Tag { span, .. },
} => {
let string = Spanned::from_item(span.slice(source).to_string(), span);
let string =
Tagged::from_simple_spanned_item(span.slice(source).to_string(), span);
let path = hir::Path::new(
Spanned::from_item(
Tagged::from_simple_spanned_item(
// TODO: Deal with synthetic nodes that have no representation at all in source
hir::RawExpression::Variable(hir::Variable::It(Span::from((0, 0)))),
(0, 0),
@ -121,18 +122,16 @@ pub fn baseline_parse_next_expr(
vec![string],
);
let path = hir::RawExpression::Path(Box::new(path));
Spanned {
item: path,
span: first.span,
}
Tagged::from_simple_spanned_item(path, first.span())
}
Spanned {
Tagged {
item: hir::RawExpression::Literal(hir::Literal::String(inner)),
span,
tag: Tag { span, .. },
} => {
let string = Spanned::from_item(inner.slice(source).to_string(), span);
let string =
Tagged::from_simple_spanned_item(inner.slice(source).to_string(), span);
let path = hir::Path::new(
Spanned::from_item(
Tagged::from_simple_spanned_item(
// TODO: Deal with synthetic nodes that have no representation at all in source
hir::RawExpression::Variable(hir::Variable::It(Span::from((0, 0)))),
(0, 0),
@ -140,16 +139,16 @@ pub fn baseline_parse_next_expr(
vec![string],
);
let path = hir::RawExpression::Path(Box::new(path));
Spanned {
item: path,
span: first.span,
}
Tagged::from_simple_spanned_item(path, first.span())
}
Spanned {
Tagged {
item: hir::RawExpression::Variable(..),
..
} => first,
Spanned { span, item } => {
Tagged {
tag: Tag { span, .. },
item,
} => {
return Err(ShellError::labeled_error(
"The first part of an un-braced block must be a column name",
item.type_name(),
@ -160,10 +159,10 @@ pub fn baseline_parse_next_expr(
let binary = hir::Binary::new(path, op, second);
let binary = hir::RawExpression::Binary(Box::new(binary));
let binary = Spanned::from_item(binary, span);
let binary = Tagged::from_simple_spanned_item(binary, span);
let block = hir::RawExpression::Block(vec![binary]);
let block = Spanned::from_item(block, span);
let block = Tagged::from_simple_spanned_item(block, span);
Ok(block)
}
@ -197,7 +196,7 @@ pub fn baseline_parse_semantic_token(
}
pub fn baseline_parse_delimited(
token: &Spanned<DelimitedNode>,
token: &Tagged<DelimitedNode>,
registry: &CommandRegistry,
source: &Text,
) -> Result<hir::Expression, ShellError> {
@ -208,7 +207,7 @@ pub fn baseline_parse_delimited(
baseline_parse_tokens(&mut TokensIterator::new(children), registry, source)?;
let expr = hir::RawExpression::Block(exprs);
Ok(Spanned::from_item(expr, token.span()))
Ok(Tagged::from_simple_spanned_item(expr, token.span()))
}
Delimiter::Paren => unimplemented!(),
Delimiter::Square => {
@ -217,13 +216,13 @@ pub fn baseline_parse_delimited(
baseline_parse_tokens(&mut TokensIterator::new(children), registry, source)?;
let expr = hir::RawExpression::List(exprs);
Ok(expr.spanned(token.span()))
Ok(expr.tagged(Tag::unknown_origin(token.span())))
}
}
}
pub fn baseline_parse_path(
token: &Spanned<PathNode>,
token: &Tagged<PathNode>,
registry: &CommandRegistry,
source: &Text,
) -> Result<hir::Expression, ShellError> {
@ -239,7 +238,7 @@ pub fn baseline_parse_path(
RawToken::Integer(_) | RawToken::Size(..) | RawToken::Variable(_) => {
return Err(ShellError::type_error(
"String",
token.type_name().spanned(part),
token.type_name().simple_spanned(part),
))
}
},
@ -251,10 +250,10 @@ pub fn baseline_parse_path(
}
.to_string();
tail.push(string.spanned(part));
tail.push(string.simple_spanned(part));
}
Ok(hir::path(head, tail).spanned(token).into())
Ok(hir::path(head, tail).simple_spanned(token).into())
}
#[derive(Debug, new)]

View file

@ -1,5 +1,6 @@
use crate::parser::{hir::Expression, Operator, Spanned};
use crate::parser::{hir::Expression, Operator};
use crate::prelude::*;
use crate::Tagged;
use derive_new::new;
use getset::Getters;
use serde::{Deserialize, Serialize};
@ -11,7 +12,7 @@ use std::fmt;
#[get = "crate"]
pub struct Binary {
left: Expression,
op: Spanned<Operator>,
op: Tagged<Operator>,
right: Expression,
}

View file

@ -1,6 +1,7 @@
use crate::parser::hir::Expression;
use crate::parser::{Flag, Span};
use crate::parser::Flag;
use crate::prelude::*;
use crate::Span;
use derive_new::new;
use indexmap::IndexMap;
use log::trace;

View file

@ -1,5 +1,6 @@
use crate::parser::{hir::Expression, Spanned};
use crate::parser::hir::Expression;
use crate::prelude::*;
use crate::Tagged;
use derive_new::new;
use getset::Getters;
use serde::{Deserialize, Serialize};
@ -11,7 +12,7 @@ use std::fmt;
#[get = "crate"]
pub struct Path {
head: Expression,
tail: Vec<Spanned<String>>,
tail: Vec<Tagged<String>>,
}
impl ToDebug for Path {

View file

@ -4,7 +4,6 @@ crate mod flag;
crate mod operator;
crate mod parser;
crate mod pipeline;
crate mod span;
crate mod text;
crate mod token_tree;
crate mod token_tree_builder;

View file

@ -1,4 +1,4 @@
use crate::parser::parse::span::Span;
use crate::Span;
use derive_new::new;
use language_reporting::{FileName, Location};

View file

@ -1,4 +1,4 @@
use crate::parser::Span;
use crate::Span;
use derive_new::new;
use getset::Getters;
use serde::{Deserialize, Serialize};

View file

@ -1,9 +1,10 @@
#![allow(unused)]
use crate::parser::parse::{
call_node::*, flag::*, operator::*, pipeline::*, span::*, token_tree::*, token_tree_builder::*,
call_node::*, flag::*, operator::*, pipeline::*, token_tree::*, token_tree_builder::*,
tokens::*, unit::*,
};
use crate::{Span, Tagged};
use nom;
use nom::branch::*;
use nom::bytes::complete::*;
@ -67,7 +68,7 @@ fn trace_step<'a, T: Debug>(
}
}
pub fn raw_integer(input: NomSpan) -> IResult<NomSpan, Spanned<i64>> {
pub fn raw_integer(input: NomSpan) -> IResult<NomSpan, Tagged<i64>> {
let start = input.offset;
trace_step(input, "raw_integer", move |input| {
let (input, neg) = opt(tag("-"))(input)?;
@ -76,7 +77,7 @@ pub fn raw_integer(input: NomSpan) -> IResult<NomSpan, Spanned<i64>> {
Ok((
input,
Spanned::from_item(int(num.fragment, neg), (start, end)),
Tagged::from_simple_spanned_item(int(num.fragment, neg), (start, end)),
))
})
}
@ -85,7 +86,7 @@ pub fn integer(input: NomSpan) -> IResult<NomSpan, TokenNode> {
trace_step(input, "integer", move |input| {
let (input, int) = raw_integer(input)?;
Ok((input, TokenTreeBuilder::spanned_int(*int, int.span)))
Ok((input, TokenTreeBuilder::spanned_int(*int, int.span())))
})
}
*/
@ -202,7 +203,7 @@ pub fn shorthand(input: NomSpan) -> IResult<NomSpan, TokenNode> {
})
}
pub fn raw_unit(input: NomSpan) -> IResult<NomSpan, Spanned<Unit>> {
pub fn raw_unit(input: NomSpan) -> IResult<NomSpan, Tagged<Unit>> {
trace_step(input, "raw_unit", move |input| {
let start = input.offset;
let (input, unit) = alt((
@ -230,7 +231,7 @@ pub fn raw_unit(input: NomSpan) -> IResult<NomSpan, Spanned<Unit>> {
Ok((
input,
Spanned::from_item(Unit::from(unit.fragment), (start, end)),
Tagged::from_simple_spanned_item(Unit::from(unit.fragment), (start, end)),
))
})
}
@ -408,7 +409,7 @@ pub fn delimited_brace(input: NomSpan) -> IResult<NomSpan, TokenNode> {
})
}
pub fn raw_call(input: NomSpan) -> IResult<NomSpan, Spanned<CallNode>> {
pub fn raw_call(input: NomSpan) -> IResult<NomSpan, Tagged<CallNode>> {
trace_step(input, "raw_call", move |input| {
let left = input.offset;
let (input, items) = token_list(input)?;
@ -484,10 +485,10 @@ pub fn pipeline(input: NomSpan) -> IResult<NomSpan, TokenNode> {
}
fn make_call_list(
head: Option<(Spanned<CallNode>, Option<NomSpan>, Option<NomSpan>)>,
head: Option<(Tagged<CallNode>, Option<NomSpan>, Option<NomSpan>)>,
items: Vec<(
Option<NomSpan>,
Spanned<CallNode>,
Tagged<CallNode>,
Option<NomSpan>,
Option<NomSpan>,
)>,
@ -531,6 +532,8 @@ fn is_start_bare_char(c: char) -> bool {
'_' => true,
'-' => true,
'@' => true,
'*' => true,
'?' => true,
_ => false,
}
}
@ -545,6 +548,8 @@ fn is_bare_char(c: char) -> bool {
'_' => true,
'-' => true,
'@' => true,
'*' => true,
'?' => true,
_ => false,
}
}
@ -701,12 +706,12 @@ mod tests {
fn test_flag() {
// assert_leaf! {
// parsers [ flag ]
// "--hello" -> 0..7 { Flag(Spanned::from_item(FlagKind::Longhand, span(2, 7))) }
// "--hello" -> 0..7 { Flag(Tagged::from_item(FlagKind::Longhand, span(2, 7))) }
// }
// assert_leaf! {
// parsers [ flag ]
// "--hello-world" -> 0..13 { Flag(Spanned::from_item(FlagKind::Longhand, span(2, 13))) }
// "--hello-world" -> 0..13 { Flag(Tagged::from_item(FlagKind::Longhand, span(2, 13))) }
// }
}
@ -714,7 +719,7 @@ mod tests {
fn test_shorthand() {
// assert_leaf! {
// parsers [ shorthand ]
// "-alt" -> 0..4 { Flag(Spanned::from_item(FlagKind::Shorthand, span(1, 4))) }
// "-alt" -> 0..4 { Flag(Tagged::from_item(FlagKind::Shorthand, span(1, 4))) }
// }
}
@ -1024,7 +1029,7 @@ mod tests {
right: usize,
) -> TokenNode {
let node = DelimitedNode::new(delimiter, children);
let spanned = Spanned::from_item(node, (left, right));
let spanned = Tagged::from_simple_spanned_item(node, (left, right));
TokenNode::Delimited(spanned)
}
@ -1033,16 +1038,16 @@ mod tests {
Box::new(head),
tail.into_iter().map(TokenNode::Token).collect(),
);
let spanned = Spanned::from_item(node, (left, right));
let spanned = Tagged::from_simple_spanned_item(node, (left, right));
TokenNode::Path(spanned)
}
fn leaf_token(token: RawToken, left: usize, right: usize) -> TokenNode {
TokenNode::Token(Spanned::from_item(token, (left, right)))
TokenNode::Token(Tagged::from_simple_spanned_item(token, (left, right)))
}
fn token(token: RawToken, left: usize, right: usize) -> TokenNode {
TokenNode::Token(Spanned::from_item(token, (left, right)))
TokenNode::Token(Tagged::from_simple_spanned_item(token, (left, right)))
}
fn build<T>(block: CurriedNode<T>) -> T {

View file

@ -1,4 +1,5 @@
use crate::parser::{CallNode, Span, Spanned};
use crate::parser::CallNode;
use crate::{Span, Tagged};
use derive_new::new;
use getset::Getters;
@ -12,7 +13,7 @@ pub struct Pipeline {
pub struct PipelineElement {
pub pre_ws: Option<Span>,
#[get = "crate"]
call: Spanned<CallNode>,
call: Tagged<CallNode>,
pub post_ws: Option<Span>,
pub post_pipe: Option<Span>,
}

View file

@ -1,6 +1,6 @@
use crate::errors::ShellError;
use crate::parser::parse::{call_node::*, flag::*, operator::*, pipeline::*, span::*, tokens::*};
use crate::Text;
use crate::parser::parse::{call_node::*, flag::*, operator::*, pipeline::*, tokens::*};
use crate::{Span, Tagged, Text};
use derive_new::new;
use enum_utils::FromStr;
use getset::Getters;
@ -10,16 +10,16 @@ use std::fmt;
pub enum TokenNode {
Token(Token),
#[allow(unused)]
Call(Spanned<CallNode>),
Delimited(Spanned<DelimitedNode>),
Pipeline(Spanned<Pipeline>),
Operator(Spanned<Operator>),
Flag(Spanned<Flag>),
Call(Tagged<CallNode>),
Delimited(Tagged<DelimitedNode>),
Pipeline(Tagged<Pipeline>),
Operator(Tagged<Operator>),
Flag(Tagged<Flag>),
Member(Span),
Whitespace(Span),
#[allow(unused)]
Error(Spanned<Box<ShellError>>),
Path(Spanned<PathNode>),
Error(Tagged<Box<ShellError>>),
Path(Tagged<PathNode>),
}
pub struct DebugTokenNode<'a> {
@ -86,16 +86,16 @@ impl From<&TokenNode> for Span {
impl TokenNode {
pub fn span(&self) -> Span {
match self {
TokenNode::Token(t) => t.span,
TokenNode::Call(s) => s.span,
TokenNode::Delimited(s) => s.span,
TokenNode::Pipeline(s) => s.span,
TokenNode::Operator(s) => s.span,
TokenNode::Flag(s) => s.span,
TokenNode::Token(t) => t.span(),
TokenNode::Call(s) => s.span(),
TokenNode::Delimited(s) => s.span(),
TokenNode::Pipeline(s) => s.span(),
TokenNode::Operator(s) => s.span(),
TokenNode::Flag(s) => s.span(),
TokenNode::Member(s) => *s,
TokenNode::Whitespace(s) => *s,
TokenNode::Error(s) => s.span,
TokenNode::Path(s) => s.span,
TokenNode::Error(s) => s.span(),
TokenNode::Path(s) => s.span(),
}
}
@ -129,7 +129,7 @@ impl TokenNode {
pub fn is_bare(&self) -> bool {
match self {
TokenNode::Token(Spanned {
TokenNode::Token(Tagged {
item: RawToken::Bare,
..
}) => true,
@ -137,10 +137,10 @@ impl TokenNode {
}
}
crate fn as_flag(&self, value: &str, source: &Text) -> Option<Spanned<Flag>> {
crate fn as_flag(&self, value: &str, source: &Text) -> Option<Tagged<Flag>> {
match self {
TokenNode::Flag(
flag @ Spanned {
flag @ Tagged {
item: Flag { .. }, ..
},
) if value == flag.name().slice(source) => Some(*flag),
@ -150,7 +150,7 @@ impl TokenNode {
pub fn as_pipeline(&self) -> Result<Pipeline, ShellError> {
match self {
TokenNode::Pipeline(Spanned { item, .. }) => Ok(item.clone()),
TokenNode::Pipeline(Tagged { item, .. }) => Ok(item.clone()),
_ => Err(ShellError::string("unimplemented")),
}
}

View file

@ -4,11 +4,11 @@ use crate::prelude::*;
use crate::parser::parse::flag::{Flag, FlagKind};
use crate::parser::parse::operator::Operator;
use crate::parser::parse::pipeline::{Pipeline, PipelineElement};
use crate::parser::parse::span::{Span, Spanned};
use crate::parser::parse::token_tree::{DelimitedNode, Delimiter, PathNode, TokenNode};
use crate::parser::parse::tokens::{RawToken, Token};
use crate::parser::parse::unit::Unit;
use crate::parser::CallNode;
use crate::Span;
use derive_new::new;
#[derive(new)]
@ -20,7 +20,7 @@ pub struct TokenTreeBuilder {
#[allow(unused)]
pub type CurriedNode<T> = Box<dyn FnOnce(&mut TokenTreeBuilder) -> T + 'static>;
pub type CurriedToken = Box<dyn FnOnce(&mut TokenTreeBuilder) -> TokenNode + 'static>;
pub type CurriedCall = Box<dyn FnOnce(&mut TokenTreeBuilder) -> Spanned<CallNode> + 'static>;
pub type CurriedCall = Box<dyn FnOnce(&mut TokenTreeBuilder) -> Tagged<CallNode> + 'static>;
#[allow(unused)]
impl TokenTreeBuilder {
@ -92,7 +92,7 @@ impl TokenTreeBuilder {
input: (Vec<PipelineElement>, Option<Span>),
span: impl Into<Span>,
) -> TokenNode {
TokenNode::Pipeline(Spanned::from_item(
TokenNode::Pipeline(Tagged::from_simple_spanned_item(
Pipeline::new(input.0, input.1.into()),
span,
))
@ -111,7 +111,7 @@ impl TokenTreeBuilder {
}
pub fn spanned_op(input: impl Into<Operator>, span: impl Into<Span>) -> TokenNode {
TokenNode::Operator(Spanned::from_item(input.into(), span.into()))
TokenNode::Operator(Tagged::from_simple_spanned_item(input.into(), span.into()))
}
pub fn string(input: impl Into<String>) -> CurriedToken {
@ -128,7 +128,7 @@ impl TokenTreeBuilder {
}
pub fn spanned_string(input: impl Into<Span>, span: impl Into<Span>) -> TokenNode {
TokenNode::Token(Spanned::from_item(
TokenNode::Token(Tagged::from_simple_spanned_item(
RawToken::String(input.into()),
span.into(),
))
@ -146,7 +146,10 @@ impl TokenTreeBuilder {
}
pub fn spanned_bare(input: impl Into<Span>) -> TokenNode {
TokenNode::Token(Spanned::from_item(RawToken::Bare, input.into()))
TokenNode::Token(Tagged::from_simple_spanned_item(
RawToken::Bare,
input.into(),
))
}
pub fn int(input: impl Into<i64>) -> CurriedToken {
@ -161,7 +164,10 @@ impl TokenTreeBuilder {
}
pub fn spanned_int(input: impl Into<i64>, span: impl Into<Span>) -> TokenNode {
TokenNode::Token(Token::from_item(RawToken::Integer(input.into()), span))
TokenNode::Token(Token::from_simple_spanned_item(
RawToken::Integer(input.into()),
span,
))
}
pub fn size(int: impl Into<i64>, unit: impl Into<Unit>) -> CurriedToken {
@ -183,7 +189,10 @@ impl TokenTreeBuilder {
) -> TokenNode {
let (int, unit) = (input.0.into(), input.1.into());
TokenNode::Token(Spanned::from_item(RawToken::Size(int, unit), span))
TokenNode::Token(Tagged::from_simple_spanned_item(
RawToken::Size(int, unit),
span,
))
}
pub fn path(head: CurriedToken, tail: Vec<CurriedToken>) -> CurriedToken {
@ -206,7 +215,7 @@ impl TokenTreeBuilder {
}
pub fn spanned_path(input: (TokenNode, Vec<TokenNode>), span: impl Into<Span>) -> TokenNode {
TokenNode::Path(Spanned::from_item(
TokenNode::Path(Tagged::from_simple_spanned_item(
PathNode::new(Box::new(input.0), input.1),
span,
))
@ -224,7 +233,7 @@ impl TokenTreeBuilder {
}
pub fn spanned_var(input: impl Into<Span>, span: impl Into<Span>) -> TokenNode {
TokenNode::Token(Spanned::from_item(
TokenNode::Token(Tagged::from_simple_spanned_item(
RawToken::Variable(input.into()),
span.into(),
))
@ -242,7 +251,7 @@ impl TokenTreeBuilder {
}
pub fn spanned_flag(input: impl Into<Span>, span: impl Into<Span>) -> TokenNode {
TokenNode::Flag(Spanned::from_item(
TokenNode::Flag(Tagged::from_simple_spanned_item(
Flag::new(FlagKind::Longhand, input.into()),
span.into(),
))
@ -260,7 +269,7 @@ impl TokenTreeBuilder {
}
pub fn spanned_shorthand(input: impl Into<Span>, span: impl Into<Span>) -> TokenNode {
TokenNode::Flag(Spanned::from_item(
TokenNode::Flag(Tagged::from_simple_spanned_item(
Flag::new(FlagKind::Shorthand, input.into()),
span.into(),
))
@ -296,7 +305,7 @@ impl TokenTreeBuilder {
})
}
pub fn spanned_call(input: Vec<TokenNode>, span: impl Into<Span>) -> Spanned<CallNode> {
pub fn spanned_call(input: Vec<TokenNode>, span: impl Into<Span>) -> Tagged<CallNode> {
if input.len() == 0 {
panic!("BUG: spanned call (TODO)")
}
@ -306,7 +315,7 @@ impl TokenTreeBuilder {
let head = input.next().unwrap();
let tail = input.collect();
Spanned::from_item(CallNode::new(Box::new(head), tail), span)
Tagged::from_simple_spanned_item(CallNode::new(Box::new(head), tail), span)
}
pub fn parens(input: Vec<CurriedToken>) -> CurriedToken {
@ -324,7 +333,7 @@ impl TokenTreeBuilder {
}
pub fn spanned_parens(input: impl Into<Vec<TokenNode>>, span: impl Into<Span>) -> TokenNode {
TokenNode::Delimited(Spanned::from_item(
TokenNode::Delimited(Tagged::from_simple_spanned_item(
DelimitedNode::new(Delimiter::Paren, input.into()),
span,
))
@ -345,7 +354,7 @@ impl TokenTreeBuilder {
}
pub fn spanned_square(input: impl Into<Vec<TokenNode>>, span: impl Into<Span>) -> TokenNode {
TokenNode::Delimited(Spanned::from_item(
TokenNode::Delimited(Tagged::from_simple_spanned_item(
DelimitedNode::new(Delimiter::Square, input.into()),
span,
))
@ -366,7 +375,7 @@ impl TokenTreeBuilder {
}
pub fn spanned_brace(input: impl Into<Vec<TokenNode>>, span: impl Into<Span>) -> TokenNode {
TokenNode::Delimited(Spanned::from_item(
TokenNode::Delimited(Tagged::from_simple_spanned_item(
DelimitedNode::new(Delimiter::Brace, input.into()),
span,
))

View file

@ -1,6 +1,5 @@
use crate::parser::parse::span::*;
use crate::parser::parse::unit::*;
use crate::Text;
use crate::{Span, Tagged, Text};
use std::fmt;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
@ -24,7 +23,7 @@ impl RawToken {
}
}
pub type Token = Spanned<RawToken>;
pub type Token = Tagged<RawToken>;
impl Token {
pub fn debug(&self, source: &'a Text) -> DebugToken<'a> {

View file

@ -1,20 +1,20 @@
use crate::errors::{ArgumentError, ShellError};
use crate::parser::registry::{Signature, CommandRegistry, NamedType, PositionalType};
use crate::parser::{baseline_parse_tokens, CallNode, Span, Spanned};
use crate::parser::registry::{CommandRegistry, NamedType, PositionalType, Signature};
use crate::parser::{baseline_parse_tokens, CallNode};
use crate::parser::{
hir::{self, NamedArguments},
Flag, RawToken, TokenNode,
};
use crate::Text;
use crate::{Span, Tag, Tagged, Text};
use log::trace;
pub fn parse_command(
config: &Signature,
registry: &CommandRegistry,
call: &Spanned<CallNode>,
call: &Tagged<CallNode>,
source: &Text,
) -> Result<hir::Call, ShellError> {
let Spanned { item: raw_call, .. } = call;
let Tagged { item: raw_call, .. } = call;
trace!("Processing {:?}", config);
@ -31,7 +31,7 @@ pub fn parse_command(
.collect()
});
match parse_command_tail(&config, registry, children, source, call.span)? {
match parse_command_tail(&config, registry, children, source, call.span())? {
None => Ok(hir::Call::new(Box::new(head), None, None)),
Some((positional, named)) => Ok(hir::Call::new(Box::new(head), positional, named)),
}
@ -40,16 +40,16 @@ pub fn parse_command(
fn parse_command_head(head: &TokenNode) -> Result<hir::Expression, ShellError> {
match head {
TokenNode::Token(
spanned @ Spanned {
spanned @ Tagged {
item: RawToken::Bare,
..
},
) => Ok(spanned.map(|_| hir::RawExpression::Literal(hir::Literal::Bare))),
TokenNode::Token(Spanned {
TokenNode::Token(Tagged {
item: RawToken::String(inner_span),
span,
}) => Ok(Spanned::from_item(
tag: Tag { span, origin: None },
}) => Ok(Tagged::from_simple_spanned_item(
hir::RawExpression::Literal(hir::Literal::String(*inner_span)),
*span,
)),
@ -96,7 +96,7 @@ fn parse_command_tail(
return Err(ShellError::argument_error(
config.name.clone(),
ArgumentError::MissingValueForName(name.to_string()),
flag.span,
flag.span(),
));
}
@ -117,7 +117,7 @@ fn parse_command_tail(
return Err(ShellError::argument_error(
config.name.clone(),
ArgumentError::MissingValueForName(name.to_string()),
flag.span,
flag.span(),
));
}
@ -202,7 +202,7 @@ fn extract_mandatory(
tokens: &mut hir::TokensIterator<'a>,
source: &Text,
span: Span,
) -> Result<(usize, Spanned<Flag>), ShellError> {
) -> Result<(usize, Tagged<Flag>), ShellError> {
let flag = tokens.extract(|t| t.as_flag(name, source));
match flag {
@ -223,7 +223,7 @@ fn extract_optional(
name: &str,
tokens: &mut hir::TokensIterator<'a>,
source: &Text,
) -> Result<(Option<(usize, Spanned<Flag>)>), ShellError> {
) -> Result<(Option<(usize, Tagged<Flag>)>), ShellError> {
let flag = tokens.extract(|t| t.as_flag(name, source));
match flag {

View file

@ -1,7 +1,7 @@
// TODO: Temporary redirect
crate use crate::context::CommandRegistry;
use crate::evaluate::{evaluate_baseline_expr, Scope};
use crate::parser::{hir, hir::SyntaxType, parse_command, CallNode, Spanned};
use crate::parser::{hir, hir::SyntaxType, parse_command, CallNode};
use crate::prelude::*;
use derive_new::new;
use indexmap::IndexMap;
@ -136,15 +136,15 @@ impl Signature {
}
}
#[derive(Debug, Default, new, Serialize, Deserialize)]
#[derive(Debug, Default, new, Serialize, Deserialize, Clone)]
pub struct EvaluatedArgs {
pub positional: Option<Vec<Spanned<Value>>>,
pub named: Option<IndexMap<String, Spanned<Value>>>,
pub positional: Option<Vec<Tagged<Value>>>,
pub named: Option<IndexMap<String, Tagged<Value>>>,
}
#[derive(new)]
pub struct DebugEvaluatedPositional<'a> {
positional: &'a Option<Vec<Spanned<Value>>>,
positional: &'a Option<Vec<Tagged<Value>>>,
}
impl fmt::Debug for DebugEvaluatedPositional<'a> {
@ -161,7 +161,7 @@ impl fmt::Debug for DebugEvaluatedPositional<'a> {
#[derive(new)]
pub struct DebugEvaluatedNamed<'a> {
named: &'a Option<IndexMap<String, Spanned<Value>>>,
named: &'a Option<IndexMap<String, Tagged<Value>>>,
}
impl fmt::Debug for DebugEvaluatedNamed<'a> {
@ -199,14 +199,14 @@ impl EvaluatedArgs {
DebugEvaluatedArgs { args: self }
}
pub fn nth(&self, pos: usize) -> Option<&Spanned<Value>> {
pub fn nth(&self, pos: usize) -> Option<&Tagged<Value>> {
match &self.positional {
None => None,
Some(array) => array.iter().nth(pos),
}
}
pub fn expect_nth(&self, pos: usize) -> Result<&Spanned<Value>, ShellError> {
pub fn expect_nth(&self, pos: usize) -> Result<&Tagged<Value>, ShellError> {
match &self.positional {
None => Err(ShellError::unimplemented("Better error: expect_nth")),
Some(array) => match array.iter().nth(pos) {
@ -230,7 +230,7 @@ impl EvaluatedArgs {
}
}
pub fn get(&self, name: &str) -> Option<&Spanned<Value>> {
pub fn get(&self, name: &str) -> Option<&Tagged<Value>> {
match &self.named {
None => None,
Some(named) => named.get(name),
@ -250,11 +250,11 @@ impl EvaluatedArgs {
pub enum PositionalIter<'a> {
Empty,
Array(std::slice::Iter<'a, Spanned<Value>>),
Array(std::slice::Iter<'a, Tagged<Value>>),
}
impl Iterator for PositionalIter<'a> {
type Item = &'a Spanned<Value>;
type Item = &'a Tagged<Value>;
fn next(&mut self) -> Option<Self::Item> {
match self {
@ -267,7 +267,7 @@ impl Iterator for PositionalIter<'a> {
impl Signature {
crate fn parse_args(
&self,
call: &Spanned<CallNode>,
call: &Tagged<CallNode>,
registry: &CommandRegistry,
source: &Text,
) -> Result<hir::Call, ShellError> {
@ -302,7 +302,7 @@ crate fn evaluate_args(
let positional = positional?;
let named: Result<Option<IndexMap<String, Spanned<Value>>>, ShellError> = call
let named: Result<Option<IndexMap<String, Tagged<Value>>>, ShellError> = call
.named()
.as_ref()
.map(|n| {
@ -313,7 +313,7 @@ crate fn evaluate_args(
hir::named::NamedValue::PresentSwitch(span) => {
results.insert(
name.clone(),
Spanned::from_item(Value::boolean(true), *span),
Tagged::from_simple_spanned_item(Value::boolean(true), *span),
);
}
hir::named::NamedValue::Value(expr) => {

View file

@ -1,25 +1,28 @@
use crate::{CallInfo, Signature, ReturnValue, ShellError, Spanned, Value};
use crate::Signature;
use crate::Tagged;
use crate::{CallInfo, ReturnValue, ShellError, Value};
use serde::{Deserialize, Serialize};
use std::io;
pub trait Plugin {
fn config(&mut self) -> Result<Signature, ShellError>;
#[allow(unused)]
fn begin_filter(&mut self, call_info: CallInfo) -> Result<(), ShellError> {
Err(ShellError::string(
"`begin_filter` not implemented in plugin",
))
}
#[allow(unused)]
fn filter(&mut self, input: Spanned<Value>) -> Result<Vec<ReturnValue>, ShellError> {
Err(ShellError::string("`filter` not implemented in plugin"))
}
#[allow(unused)]
fn sink(&mut self, call_info: CallInfo, input: Vec<Spanned<Value>>) {}
fn quit(&mut self) {
return;
#[allow(unused)]
fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
Ok(vec![])
}
#[allow(unused)]
fn filter(&mut self, input: Tagged<Value>) -> Result<Vec<ReturnValue>, ShellError> {
Ok(vec![])
}
#[allow(unused)]
fn end_filter(&mut self) -> Result<Vec<ReturnValue>, ShellError> {
Ok(vec![])
}
#[allow(unused)]
fn sink(&mut self, call_info: CallInfo, input: Vec<Tagged<Value>>) {}
fn quit(&mut self) {}
}
pub fn serve_plugin(plugin: &mut dyn Plugin) {
@ -33,15 +36,15 @@ pub fn serve_plugin(plugin: &mut dyn Plugin) {
send_response(plugin.config());
}
Ok(NuCommand::begin_filter { params }) => {
send_response(
plugin
.begin_filter(params)
.map(|_| Vec::<ReturnValue>::new()),
);
send_response(plugin.begin_filter(params));
}
Ok(NuCommand::filter { params }) => {
send_response(plugin.filter(params));
}
Ok(NuCommand::end_filter) => {
send_response(plugin.end_filter());
}
Ok(NuCommand::sink { params }) => {
plugin.sink(params.0, params.1);
return;
@ -70,15 +73,14 @@ pub fn serve_plugin(plugin: &mut dyn Plugin) {
send_response(plugin.config());
}
Ok(NuCommand::begin_filter { params }) => {
send_response(
plugin
.begin_filter(params)
.map(|_| Vec::<ReturnValue>::new()),
);
send_response(plugin.begin_filter(params));
}
Ok(NuCommand::filter { params }) => {
send_response(plugin.filter(params));
}
Ok(NuCommand::end_filter) => {
send_response(plugin.end_filter());
}
Ok(NuCommand::sink { params }) => {
plugin.sink(params.0, params.1);
break;
@ -138,10 +140,11 @@ pub enum NuCommand {
params: CallInfo,
},
filter {
params: Spanned<Value>,
params: Tagged<Value>,
},
end_filter,
sink {
params: (CallInfo, Vec<Spanned<Value>>),
params: (CallInfo, Vec<Tagged<Value>>),
},
quit,
}

View file

@ -1,7 +1,7 @@
use indexmap::IndexMap;
use nu::{
serve_plugin, CallInfo, Plugin, PositionalType, Primitive, ReturnSuccess, ReturnValue,
ShellError, Signature, Spanned, Value,
ShellError, Signature, Tagged, Value,
};
struct Add {
@ -16,10 +16,11 @@ impl Add {
}
}
fn add(&self, value: Spanned<Value>) -> Result<Spanned<Value>, ShellError> {
fn add(&self, value: Tagged<Value>) -> Result<Tagged<Value>, ShellError> {
let value_tag = value.tag();
match (value.item, self.value.clone()) {
(obj @ Value::Object(_), Some(v)) => match &self.field {
Some(f) => match obj.insert_data_at_path(value.span, &f, v) {
Some(f) => match obj.insert_data_at_path(value_tag, &f, v) {
Some(v) => return Ok(v),
None => {
return Err(ShellError::string(
@ -52,10 +53,10 @@ impl Plugin for Add {
rest_positional: true,
})
}
fn begin_filter(&mut self, call_info: CallInfo) -> Result<(), ShellError> {
fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
if let Some(args) = call_info.args.positional {
match &args[0] {
Spanned {
Tagged {
item: Value::Primitive(Primitive::String(s)),
..
} => {
@ -69,16 +70,16 @@ impl Plugin for Add {
}
}
match &args[1] {
Spanned { item: v, .. } => {
Tagged { item: v, .. } => {
self.value = Some(v.clone());
}
}
}
Ok(())
Ok(vec![])
}
fn filter(&mut self, input: Spanned<Value>) -> Result<Vec<ReturnValue>, ShellError> {
fn filter(&mut self, input: Tagged<Value>) -> Result<Vec<ReturnValue>, ShellError> {
Ok(vec![ReturnSuccess::value(self.add(input)?)])
}
}

View file

@ -2,7 +2,7 @@
use crossterm::{cursor, terminal, Attribute, RawScreen};
use indexmap::IndexMap;
use nu::{
serve_plugin, CallInfo, NamedType, Plugin, ShellError, Signature, SpanSource, Spanned, Value,
serve_plugin, CallInfo, NamedType, Plugin, ShellError, Signature, SpanSource, Tagged, Value,
};
use pretty_hex::*;
@ -27,14 +27,12 @@ impl Plugin for BinaryView {
})
}
fn sink(&mut self, call_info: CallInfo, input: Vec<Spanned<Value>>) {
fn sink(&mut self, call_info: CallInfo, input: Vec<Tagged<Value>>) {
for v in input {
match v {
Spanned {
item: Value::Binary(b),
span,
} => {
let source = span.source.map(|x| call_info.source_map.get(&x)).flatten();
let value_origin = v.origin();
match v.item {
Value::Binary(b) => {
let source = value_origin.map(|x| call_info.source_map.get(&x)).flatten();
let _ = view_binary(&b, source, call_info.args.has("lores"));
}
_ => {}
@ -195,11 +193,11 @@ impl RenderContext {
let cursor = cursor();
cursor.hide()?;
self.width = terminal_size.0 as usize + 1;
self.width = terminal_size.0 as usize;
self.height = if self.lores_mode {
terminal_size.1 as usize
terminal_size.1 as usize - 1
} else {
terminal_size.1 as usize * 2
(terminal_size.1 as usize - 1) * 2
};
}
@ -356,7 +354,7 @@ pub fn view_contents_interactive(
None
};
let mut nes = neso::Nes::new(48000.0);
let mut nes = neso::Nes::new(0.0);
let rawkey = RawKey::new();
nes.load_rom(&buffer);
@ -375,10 +373,10 @@ pub fn view_contents_interactive(
let cursor = cursor();
let buttons = vec![
KeyCode::LShift,
KeyCode::LControl,
KeyCode::Alt,
KeyCode::LeftControl,
KeyCode::Tab,
KeyCode::Back,
KeyCode::BackSpace,
KeyCode::UpArrow,
KeyCode::DownArrow,
KeyCode::LeftArrow,

View file

@ -1,7 +1,7 @@
use indexmap::IndexMap;
use nu::{
serve_plugin, CallInfo, Plugin, PositionalType, Primitive, ReturnSuccess, ReturnValue,
ShellError, Signature, Spanned, Value,
ShellError, Signature, Tagged, Value,
};
struct Edit {
@ -16,10 +16,11 @@ impl Edit {
}
}
fn edit(&self, value: Spanned<Value>) -> Result<Spanned<Value>, ShellError> {
fn edit(&self, value: Tagged<Value>) -> Result<Tagged<Value>, ShellError> {
let value_tag = value.tag();
match (value.item, self.value.clone()) {
(obj @ Value::Object(_), Some(v)) => match &self.field {
Some(f) => match obj.replace_data_at_path(value.span, &f, v) {
Some(f) => match obj.replace_data_at_path(value_tag, &f, v) {
Some(v) => return Ok(v),
None => {
return Err(ShellError::string(
@ -52,10 +53,10 @@ impl Plugin for Edit {
rest_positional: true,
})
}
fn begin_filter(&mut self, call_info: CallInfo) -> Result<(), ShellError> {
fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
if let Some(args) = call_info.args.positional {
match &args[0] {
Spanned {
Tagged {
item: Value::Primitive(Primitive::String(s)),
..
} => {
@ -69,16 +70,16 @@ impl Plugin for Edit {
}
}
match &args[1] {
Spanned { item: v, .. } => {
Tagged { item: v, .. } => {
self.value = Some(v.clone());
}
}
}
Ok(())
Ok(vec![])
}
fn filter(&mut self, input: Spanned<Value>) -> Result<Vec<ReturnValue>, ShellError> {
fn filter(&mut self, input: Tagged<Value>) -> Result<Vec<ReturnValue>, ShellError> {
Ok(vec![ReturnSuccess::value(self.edit(input)?)])
}
}

View file

@ -1,7 +1,7 @@
use indexmap::IndexMap;
use nu::{
serve_plugin, CallInfo, NamedType, Plugin, PositionalType, Primitive, ReturnSuccess,
ReturnValue, ShellError, Signature, Spanned, SpannedItem, Value,
ReturnValue, ShellError, Signature, Tagged, TaggedItem, Value,
};
struct Inc {
@ -22,20 +22,20 @@ impl Inc {
fn inc(
&self,
value: Spanned<Value>,
value: Tagged<Value>,
field: &Option<String>,
) -> Result<Spanned<Value>, ShellError> {
) -> Result<Tagged<Value>, ShellError> {
match value.item {
Value::Primitive(Primitive::Int(i)) => Ok(Value::int(i + 1).spanned(value.span)),
Value::Primitive(Primitive::Int(i)) => Ok(Value::int(i + 1).tagged(value.tag())),
Value::Primitive(Primitive::Bytes(b)) => {
Ok(Value::bytes(b + 1 as u64).spanned(value.span))
Ok(Value::bytes(b + 1 as u64).tagged(value.tag()))
}
Value::Primitive(Primitive::String(s)) => {
Value::Primitive(Primitive::String(ref s)) => {
if let Ok(i) = s.parse::<u64>() {
Ok(Spanned {
item: Value::string(format!("{}", i + 1)),
span: value.span,
})
Ok(Tagged::from_item(
Value::string(format!("{}", i + 1)),
value.tag(),
))
} else if let Ok(mut ver) = semver::Version::parse(&s) {
if self.major {
ver.increment_major();
@ -45,17 +45,17 @@ impl Inc {
self.patch;
ver.increment_patch();
}
Ok(Spanned {
item: Value::string(ver.to_string()),
span: value.span,
})
Ok(Tagged::from_item(
Value::string(ver.to_string()),
value.tag(),
))
} else {
Err(ShellError::string("string could not be incremented"))
}
}
Value::Object(_) => match field {
Some(f) => {
let replacement = match value.item.get_data_by_path(value.span, f) {
let replacement = match value.item.get_data_by_path(value.tag(), f) {
Some(result) => self.inc(result.map(|x| x.clone()), &None)?,
None => {
return Err(ShellError::string("inc could not find field to replace"))
@ -63,7 +63,7 @@ impl Inc {
};
match value
.item
.replace_data_at_path(value.span, f, replacement.item.clone())
.replace_data_at_path(value.tag(), f, replacement.item.clone())
{
Some(v) => return Ok(v),
None => {
@ -98,7 +98,7 @@ impl Plugin for Inc {
rest_positional: true,
})
}
fn begin_filter(&mut self, call_info: CallInfo) -> Result<(), ShellError> {
fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
if call_info.args.has("major") {
self.major = true;
}
@ -112,7 +112,7 @@ impl Plugin for Inc {
if let Some(args) = call_info.args.positional {
for arg in args {
match arg {
Spanned {
Tagged {
item: Value::Primitive(Primitive::String(s)),
..
} => {
@ -128,10 +128,10 @@ impl Plugin for Inc {
}
}
Ok(())
Ok(vec![])
}
fn filter(&mut self, input: Spanned<Value>) -> Result<Vec<ReturnValue>, ShellError> {
fn filter(&mut self, input: Tagged<Value>) -> Result<Vec<ReturnValue>, ShellError> {
Ok(vec![ReturnSuccess::value(self.inc(input, &self.field)?)])
}
}

View file

@ -1,19 +1,19 @@
use indexmap::IndexMap;
use nu::{
serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature,
Spanned, Value,
Tagged, Value,
};
struct NewSkip {
struct Skip {
skip_amount: i64,
}
impl NewSkip {
fn new() -> NewSkip {
NewSkip { skip_amount: 0 }
impl Skip {
fn new() -> Skip {
Skip { skip_amount: 0 }
}
}
impl Plugin for NewSkip {
impl Plugin for Skip {
fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature {
name: "skip".to_string(),
@ -23,11 +23,11 @@ impl Plugin for NewSkip {
rest_positional: true,
})
}
fn begin_filter(&mut self, call_info: CallInfo) -> Result<(), ShellError> {
fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
if let Some(args) = call_info.args.positional {
for arg in args {
match arg {
Spanned {
Tagged {
item: Value::Primitive(Primitive::Int(i)),
..
} => {
@ -37,17 +37,17 @@ impl Plugin for NewSkip {
return Err(ShellError::labeled_error(
"Unrecognized type in params",
"expected an integer",
arg.span,
arg.span(),
))
}
}
}
}
Ok(())
Ok(vec![])
}
fn filter(&mut self, input: Spanned<Value>) -> Result<Vec<ReturnValue>, ShellError> {
fn filter(&mut self, input: Tagged<Value>) -> Result<Vec<ReturnValue>, ShellError> {
if self.skip_amount == 0 {
Ok(vec![ReturnSuccess::value(input)])
} else {
@ -58,5 +58,5 @@ impl Plugin for NewSkip {
}
fn main() {
serve_plugin(&mut NewSkip::new());
serve_plugin(&mut Skip::new());
}

430
src/plugins/str.rs Normal file
View file

@ -0,0 +1,430 @@
use indexmap::IndexMap;
use nu::{
serve_plugin, CallInfo, NamedType, Plugin, PositionalType, Primitive, ReturnSuccess,
ReturnValue, ShellError, Signature, Tagged, Value,
};
enum Action {
Downcase,
Upcase,
ToInteger,
}
struct Str {
field: Option<String>,
error: Option<String>,
action: Option<Action>,
}
impl Str {
fn new() -> Str {
Str {
field: None,
error: None,
action: None,
}
}
fn apply(&self, input: &str) -> Value {
match self.action {
Some(Action::Downcase) => Value::string(input.to_ascii_lowercase()),
Some(Action::Upcase) => Value::string(input.to_ascii_uppercase()),
Some(Action::ToInteger) => match input.trim().parse::<i64>() {
Ok(v) => Value::int(v),
Err(_) => Value::string(input),
},
None => Value::string(input.to_string()),
}
}
fn for_input(&mut self, field: String) {
self.field = Some(field);
}
fn permit(&mut self) -> bool {
self.action.is_none()
}
fn log_error(&mut self, message: &str) {
self.error = Some(message.to_string());
}
fn for_to_int(&mut self) {
if self.permit() {
self.action = Some(Action::ToInteger);
} else {
self.log_error("can only apply one");
}
}
fn for_downcase(&mut self) {
if self.permit() {
self.action = Some(Action::Downcase);
} else {
self.log_error("can only apply one");
}
}
fn for_upcase(&mut self) {
if self.permit() {
self.action = Some(Action::Upcase);
} else {
self.log_error("can only apply one");
}
}
fn usage(&self) -> &'static str {
"Usage: str field [--downcase|--upcase|--to-int]"
}
}
impl Str {
fn strutils(
&self,
value: Tagged<Value>,
field: &Option<String>,
) -> Result<Tagged<Value>, ShellError> {
match value.item {
Value::Primitive(Primitive::String(ref s)) => {
Ok(Tagged::from_item(self.apply(&s), value.tag()))
}
Value::Object(_) => match field {
Some(f) => {
let replacement = match value.item.get_data_by_path(value.tag(), f) {
Some(result) => self.strutils(result.map(|x| x.clone()), &None)?,
None => {
return Err(ShellError::string("str could not find field to replace"))
}
};
match value
.item
.replace_data_at_path(value.tag(), f, replacement.item.clone())
{
Some(v) => return Ok(v),
None => {
return Err(ShellError::string("str could not find field to replace"))
}
}
}
None => Err(ShellError::string(format!(
"{}: {}",
"str needs a field when applying it to a value in an object",
self.usage()
))),
},
x => Err(ShellError::string(format!(
"Unrecognized type in stream: {:?}",
x
))),
}
}
}
impl Plugin for Str {
fn config(&mut self) -> Result<Signature, ShellError> {
let mut named = IndexMap::new();
named.insert("downcase".to_string(), NamedType::Switch);
named.insert("upcase".to_string(), NamedType::Switch);
named.insert("to-int".to_string(), NamedType::Switch);
Ok(Signature {
name: "str".to_string(),
positional: vec![PositionalType::optional_any("Field")],
is_filter: true,
named,
rest_positional: true,
})
}
fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
if call_info.args.has("downcase") {
self.for_downcase();
}
if call_info.args.has("upcase") {
self.for_upcase();
}
if call_info.args.has("to-int") {
self.for_to_int();
}
if let Some(args) = call_info.args.positional {
for arg in args {
match arg {
Tagged {
item: Value::Primitive(Primitive::String(s)),
..
} => {
self.for_input(s);
}
_ => {
return Err(ShellError::string(format!(
"Unrecognized type in params: {:?}",
arg
)))
}
}
}
}
match &self.error {
Some(reason) => {
return Err(ShellError::string(format!("{}: {}", reason, self.usage())))
}
None => Ok(vec![]),
}
}
fn filter(&mut self, input: Tagged<Value>) -> Result<Vec<ReturnValue>, ShellError> {
Ok(vec![ReturnSuccess::value(
self.strutils(input, &self.field)?,
)])
}
}
fn main() {
serve_plugin(&mut Str::new());
}
#[cfg(test)]
mod tests {
use super::Str;
use indexmap::IndexMap;
use nu::{
CallInfo, EvaluatedArgs, Plugin, ReturnSuccess, SourceMap, Span, Tag, Tagged,
TaggedDictBuilder, TaggedItem, Value,
};
struct CallStub {
positionals: Vec<Tagged<Value>>,
flags: IndexMap<String, Tagged<Value>>,
}
impl CallStub {
fn new() -> CallStub {
CallStub {
positionals: vec![],
flags: indexmap::IndexMap::new(),
}
}
fn with_long_flag(&mut self, name: &str) -> &mut Self {
self.flags.insert(
name.to_string(),
Value::boolean(true).simple_spanned(Span::unknown()),
);
self
}
fn with_parameter(&mut self, name: &str) -> &mut Self {
self.positionals
.push(Value::string(name.to_string()).simple_spanned(Span::unknown()));
self
}
fn create(&self, name_span: Span) -> CallInfo {
CallInfo {
args: EvaluatedArgs::new(Some(self.positionals.clone()), Some(self.flags.clone())),
source_map: SourceMap::new(),
name_span,
}
}
}
fn sample_record(key: &str, value: &str) -> Tagged<Value> {
let mut record = TaggedDictBuilder::new(Tag::unknown());
record.insert(key.clone(), Value::string(value));
record.into_tagged_value()
}
#[test]
fn str_plugin_configuration_flags_wired() {
let mut plugin = Str::new();
let configured = plugin.config().unwrap();
for action_flag in &["downcase", "upcase", "to-int"] {
assert!(configured.named.get(*action_flag).is_some());
}
}
#[test]
fn str_plugin_accepts_downcase() {
let mut plugin = Str::new();
assert!(plugin
.begin_filter(
CallStub::new()
.with_long_flag("downcase")
.create(Span::unknown())
)
.is_ok());
assert!(plugin.action.is_some());
}
#[test]
fn str_plugin_accepts_upcase() {
let mut plugin = Str::new();
assert!(plugin
.begin_filter(
CallStub::new()
.with_long_flag("upcase")
.create(Span::unknown())
)
.is_ok());
assert!(plugin.action.is_some());
}
#[test]
fn str_plugin_accepts_to_int() {
let mut plugin = Str::new();
assert!(plugin
.begin_filter(
CallStub::new()
.with_long_flag("to-int")
.create(Span::unknown())
)
.is_ok());
assert!(plugin.action.is_some());
}
#[test]
fn str_plugin_accepts_field() {
let mut plugin = Str::new();
assert!(plugin
.begin_filter(
CallStub::new()
.with_parameter("package.description")
.create(Span::unknown())
)
.is_ok());
assert_eq!(plugin.field, Some("package.description".to_string()));
}
#[test]
fn str_plugin_accepts_only_one_action() {
let mut plugin = Str::new();
assert!(plugin
.begin_filter(
CallStub::new()
.with_long_flag("upcase")
.with_long_flag("downcase")
.with_long_flag("to-int")
.create(Span::unknown()),
)
.is_err());
assert_eq!(plugin.error, Some("can only apply one".to_string()));
}
#[test]
fn str_downcases() {
let mut strutils = Str::new();
strutils.for_downcase();
assert_eq!(strutils.apply("ANDRES"), Value::string("andres"));
}
#[test]
fn str_upcases() {
let mut strutils = Str::new();
strutils.for_upcase();
assert_eq!(strutils.apply("andres"), Value::string("ANDRES"));
}
#[test]
fn str_to_int() {
let mut strutils = Str::new();
strutils.for_to_int();
assert_eq!(strutils.apply("9999"), Value::int(9999 as i64));
}
#[test]
fn str_plugin_applies_upcase() {
let mut plugin = Str::new();
assert!(plugin
.begin_filter(
CallStub::new()
.with_long_flag("upcase")
.with_parameter("name")
.create(Span::unknown())
)
.is_ok());
let subject = sample_record("name", "jotandrehuda");
let output = plugin.filter(subject).unwrap();
match output[0].as_ref().unwrap() {
ReturnSuccess::Value(Tagged {
item: Value::Object(o),
..
}) => assert_eq!(
*o.get_data(&String::from("name")).borrow(),
Value::string(String::from("JOTANDREHUDA"))
),
_ => {}
}
}
#[test]
fn str_plugin_applies_downcase() {
let mut plugin = Str::new();
assert!(plugin
.begin_filter(
CallStub::new()
.with_long_flag("downcase")
.with_parameter("name")
.create(Span::unknown())
)
.is_ok());
let subject = sample_record("name", "JOTANDREHUDA");
let output = plugin.filter(subject).unwrap();
match output[0].as_ref().unwrap() {
ReturnSuccess::Value(Tagged {
item: Value::Object(o),
..
}) => assert_eq!(
*o.get_data(&String::from("name")).borrow(),
Value::string(String::from("jotandrehuda"))
),
_ => {}
}
}
#[test]
fn str_plugin_applies_to_int() {
let mut plugin = Str::new();
assert!(plugin
.begin_filter(
CallStub::new()
.with_long_flag("to-int")
.with_parameter("Nu_birthday")
.create(Span::unknown())
)
.is_ok());
let subject = sample_record("Nu_birthday", "10");
let output = plugin.filter(subject).unwrap();
match output[0].as_ref().unwrap() {
ReturnSuccess::Value(Tagged {
item: Value::Object(o),
..
}) => assert_eq!(
*o.get_data(&String::from("Nu_birthday")).borrow(),
Value::int(10)
),
_ => {}
}
}
}

94
src/plugins/sum.rs Normal file
View file

@ -0,0 +1,94 @@
use indexmap::IndexMap;
use nu::{
serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature,
Tag, Tagged, Value,
};
struct Sum {
total: Option<Tagged<Value>>,
}
impl Sum {
fn new() -> Sum {
Sum { total: None }
}
fn sum(&mut self, value: Tagged<Value>) -> Result<(), ShellError> {
match value.item {
Value::Primitive(Primitive::Int(i)) => {
match self.total {
Some(Tagged {
item: Value::Primitive(Primitive::Int(j)),
tag: Tag { span, .. },
}) => {
//TODO: handle overflow
self.total =
Some(Tagged::from_simple_spanned_item(Value::int(i + j), span));
Ok(())
}
None => {
self.total = Some(value);
Ok(())
}
_ => Err(ShellError::string(format!(
"Could not sum non-integer or unrelated types"
))),
}
}
Value::Primitive(Primitive::Bytes(b)) => {
match self.total {
Some(Tagged {
item: Value::Primitive(Primitive::Bytes(j)),
tag: Tag { span, .. },
}) => {
//TODO: handle overflow
self.total =
Some(Tagged::from_simple_spanned_item(Value::bytes(b + j), span));
Ok(())
}
None => {
self.total = Some(value);
Ok(())
}
_ => Err(ShellError::string(format!(
"Could not sum non-integer or unrelated types"
))),
}
}
x => Err(ShellError::string(format!(
"Unrecognized type in stream: {:?}",
x
))),
}
}
}
impl Plugin for Sum {
fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature {
name: "sum".to_string(),
positional: vec![],
is_filter: true,
named: IndexMap::new(),
rest_positional: true,
})
}
fn begin_filter(&mut self, _: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
Ok(vec![])
}
fn filter(&mut self, input: Tagged<Value>) -> Result<Vec<ReturnValue>, ShellError> {
self.sum(input)?;
Ok(vec![])
}
fn end_filter(&mut self) -> Result<Vec<ReturnValue>, ShellError> {
match self.total {
None => Ok(vec![]),
Some(ref v) => Ok(vec![ReturnSuccess::value(v.clone())]),
}
}
}
fn main() {
serve_plugin(&mut Sum::new());
}

201
src/plugins/sys.rs Normal file
View file

@ -0,0 +1,201 @@
#![feature(async_await)]
use futures::executor::block_on;
use futures::stream::StreamExt;
use heim::{disk, memory};
use indexmap::IndexMap;
use nu::{
serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature,
Tag, Tagged, TaggedDictBuilder, Value, OF64,
};
use std::ffi::OsStr;
struct Sys;
impl Sys {
fn new() -> Sys {
Sys
}
}
//TODO: add more error checking
async fn cpu(tag: Tag) -> Option<Tagged<Value>> {
if let (Ok(num_cpu), Ok(cpu_speed)) = (sys_info::cpu_num(), sys_info::cpu_speed()) {
let mut cpu_idx = TaggedDictBuilder::new(tag);
cpu_idx.insert("cores", Primitive::Int(num_cpu as i64));
cpu_idx.insert("speed", Primitive::Int(cpu_speed as i64));
Some(cpu_idx.into_tagged_value())
} else {
None
}
}
async fn mem(tag: Tag) -> Tagged<Value> {
let mut dict = TaggedDictBuilder::new(tag);
if let Ok(memory) = memory::memory().await {
dict.insert("total", Value::bytes(memory.total().get()));
dict.insert("free", Value::bytes(memory.free().get()));
}
if let Ok(swap) = memory::swap().await {
dict.insert("swap total", Value::bytes(swap.total().get()));
dict.insert("swap free", Value::bytes(swap.free().get()));
}
dict.into_tagged_value()
}
async fn host(tag: Tag) -> Tagged<Value> {
let mut dict = TaggedDictBuilder::new(tag);
// OS
if let Ok(platform) = heim::host::platform().await {
dict.insert("name", Value::string(platform.system()));
dict.insert("release", Value::string(platform.release()));
dict.insert("hostname", Value::string(platform.hostname()));
dict.insert("arch", Value::string(platform.architecture().as_str()));
}
// Uptime
if let Ok(uptime) = heim::host::uptime().await {
let mut uptime_dict = TaggedDictBuilder::new(tag);
let uptime = uptime.get().round() as i64;
let days = uptime / (60 * 60 * 24);
let hours = (uptime - days * 60 * 60 * 24) / (60 * 60);
let minutes = (uptime - days * 60 * 60 * 24 - hours * 60 * 60) / 60;
let seconds = uptime % 60;
uptime_dict.insert("days", Value::int(days));
uptime_dict.insert("hours", Value::int(hours));
uptime_dict.insert("mins", Value::int(minutes));
uptime_dict.insert("secs", Value::int(seconds));
dict.insert_tagged("uptime", uptime_dict.into_tagged_value());
}
// Users
let mut users = heim::host::users();
let mut user_vec = vec![];
while let Some(user) = users.next().await {
if let Ok(user) = user {
user_vec.push(Tagged::from_item(Value::string(user.username()), tag));
}
}
let user_list = Value::List(user_vec);
dict.insert("users", user_list);
dict.into_tagged_value()
}
async fn disks(tag: Tag) -> Value {
let mut output = vec![];
let mut partitions = disk::partitions_physical();
while let Some(part) = partitions.next().await {
if let Ok(part) = part {
let mut dict = TaggedDictBuilder::new(tag);
dict.insert(
"device",
Value::string(
part.device()
.unwrap_or_else(|| OsStr::new("N/A"))
.to_string_lossy(),
),
);
dict.insert("type", Value::string(part.file_system().as_str()));
dict.insert("mount", Value::string(part.mount_point().to_string_lossy()));
if let Ok(usage) = disk::usage(part.mount_point().to_path_buf()).await {
dict.insert("total", Value::bytes(usage.total().get()));
dict.insert("used", Value::bytes(usage.used().get()));
dict.insert("free", Value::bytes(usage.free().get()));
}
output.push(dict.into_tagged_value());
}
}
Value::List(output)
}
async fn temp(tag: Tag) -> Value {
use sysinfo::{ComponentExt, RefreshKind, SystemExt};
let system = sysinfo::System::new_with_specifics(RefreshKind::new().with_system());
let components_list = system.get_components_list();
if components_list.len() > 0 {
let mut v: Vec<Tagged<Value>> = vec![];
for component in components_list {
let mut component_idx = TaggedDictBuilder::new(tag);
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());
}
Value::List(v)
} else {
Value::List(vec![])
}
}
async fn net(tag: Tag) -> Tagged<Value> {
use sysinfo::{NetworkExt, RefreshKind, SystemExt};
let system = sysinfo::System::new_with_specifics(RefreshKind::new().with_network());
let network = system.get_network();
let incoming = network.get_income();
let outgoing = network.get_outcome();
let mut network_idx = TaggedDictBuilder::new(tag);
network_idx.insert("incoming", Value::bytes(incoming));
network_idx.insert("outgoing", Value::bytes(outgoing));
network_idx.into_tagged_value()
}
async fn sysinfo(tag: Tag) -> Vec<Tagged<Value>> {
let mut sysinfo = TaggedDictBuilder::new(tag);
sysinfo.insert_tagged("host", host(tag).await);
if let Some(cpu) = cpu(tag).await {
sysinfo.insert_tagged("cpu", cpu);
}
sysinfo.insert("disks", disks(tag).await);
sysinfo.insert_tagged("mem", mem(tag).await);
sysinfo.insert("temp", temp(tag).await);
sysinfo.insert_tagged("net", net(tag).await);
vec![sysinfo.into_tagged_value()]
}
impl Plugin for Sys {
fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature {
name: "sys".to_string(),
positional: vec![],
is_filter: true,
named: IndexMap::new(),
rest_positional: true,
})
}
fn begin_filter(&mut self, callinfo: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
Ok(block_on(sysinfo(Tag::unknown_origin(callinfo.name_span)))
.into_iter()
.map(|x| ReturnSuccess::value(x))
.collect())
}
fn filter(&mut self, _: Tagged<Value>) -> Result<Vec<ReturnValue>, ShellError> {
Ok(vec![])
}
}
fn main() {
serve_plugin(&mut Sys::new());
}

287
src/plugins/textview.rs Normal file
View file

@ -0,0 +1,287 @@
#![feature(option_flattening)]
use crossterm::{cursor, terminal, RawScreen};
use indexmap::IndexMap;
use nu::{
serve_plugin, CallInfo, Plugin, Primitive, ShellError, Signature, SourceMap, SpanSource,
Tagged, Value,
};
use rawkey::RawKey;
use syntect::easy::HighlightLines;
use syntect::highlighting::{Style, ThemeSet};
use syntect::parsing::SyntaxSet;
use std::io::Write;
use std::path::Path;
use std::{thread, time::Duration};
enum DrawCommand {
DrawString(Style, String),
NextLine,
}
struct TextView;
impl TextView {
fn new() -> TextView {
TextView
}
}
impl Plugin for TextView {
fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature {
name: "textview".to_string(),
positional: vec![],
is_filter: false,
named: IndexMap::new(),
rest_positional: false,
})
}
fn sink(&mut self, call_info: CallInfo, input: Vec<Tagged<Value>>) {
view_text_value(&input[0], &call_info.source_map);
}
}
fn paint_textview(
draw_commands: &Vec<DrawCommand>,
starting_row: usize,
use_color_buffer: bool,
) -> usize {
let terminal = terminal();
let cursor = cursor();
let size = terminal.terminal_size();
// render
let mut pos = 0;
let width = size.0 as usize;
let height = size.1 as usize - 1;
let mut frame_buffer = vec![];
for command in draw_commands {
match command {
DrawCommand::DrawString(style, string) => {
for chr in string.chars() {
frame_buffer.push((
chr,
style.foreground.r,
style.foreground.g,
style.foreground.b,
));
pos += 1;
}
}
DrawCommand::NextLine => {
for _ in 0..(width - pos % width) {
frame_buffer.push((' ', 0, 0, 0));
}
pos += width - pos % width;
}
}
}
let num_frame_buffer_rows = frame_buffer.len() / width;
let buffer_needs_scrolling = num_frame_buffer_rows > height;
// display
let mut ansi_strings = vec![];
let mut normal_chars = vec![];
for c in
&frame_buffer[starting_row * width..std::cmp::min(pos, (starting_row + height) * width)]
{
if use_color_buffer {
ansi_strings.push(ansi_term::Colour::RGB(c.1, c.2, c.3).paint(format!("{}", c.0)));
} else {
normal_chars.push(c.0);
}
}
if buffer_needs_scrolling {
let _ = cursor.goto(0, 0);
}
if use_color_buffer {
print!("{}", ansi_term::ANSIStrings(&ansi_strings));
} else {
let s: String = normal_chars.into_iter().collect();
print!("{}", s);
}
if buffer_needs_scrolling {
let _ = cursor.goto(0, size.1);
print!(
"{}",
ansi_term::Colour::Blue.paint("[ESC to quit, arrow keys to move]")
);
}
let _ = std::io::stdout().flush();
num_frame_buffer_rows
}
fn scroll_view_lines_if_needed(draw_commands: Vec<DrawCommand>, use_color_buffer: bool) {
let mut starting_row = 0;
let rawkey = RawKey::new();
if let Ok(_raw) = RawScreen::into_raw_mode() {
let cursor = cursor();
let _ = cursor.hide();
let input = crossterm::input();
let _ = input.read_async();
let terminal = terminal();
let mut size = terminal.terminal_size();
let height = size.1 as usize - 1;
let mut max_bottom_line = paint_textview(&draw_commands, starting_row, use_color_buffer);
// Only scroll if needed
if max_bottom_line > height as usize {
loop {
if rawkey.is_pressed(rawkey::KeyCode::Escape) {
break;
}
if rawkey.is_pressed(rawkey::KeyCode::UpArrow) {
if starting_row > 0 {
starting_row -= 1;
max_bottom_line =
paint_textview(&draw_commands, starting_row, use_color_buffer);
}
}
if rawkey.is_pressed(rawkey::KeyCode::DownArrow) {
if starting_row < (max_bottom_line - height) {
starting_row += 1;
}
max_bottom_line =
paint_textview(&draw_commands, starting_row, use_color_buffer);
}
if rawkey.is_pressed(rawkey::KeyCode::PageUp) {
starting_row -= std::cmp::min(height, starting_row);
max_bottom_line =
paint_textview(&draw_commands, starting_row, use_color_buffer);
}
if rawkey.is_pressed(rawkey::KeyCode::PageDown) {
if starting_row < (max_bottom_line - height) {
starting_row += height;
if starting_row > (max_bottom_line - height) {
starting_row = max_bottom_line - height;
}
}
max_bottom_line =
paint_textview(&draw_commands, starting_row, use_color_buffer);
}
thread::sleep(Duration::from_millis(50));
let new_size = terminal.terminal_size();
if size != new_size {
size = new_size;
let _ = terminal.clear(crossterm::ClearType::All);
max_bottom_line =
paint_textview(&draw_commands, starting_row, use_color_buffer);
}
}
}
}
let cursor = cursor();
let _ = cursor.show();
#[allow(unused)]
let screen = RawScreen::disable_raw_mode();
println!("");
}
fn scroll_view(s: &str) {
let mut v = vec![];
for line in s.lines() {
v.push(DrawCommand::DrawString(Style::default(), line.to_string()));
v.push(DrawCommand::NextLine);
}
scroll_view_lines_if_needed(v, false);
}
fn view_text_value(value: &Tagged<Value>, source_map: &SourceMap) {
let value_origin = value.origin();
match value.item {
Value::Primitive(Primitive::String(ref s)) => {
let source = value_origin.map(|x| source_map.get(&x)).flatten();
if let Some(source) = source {
let extension: Option<String> = match source {
SpanSource::File(file) => {
let path = Path::new(file);
path.extension().map(|x| x.to_string_lossy().to_string())
}
SpanSource::Url(url) => {
let url = reqwest::Url::parse(url);
if let Ok(url) = url {
let url = url.clone();
if let Some(mut segments) = url.path_segments() {
if let Some(file) = segments.next_back() {
let path = Path::new(file);
path.extension().map(|x| x.to_string_lossy().to_string())
} else {
None
}
} else {
None
}
} else {
None
}
}
//FIXME: this probably isn't correct
SpanSource::Source(source) => None,
};
match extension {
Some(extension) => {
// 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) {
let ts: ThemeSet = syntect::dumps::from_binary(include_bytes!(
"../../assets/themes.bin"
));
let mut h = HighlightLines::new(syntax, &ts.themes["OneHalfDark"]);
let mut v = vec![];
for line in s.lines() {
let ranges: Vec<(Style, &str)> = h.highlight(line, &ps);
for range in ranges {
v.push(DrawCommand::DrawString(range.0, range.1.to_string()));
}
v.push(DrawCommand::NextLine);
}
scroll_view_lines_if_needed(v, true);
} else {
scroll_view(s);
}
}
_ => {
scroll_view(s);
}
}
} else {
scroll_view(s);
}
}
_ => {}
}
}
fn main() {
serve_plugin(&mut TextView::new());
}

View file

@ -1,6 +1,6 @@
use derive_new::new;
use indexmap::IndexMap;
use nu::{serve_plugin, CallInfo, Plugin, ShellError, Signature, Spanned, Value};
use nu::{serve_plugin, CallInfo, Plugin, ShellError, Signature, Tagged, Value};
use ptree::item::StringItem;
use ptree::output::print_tree_with;
use ptree::print_config::PrintConfig;
@ -90,15 +90,13 @@ impl Plugin for TreeViewer {
})
}
fn sink(&mut self, _call_info: CallInfo, input: Vec<Spanned<Value>>) {
fn sink(&mut self, _call_info: CallInfo, input: Vec<Tagged<Value>>) {
if input.len() > 0 {
for i in input.iter() {
let view = TreeView::from_value(&i);
let _ = view.render_view();
}
}
//Ok(())
}
}

View file

@ -36,17 +36,24 @@ crate use crate::cli::MaybeOwned;
crate use crate::commands::command::{
CommandAction, CommandArgs, ReturnSuccess, ReturnValue, RunnableContext,
};
crate use crate::context::{CommandRegistry, Context};
crate use crate::commands::StaticCommand;
crate use crate::context::CommandRegistry;
crate use crate::context::{Context, SpanSource};
crate use crate::env::host::handle_unexpected;
crate use crate::env::{Environment, Host};
crate use crate::env::Host;
crate use crate::errors::ShellError;
crate use crate::object::base as value;
crate use crate::object::meta::{Tag, Tagged, TaggedItem};
crate use crate::object::types::ExtractType;
crate use crate::object::{Primitive, Value};
crate use crate::parser::hir::SyntaxType;
crate use crate::parser::registry::Signature;
crate use crate::parser::{hir::SyntaxType, Span, Spanned, SpannedItem};
crate use crate::shell::filesystem_shell::FilesystemShell;
crate use crate::shell::shell_manager::ShellManager;
crate use crate::shell::value_shell::ValueShell;
crate use crate::stream::{InputStream, OutputStream};
crate use crate::traits::{HasSpan, ToDebug};
crate use crate::Span;
crate use crate::Text;
crate use futures::stream::BoxStream;
crate use futures::{FutureExt, Stream, StreamExt};
@ -63,7 +70,7 @@ pub trait FromInputStream {
impl<T> FromInputStream for T
where
T: Stream<Item = Spanned<Value>> + Send + 'static,
T: Stream<Item = Tagged<Value>> + Send + 'static,
{
fn from_input_stream(self) -> OutputStream {
OutputStream {

View file

@ -1,4 +1,8 @@
crate mod completer;
crate mod filesystem_shell;
crate mod helper;
crate mod shell;
crate mod shell_manager;
crate mod value_shell;
crate use helper::Helper;

View file

@ -1,5 +1,3 @@
use crate::context::CommandRegistry;
use derive_new::new;
use rustyline::completion::Completer;
use rustyline::completion::{self, FilenameCompleter};
@ -8,19 +6,32 @@ use rustyline::line_buffer::LineBuffer;
#[derive(new)]
crate struct NuCompleter {
pub file_completer: FilenameCompleter,
pub commands: CommandRegistry,
//pub commands: indexmap::IndexMap<String, Arc<dyn Command>>,
}
impl Completer for NuCompleter {
type Candidate = completion::Pair;
pub struct CompletionPair {
pub display: String,
pub replacement: String,
}
fn complete(
impl Into<completion::Pair> for CompletionPair {
fn into(self) -> completion::Pair {
completion::Pair {
display: self.display,
replacement: self.replacement,
}
}
}
impl NuCompleter {
/*
pub fn complete(
&self,
line: &str,
pos: usize,
context: &rustyline::Context,
) -> rustyline::Result<(usize, Vec<completion::Pair>)> {
let commands: Vec<String> = self.commands.names();
) -> rustyline::Result<(usize, Vec<CompletionPair>)> {
//let commands: Vec<String> = self.commands.keys().cloned().collect();
let mut completions = self.file_completer.complete(line, pos, context)?.1;
@ -51,6 +62,7 @@ impl Completer for NuCompleter {
replace_pos -= 1;
}
/*
for command in commands.iter() {
let mut pos = replace_pos;
let mut matched = true;
@ -68,15 +80,17 @@ impl Completer for NuCompleter {
}
if matched {
completions.push(completion::Pair {
completions.push(CompletionPair {
display: command.clone(),
replacement: command.clone(),
});
}
}
*/
Ok((replace_pos, completions))
}
*/
fn update(&self, line: &mut LineBuffer, start: usize, elected: &str) {
let end = line.pos();

View file

@ -0,0 +1,212 @@
use crate::commands::command::CallInfo;
use crate::object::dir_entry_dict;
use crate::prelude::*;
use crate::shell::completer::{CompletionPair, NuCompleter};
use crate::shell::shell::Shell;
use rustyline::completion::{Completer, FilenameCompleter};
use rustyline::error::ReadlineError;
use rustyline::hint::{Hinter, HistoryHinter};
use std::path::{Path, PathBuf};
pub struct FilesystemShell {
crate path: String,
completer: NuCompleter,
hinter: HistoryHinter,
}
impl Clone for FilesystemShell {
fn clone(&self) -> Self {
FilesystemShell {
path: self.path.clone(),
completer: NuCompleter {
file_completer: FilenameCompleter::new(),
},
hinter: HistoryHinter {},
}
}
}
impl FilesystemShell {
pub fn basic() -> Result<FilesystemShell, std::io::Error> {
let path = std::env::current_dir()?;
Ok(FilesystemShell {
path: path.to_string_lossy().to_string(),
completer: NuCompleter {
file_completer: FilenameCompleter::new(),
},
hinter: HistoryHinter {},
})
}
pub fn with_location(path: String) -> Result<FilesystemShell, std::io::Error> {
Ok(FilesystemShell {
path,
completer: NuCompleter {
file_completer: FilenameCompleter::new(),
},
hinter: HistoryHinter {},
})
}
}
impl Shell for FilesystemShell {
fn name(&self) -> String {
"filesystem".to_string()
}
fn ls(&self, call_info: CallInfo, _input: InputStream) -> Result<OutputStream, ShellError> {
let cwd = self.path.clone();
let mut full_path = PathBuf::from(&self.path);
match &call_info.args.nth(0) {
Some(Tagged { item: value, .. }) => full_path.push(Path::new(&value.as_string()?)),
_ => {}
}
let entries = glob::glob(&full_path.to_string_lossy());
if entries.is_err() {
return Err(ShellError::string("Invalid pattern."));
}
let mut shell_entries = VecDeque::new();
let entries: Vec<_> = entries.unwrap().collect();
// If this is a single entry, try to display the contents of the entry if it's a directory
if entries.len() == 1 {
if let Ok(entry) = &entries[0] {
if entry.is_dir() {
let entries = std::fs::read_dir(&full_path);
let entries = match entries {
Err(e) => {
if let Some(s) = call_info.args.nth(0) {
return Err(ShellError::labeled_error(
e.to_string(),
e.to_string(),
s.span(),
));
} else {
return Err(ShellError::labeled_error(
e.to_string(),
e.to_string(),
call_info.name_span,
));
}
}
Ok(o) => o,
};
for entry in entries {
let entry = entry?;
let filepath = entry.path();
let filename = filepath.strip_prefix(&cwd).unwrap();
let value = dir_entry_dict(
filename,
&entry.metadata()?,
Tag::unknown_origin(call_info.name_span),
)?;
shell_entries.push_back(ReturnSuccess::value(value))
}
return Ok(shell_entries.to_output_stream());
}
}
}
// Enumerate the entries from the glob and add each
for entry in entries {
if let Ok(entry) = entry {
let filename = entry.strip_prefix(&cwd).unwrap();
let metadata = std::fs::metadata(&entry)?;
let value = dir_entry_dict(
filename,
&metadata,
Tag::unknown_origin(call_info.name_span),
)?;
shell_entries.push_back(ReturnSuccess::value(value))
}
}
Ok(shell_entries.to_output_stream())
}
fn cd(&self, call_info: CallInfo, _input: InputStream) -> Result<OutputStream, ShellError> {
let path = match call_info.args.nth(0) {
None => match dirs::home_dir() {
Some(o) => o,
_ => {
return Err(ShellError::labeled_error(
"Can not change to home directory",
"can not go to home",
call_info.name_span,
))
}
},
Some(v) => {
let target = v.as_string()?;
let path = PathBuf::from(self.path());
match dunce::canonicalize(path.join(target).as_path()) {
Ok(p) => p,
Err(_) => {
return Err(ShellError::labeled_error(
"Can not change to directory",
"directory not found",
v.span().clone(),
));
}
}
}
};
let mut stream = VecDeque::new();
match std::env::set_current_dir(&path) {
Ok(_) => {}
Err(_) => {
if call_info.args.len() > 0 {
return Err(ShellError::labeled_error(
"Can not change to directory",
"directory not found",
call_info.args.nth(0).unwrap().span().clone(),
));
} else {
return Err(ShellError::string("Can not change to directory"));
}
}
}
stream.push_back(ReturnSuccess::change_cwd(
path.to_string_lossy().to_string(),
));
Ok(stream.into())
}
fn path(&self) -> String {
self.path.clone()
}
fn set_path(&mut self, path: String) {
let pathbuf = PathBuf::from(&path);
let path = match dunce::canonicalize(pathbuf.as_path()) {
Ok(path) => {
let _ = std::env::set_current_dir(&path);
path
}
_ => {
// TODO: handle the case where the path cannot be canonicalized
pathbuf
}
};
self.path = path.to_string_lossy().to_string();
}
/*
fn complete(
&self,
line: &str,
pos: usize,
ctx: &rustyline::Context<'_>,
) -> Result<(usize, Vec<CompletionPair>), ReadlineError> {
self.completer.complete(line, pos, ctx)
}
*/
fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<String> {
self.hinter.hint(line, pos, ctx)
}
}

View file

@ -1,50 +1,60 @@
use crate::parser::nom_input;
use crate::parser::parse::span::Spanned;
use crate::parser::parse::token_tree::TokenNode;
use crate::parser::parse::tokens::RawToken;
use crate::parser::{Pipeline, PipelineElement};
use crate::prelude::*;
use crate::shell::completer::NuCompleter;
use crate::shell::completer::CompletionPair;
use crate::shell::shell_manager::ShellManager;
use crate::Tagged;
use ansi_term::Color;
use rustyline::completion::{self, Completer, FilenameCompleter};
use rustyline::completion::Completer;
use rustyline::error::ReadlineError;
use rustyline::highlight::Highlighter;
use rustyline::hint::{Hinter, HistoryHinter};
use rustyline::hint::Hinter;
use std::borrow::Cow::{self, Owned};
crate struct Helper {
completer: NuCompleter,
hinter: HistoryHinter,
helper: ShellManager,
}
impl Helper {
crate fn new(commands: CommandRegistry) -> Helper {
Helper {
completer: NuCompleter {
file_completer: FilenameCompleter::new(),
commands,
},
hinter: HistoryHinter {},
}
crate fn new(helper: ShellManager) -> Helper {
Helper { helper }
}
}
impl Completer for Helper {
type Candidate = completion::Pair;
type Candidate = rustyline::completion::Pair;
fn complete(
&self,
line: &str,
pos: usize,
ctx: &rustyline::Context<'_>,
) -> Result<(usize, Vec<rustyline::completion::Pair>), ReadlineError> {
//FIXME: Add back completions
Ok((0, vec![]))
}
}
/*
impl Completer for Helper {
type Candidate = rustyline::completion::Pair;
fn complete(
&self,
line: &str,
pos: usize,
ctx: &rustyline::Context<'_>,
) -> Result<(usize, Vec<completion::Pair>), ReadlineError> {
self.completer.complete(line, pos, ctx)
) -> Result<(usize, Vec<rustyline::completion::Pair>), ReadlineError> {
let result = self.helper.complete(line, pos, ctx);
result.map(|(x, y)| (x, y.iter().map(|z| z.into()).collect()))
}
}
*/
impl Hinter for Helper {
fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<String> {
self.hinter.hint(line, pos, ctx)
self.helper.hint(line, pos, ctx)
}
}
@ -107,23 +117,23 @@ fn paint_token_node(token_node: &TokenNode, line: &str) -> String {
TokenNode::Delimited(..) => Color::White.paint(token_node.span().slice(line)),
TokenNode::Operator(..) => Color::White.normal().paint(token_node.span().slice(line)),
TokenNode::Pipeline(..) => Color::Blue.normal().paint(token_node.span().slice(line)),
TokenNode::Token(Spanned {
TokenNode::Token(Tagged {
item: RawToken::Integer(..),
..
}) => Color::Purple.bold().paint(token_node.span().slice(line)),
TokenNode::Token(Spanned {
TokenNode::Token(Tagged {
item: RawToken::Size(..),
..
}) => Color::Purple.bold().paint(token_node.span().slice(line)),
TokenNode::Token(Spanned {
TokenNode::Token(Tagged {
item: RawToken::String(..),
..
}) => Color::Green.normal().paint(token_node.span().slice(line)),
TokenNode::Token(Spanned {
TokenNode::Token(Tagged {
item: RawToken::Variable(..),
..
}) => Color::Yellow.bold().paint(token_node.span().slice(line)),
TokenNode::Token(Spanned {
TokenNode::Token(Tagged {
item: RawToken::Bare,
..
}) => Color::Green.normal().paint(token_node.span().slice(line)),

Some files were not shown because too many files have changed in this diff Show more