mirror of
https://github.com/nushell/nushell.git
synced 2024-11-26 02:13:47 +01:00
Add nuon format for fun (#4401)
* Add nuon format for fun * more fun * More nuon fixes, allow comments, improve errors
This commit is contained in:
parent
2ba12afb01
commit
fd22211737
@ -76,7 +76,7 @@ itertools = "0.10.3"
|
||||
plugin = ["nu-plugin", "nu-parser/plugin", "nu-command/plugin", "nu-protocol/plugin", "nu-engine/plugin"]
|
||||
default = ["plugin", "inc", "example", "which"]
|
||||
stable = ["default"]
|
||||
extra = [ "default", "dataframe", "gstat", "zip-support", "query", ]
|
||||
extra = ["default", "dataframe", "gstat", "zip-support", "query", "trash-support"]
|
||||
wasi = ["inc"]
|
||||
trash-support = ["nu-command/trash-support"]
|
||||
|
||||
|
@ -235,6 +235,7 @@ pub fn create_default_context(cwd: impl AsRef<Path>) -> EngineState {
|
||||
FromIcs,
|
||||
FromIni,
|
||||
FromJson,
|
||||
FromNuon,
|
||||
FromOds,
|
||||
FromSsv,
|
||||
FromToml,
|
||||
@ -250,6 +251,7 @@ pub fn create_default_context(cwd: impl AsRef<Path>) -> EngineState {
|
||||
ToHtml,
|
||||
ToJson,
|
||||
ToMd,
|
||||
ToNuon,
|
||||
ToToml,
|
||||
ToTsv,
|
||||
ToCsv,
|
||||
|
@ -142,7 +142,7 @@ impl Command for Open {
|
||||
Some(converter_id) => engine_state.get_decl(converter_id).run(
|
||||
engine_state,
|
||||
stack,
|
||||
&Call::new(call_span),
|
||||
&Call::new(arg_span),
|
||||
output,
|
||||
),
|
||||
None => Ok(output),
|
||||
|
@ -26,7 +26,7 @@ impl Command for FromJson {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
example: "'{ a:1 }' | from json",
|
||||
example: r#"'{ "a": 1 }' | from json"#,
|
||||
description: "Converts json formatted string to table",
|
||||
result: Some(Value::Record {
|
||||
cols: vec!["a".to_string()],
|
||||
@ -38,7 +38,7 @@ impl Command for FromJson {
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
example: "'{ a:1, b: [1, 2] }' | from json",
|
||||
example: r#"'{ "a": 1, "b": [1, 2] }' | from json"#,
|
||||
description: "Converts json formatted string to table",
|
||||
result: Some(Value::Record {
|
||||
cols: vec!["a".to_string(), "b".to_string()],
|
||||
|
@ -5,6 +5,7 @@ mod eml;
|
||||
mod ics;
|
||||
mod ini;
|
||||
mod json;
|
||||
mod nuon;
|
||||
mod ods;
|
||||
mod ssv;
|
||||
mod toml;
|
||||
@ -23,6 +24,7 @@ pub use eml::FromEml;
|
||||
pub use ics::FromIcs;
|
||||
pub use ini::FromIni;
|
||||
pub use json::FromJson;
|
||||
pub use nuon::FromNuon;
|
||||
pub use ods::FromOds;
|
||||
pub use ssv::FromSsv;
|
||||
pub use tsv::FromTsv;
|
||||
|
187
crates/nu-command/src/formats/from/nuon.rs
Normal file
187
crates/nu-command/src/formats/from/nuon.rs
Normal file
@ -0,0 +1,187 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use nu_engine::{current_dir, eval_expression};
|
||||
use nu_protocol::ast::{Call, Expr, Expression};
|
||||
use nu_protocol::engine::{Command, EngineState, Stack, StateWorkingSet};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||
CONFIG_VARIABLE_ID,
|
||||
};
|
||||
#[derive(Clone)]
|
||||
pub struct FromNuon;
|
||||
|
||||
impl Command for FromNuon {
|
||||
fn name(&self) -> &str {
|
||||
"from nuon"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Convert from nuon to structured data"
|
||||
}
|
||||
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
Signature::build("from nuon").category(Category::Experimental)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
example: "'{ a:1 }' | from nuon",
|
||||
description: "Converts nuon formatted string to table",
|
||||
result: Some(Value::Record {
|
||||
cols: vec!["a".to_string()],
|
||||
vals: vec![Value::Int {
|
||||
val: 1,
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
example: "'{ a:1, b: [1, 2] }' | from nuon",
|
||||
description: "Converts nuon formatted string to table",
|
||||
result: Some(Value::Record {
|
||||
cols: vec!["a".to_string(), "b".to_string()],
|
||||
vals: vec![
|
||||
Value::Int {
|
||||
val: 1,
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::List {
|
||||
vals: vec![
|
||||
Value::Int {
|
||||
val: 1,
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Int {
|
||||
val: 2,
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let config = stack.get_config().unwrap_or_default();
|
||||
let string_input = input.collect_string("", &config)?;
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
|
||||
{
|
||||
let mut engine_state = EngineState::new();
|
||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||
let mut stack = stack.captures_to_stack(&HashMap::new());
|
||||
|
||||
let _ = working_set.add_file("nuon file".to_string(), string_input.as_bytes());
|
||||
|
||||
let mut error = None;
|
||||
|
||||
let (lexed, err) =
|
||||
nu_parser::lex(string_input.as_bytes(), 0, &[b'\n', b'\r'], &[], true);
|
||||
error = error.or(err);
|
||||
|
||||
let (lite_block, err) = nu_parser::lite_parse(&lexed);
|
||||
error = error.or(err);
|
||||
|
||||
let (block, err) = nu_parser::parse_block(&mut working_set, &lite_block, true);
|
||||
error = error.or(err);
|
||||
|
||||
if block.pipelines.get(1).is_some() {
|
||||
return Err(ShellError::SpannedLabeledError(
|
||||
"error when loading".into(),
|
||||
"excess values when loading".into(),
|
||||
head,
|
||||
));
|
||||
}
|
||||
|
||||
let expr = if let Some(pipeline) = block.pipelines.get(0) {
|
||||
if pipeline.expressions.get(1).is_some() {
|
||||
return Err(ShellError::SpannedLabeledError(
|
||||
"error when loading".into(),
|
||||
"detected a pipeline in nuon file".into(),
|
||||
head,
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(expr) = pipeline.expressions.get(0) {
|
||||
expr.clone()
|
||||
} else {
|
||||
Expression {
|
||||
expr: Expr::Nothing,
|
||||
span: head,
|
||||
custom_completion: None,
|
||||
ty: Type::Nothing,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Expression {
|
||||
expr: Expr::Nothing,
|
||||
span: head,
|
||||
custom_completion: None,
|
||||
ty: Type::Nothing,
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(err) = error {
|
||||
return Err(ShellError::SpannedLabeledError(
|
||||
"error when loading".into(),
|
||||
err.to_string(),
|
||||
head,
|
||||
));
|
||||
}
|
||||
|
||||
let delta = working_set.render();
|
||||
|
||||
engine_state.merge_delta(delta, Some(&mut stack), &cwd)?;
|
||||
|
||||
stack.add_var(
|
||||
CONFIG_VARIABLE_ID,
|
||||
Value::Record {
|
||||
cols: vec![],
|
||||
vals: vec![],
|
||||
span: head,
|
||||
},
|
||||
);
|
||||
|
||||
let result = eval_expression(&engine_state, &mut stack, &expr);
|
||||
|
||||
match result {
|
||||
Ok(result) => Ok(result.into_pipeline_data()),
|
||||
Err(ShellError::ExternalNotSupported(..)) => Err(ShellError::SpannedLabeledError(
|
||||
"error when loading".into(),
|
||||
"running commands not supported in nuon".into(),
|
||||
head,
|
||||
)),
|
||||
Err(err) => Err(ShellError::SpannedLabeledError(
|
||||
"error when loading".into(),
|
||||
err.to_string(),
|
||||
head,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(FromNuon {})
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ mod delimited;
|
||||
mod html;
|
||||
mod json;
|
||||
mod md;
|
||||
mod nuon;
|
||||
mod toml;
|
||||
mod tsv;
|
||||
mod url;
|
||||
@ -17,6 +18,7 @@ pub use command::To;
|
||||
pub use html::ToHtml;
|
||||
pub use json::ToJson;
|
||||
pub use md::ToMd;
|
||||
pub use nuon::ToNuon;
|
||||
pub use tsv::ToTsv;
|
||||
pub use xml::ToXml;
|
||||
pub use yaml::ToYaml;
|
||||
|
98
crates/nu-command/src/formats/to/nuon.rs
Normal file
98
crates/nu-command/src/formats/to/nuon.rs
Normal file
@ -0,0 +1,98 @@
|
||||
use nu_protocol::ast::{Call, RangeInclusion};
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ToNuon;
|
||||
|
||||
impl Command for ToNuon {
|
||||
fn name(&self) -> &str {
|
||||
"to nuon"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("to nuon").category(Category::Experimental)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Converts table data into Nuon (Nushell Object Notation) text."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, ShellError> {
|
||||
Ok(Value::String {
|
||||
val: to_nuon(call, input)?,
|
||||
span: call.head,
|
||||
}
|
||||
.into_pipeline_data())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Outputs a nuon string representing the contents of this table",
|
||||
example: "[1 2 3] | to nuon",
|
||||
result: Some(Value::test_string("[1, 2, 3]")),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn value_to_string(v: &Value, span: Span) -> Result<String, ShellError> {
|
||||
match v {
|
||||
Value::Binary { .. } => Err(ShellError::UnsupportedInput("binary".into(), span)),
|
||||
Value::Block { .. } => Err(ShellError::UnsupportedInput("block".into(), span)),
|
||||
Value::Bool { val, .. } => {
|
||||
if *val {
|
||||
Ok("$true".to_string())
|
||||
} else {
|
||||
Ok("$false".to_string())
|
||||
}
|
||||
}
|
||||
Value::CellPath { .. } => Err(ShellError::UnsupportedInput("cellpath".to_string(), span)),
|
||||
Value::CustomValue { .. } => Err(ShellError::UnsupportedInput("custom".to_string(), span)),
|
||||
Value::Date { .. } => Err(ShellError::UnsupportedInput("date".to_string(), span)),
|
||||
Value::Duration { val, .. } => Ok(format!("{}ns", *val)),
|
||||
Value::Error { .. } => Err(ShellError::UnsupportedInput("error".to_string(), span)),
|
||||
Value::Filesize { val, .. } => Ok(format!("{}b", *val)),
|
||||
Value::Float { val, .. } => Ok(format!("{}", *val)),
|
||||
Value::Int { val, .. } => Ok(format!("{}", *val)),
|
||||
Value::List { vals, .. } => {
|
||||
let mut collection = vec![];
|
||||
for val in vals {
|
||||
collection.push(value_to_string(val, span)?);
|
||||
}
|
||||
Ok(format!("[{}]", collection.join(", ")))
|
||||
}
|
||||
Value::Nothing { .. } => Ok("$nothing".to_string()),
|
||||
Value::Range { val, .. } => Ok(format!(
|
||||
"{}..{}{}",
|
||||
value_to_string(&val.from, span)?,
|
||||
if val.inclusion == RangeInclusion::RightExclusive {
|
||||
"<"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
value_to_string(&val.to, span)?
|
||||
)),
|
||||
Value::Record { cols, vals, .. } => {
|
||||
let mut collection = vec![];
|
||||
for (col, val) in cols.iter().zip(vals) {
|
||||
collection.push(format!("\"{}\": {}", col, value_to_string(val, span)?));
|
||||
}
|
||||
Ok(format!("{{{}}}", collection.join(", ")))
|
||||
}
|
||||
Value::String { val, .. } => Ok(format!("\"{}\"", val)),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_nuon(call: &Call, input: PipelineData) -> Result<String, ShellError> {
|
||||
let v = input.into_value(call.head);
|
||||
|
||||
value_to_string(&v, call.head)
|
||||
}
|
@ -74,7 +74,7 @@ pub enum ShellError {
|
||||
#[diagnostic(code(nu::shell::feature_not_enabled), url(docsrs))]
|
||||
FeatureNotEnabled(#[label = "feature not enabled"] Span),
|
||||
|
||||
#[error("External commands not yet supported")]
|
||||
#[error("Running external commands not supported")]
|
||||
#[diagnostic(code(nu::shell::external_commands), url(docsrs))]
|
||||
ExternalNotSupported(#[label = "external not supported"] Span),
|
||||
|
||||
@ -152,7 +152,7 @@ pub enum ShellError {
|
||||
|
||||
#[error("Unsupported input")]
|
||||
#[diagnostic(code(nu::shell::unsupported_input), url(docsrs))]
|
||||
UnsupportedInput(String, #[label("{0}")] Span),
|
||||
UnsupportedInput(String, #[label("{0} not supported")] Span),
|
||||
|
||||
#[error("Network failure")]
|
||||
#[diagnostic(code(nu::shell::network_failure), url(docsrs))]
|
||||
|
@ -137,7 +137,7 @@ let $config = {
|
||||
animate_prompt: $false # redraw the prompt every second
|
||||
float_precision: 2
|
||||
use_ansi_coloring: $true
|
||||
filesize_format: "b" # b, kb, kib, mb, mib, gb, gib, tb, tib, pb, pib, eb, eib, zb, zib, auto
|
||||
filesize_format: "auto" # b, kb, kib, mb, mib, gb, gib, tb, tib, pb, pib, eb, eib, zb, zib, auto
|
||||
edit_mode: emacs # emacs, vi
|
||||
max_history_size: 10000
|
||||
menu_config: {
|
||||
|
Loading…
Reference in New Issue
Block a user