mirror of
https://github.com/nushell/nushell.git
synced 2024-11-28 19:33:47 +01:00
Fix new clippy warnings (#2760)
* Fix new clippy warnings * Fork serde-hjson and bring in * Fork serde-hjson and bring in * Fix clippy lint again
This commit is contained in:
parent
63d4df9810
commit
930f9f0063
1788
Cargo.lock
generated
1788
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -12,6 +12,7 @@ doctest = false
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
nu-data = {version = "0.22.0", path = "../nu-data"}
|
nu-data = {version = "0.22.0", path = "../nu-data"}
|
||||||
nu-errors = {version = "0.22.0", path = "../nu-errors"}
|
nu-errors = {version = "0.22.0", path = "../nu-errors"}
|
||||||
|
nu-json = {version = "0.22.0", path = "../nu-json"}
|
||||||
nu-parser = {version = "0.22.0", path = "../nu-parser"}
|
nu-parser = {version = "0.22.0", path = "../nu-parser"}
|
||||||
nu-plugin = {version = "0.22.0", path = "../nu-plugin"}
|
nu-plugin = {version = "0.22.0", path = "../nu-plugin"}
|
||||||
nu-protocol = {version = "0.22.0", path = "../nu-protocol"}
|
nu-protocol = {version = "0.22.0", path = "../nu-protocol"}
|
||||||
@ -70,7 +71,6 @@ roxmltree = "0.13.0"
|
|||||||
rust-embed = "5.6.0"
|
rust-embed = "5.6.0"
|
||||||
rustyline = {version = "6.3.0", optional = true}
|
rustyline = {version = "6.3.0", optional = true}
|
||||||
serde = {version = "1.0.115", features = ["derive"]}
|
serde = {version = "1.0.115", features = ["derive"]}
|
||||||
serde-hjson = "0.9.1"
|
|
||||||
serde_bytes = "0.11.5"
|
serde_bytes = "0.11.5"
|
||||||
serde_ini = "0.2.0"
|
serde_ini = "0.2.0"
|
||||||
serde_json = "1.0.57"
|
serde_json = "1.0.57"
|
||||||
|
@ -845,8 +845,8 @@ fn rustyline_hinter(config: &dyn nu_data::config::Conf) -> Option<rustyline::hin
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn chomp_newline(s: &str) -> &str {
|
fn chomp_newline(s: &str) -> &str {
|
||||||
if s.ends_with('\n') {
|
if let Some(s) = s.strip_suffix('\n') {
|
||||||
&s[..s.len() - 1]
|
s
|
||||||
} else {
|
} else {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
@ -863,8 +863,8 @@ pub enum LineResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn parse_and_eval(line: &str, ctx: &mut EvaluationContext) -> Result<String, ShellError> {
|
pub async fn parse_and_eval(line: &str, ctx: &mut EvaluationContext) -> Result<String, ShellError> {
|
||||||
let line = if line.ends_with('\n') {
|
let line = if let Some(s) = line.strip_suffix('\n') {
|
||||||
&line[..line.len() - 1]
|
s
|
||||||
} else {
|
} else {
|
||||||
line
|
line
|
||||||
};
|
};
|
||||||
|
@ -63,7 +63,7 @@ pub async fn clip(
|
|||||||
let mut first = true;
|
let mut first = true;
|
||||||
for i in values.iter() {
|
for i in values.iter() {
|
||||||
if !first {
|
if !first {
|
||||||
new_copy_data.push_str("\n");
|
new_copy_data.push('\n');
|
||||||
} else {
|
} else {
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
@ -37,26 +37,26 @@ impl WholeStreamCommand for FromJSON {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_json_value_to_nu_value(v: &serde_hjson::Value, tag: impl Into<Tag>) -> Value {
|
fn convert_json_value_to_nu_value(v: &nu_json::Value, tag: impl Into<Tag>) -> Value {
|
||||||
let tag = tag.into();
|
let tag = tag.into();
|
||||||
let span = tag.span;
|
let span = tag.span;
|
||||||
|
|
||||||
match v {
|
match v {
|
||||||
serde_hjson::Value::Null => UntaggedValue::Primitive(Primitive::Nothing).into_value(&tag),
|
nu_json::Value::Null => UntaggedValue::Primitive(Primitive::Nothing).into_value(&tag),
|
||||||
serde_hjson::Value::Bool(b) => UntaggedValue::boolean(*b).into_value(&tag),
|
nu_json::Value::Bool(b) => UntaggedValue::boolean(*b).into_value(&tag),
|
||||||
serde_hjson::Value::F64(n) => UntaggedValue::decimal_from_float(*n, span).into_value(&tag),
|
nu_json::Value::F64(n) => UntaggedValue::decimal_from_float(*n, span).into_value(&tag),
|
||||||
serde_hjson::Value::U64(n) => UntaggedValue::int(*n).into_value(&tag),
|
nu_json::Value::U64(n) => UntaggedValue::int(*n).into_value(&tag),
|
||||||
serde_hjson::Value::I64(n) => UntaggedValue::int(*n).into_value(&tag),
|
nu_json::Value::I64(n) => UntaggedValue::int(*n).into_value(&tag),
|
||||||
serde_hjson::Value::String(s) => {
|
nu_json::Value::String(s) => {
|
||||||
UntaggedValue::Primitive(Primitive::String(String::from(s))).into_value(&tag)
|
UntaggedValue::Primitive(Primitive::String(String::from(s))).into_value(&tag)
|
||||||
}
|
}
|
||||||
serde_hjson::Value::Array(a) => UntaggedValue::Table(
|
nu_json::Value::Array(a) => UntaggedValue::Table(
|
||||||
a.iter()
|
a.iter()
|
||||||
.map(|x| convert_json_value_to_nu_value(x, &tag))
|
.map(|x| convert_json_value_to_nu_value(x, &tag))
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
.into_value(tag),
|
.into_value(tag),
|
||||||
serde_hjson::Value::Object(o) => {
|
nu_json::Value::Object(o) => {
|
||||||
let mut collected = TaggedDictBuilder::new(&tag);
|
let mut collected = TaggedDictBuilder::new(&tag);
|
||||||
for (k, v) in o.iter() {
|
for (k, v) in o.iter() {
|
||||||
collected.insert_value(k.clone(), convert_json_value_to_nu_value(v, &tag));
|
collected.insert_value(k.clone(), convert_json_value_to_nu_value(v, &tag));
|
||||||
@ -67,8 +67,8 @@ fn convert_json_value_to_nu_value(v: &serde_hjson::Value, tag: impl Into<Tag>) -
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_json_string_to_value(s: String, tag: impl Into<Tag>) -> serde_hjson::Result<Value> {
|
pub fn from_json_string_to_value(s: String, tag: impl Into<Tag>) -> nu_json::Result<Value> {
|
||||||
let v: serde_hjson::Value = serde_hjson::from_str(&s)?;
|
let v: nu_json::Value = nu_json::from_str(&s)?;
|
||||||
Ok(convert_json_value_to_nu_value(&v, tag))
|
Ok(convert_json_value_to_nu_value(&v, tag))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ async fn from_json(
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
let mut message = "Could not parse as JSON (".to_string();
|
let mut message = "Could not parse as JSON (".to_string();
|
||||||
message.push_str(&e.to_string());
|
message.push_str(&e.to_string());
|
||||||
message.push_str(")");
|
message.push(')');
|
||||||
|
|
||||||
Some(Err(ShellError::labeled_error_with_secondary(
|
Some(Err(ShellError::labeled_error_with_secondary(
|
||||||
message,
|
message,
|
||||||
@ -125,7 +125,7 @@ async fn from_json(
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
let mut message = "Could not parse as JSON (".to_string();
|
let mut message = "Could not parse as JSON (".to_string();
|
||||||
message.push_str(&e.to_string());
|
message.push_str(&e.to_string());
|
||||||
message.push_str(")");
|
message.push(')');
|
||||||
|
|
||||||
Ok(OutputStream::one(Err(
|
Ok(OutputStream::one(Err(
|
||||||
ShellError::labeled_error_with_secondary(
|
ShellError::labeled_error_with_secondary(
|
||||||
|
@ -68,13 +68,12 @@ fn convert_yaml_value_to_nu_value(
|
|||||||
Ok(match v {
|
Ok(match v {
|
||||||
serde_yaml::Value::Bool(b) => UntaggedValue::boolean(*b).into_value(tag),
|
serde_yaml::Value::Bool(b) => UntaggedValue::boolean(*b).into_value(tag),
|
||||||
serde_yaml::Value::Number(n) if n.is_i64() => {
|
serde_yaml::Value::Number(n) if n.is_i64() => {
|
||||||
UntaggedValue::int(n.as_i64().ok_or_else(|| err_not_compatible_number)?).into_value(tag)
|
UntaggedValue::int(n.as_i64().ok_or(err_not_compatible_number)?).into_value(tag)
|
||||||
|
}
|
||||||
|
serde_yaml::Value::Number(n) if n.is_f64() => {
|
||||||
|
UntaggedValue::decimal_from_float(n.as_f64().ok_or(err_not_compatible_number)?, span)
|
||||||
|
.into_value(tag)
|
||||||
}
|
}
|
||||||
serde_yaml::Value::Number(n) if n.is_f64() => UntaggedValue::decimal_from_float(
|
|
||||||
n.as_f64().ok_or_else(|| err_not_compatible_number)?,
|
|
||||||
span,
|
|
||||||
)
|
|
||||||
.into_value(tag),
|
|
||||||
serde_yaml::Value::String(s) => UntaggedValue::string(s).into_value(tag),
|
serde_yaml::Value::String(s) => UntaggedValue::string(s).into_value(tag),
|
||||||
serde_yaml::Value::Sequence(a) => {
|
serde_yaml::Value::Sequence(a) => {
|
||||||
let result: Result<Vec<Value>, ShellError> = a
|
let result: Result<Vec<Value>, ShellError> = a
|
||||||
|
@ -264,7 +264,7 @@ fn string_from(input: &[Value]) -> String {
|
|||||||
let mut first = true;
|
let mut first = true;
|
||||||
for i in input.iter() {
|
for i in input.iter() {
|
||||||
if !first {
|
if !first {
|
||||||
save_data.push_str("\n");
|
save_data.push('\n');
|
||||||
} else {
|
} else {
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
@ -304,7 +304,7 @@ fn print_seq(
|
|||||||
let before_dec = istr.find('.').unwrap_or(ilen);
|
let before_dec = istr.find('.').unwrap_or(ilen);
|
||||||
if pad && before_dec < padding {
|
if pad && before_dec < padding {
|
||||||
for _ in 0..(padding - before_dec) {
|
for _ in 0..(padding - before_dec) {
|
||||||
ret_str.push_str("0");
|
ret_str.push('0');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret_str.push_str(&istr);
|
ret_str.push_str(&istr);
|
||||||
|
@ -127,22 +127,22 @@ fn get_output_string(
|
|||||||
let mut output_string = String::new();
|
let mut output_string = String::new();
|
||||||
|
|
||||||
if !headers.is_empty() {
|
if !headers.is_empty() {
|
||||||
output_string.push_str("|");
|
output_string.push('|');
|
||||||
|
|
||||||
for i in 0..headers.len() {
|
for i in 0..headers.len() {
|
||||||
if pretty {
|
if pretty {
|
||||||
output_string.push_str(" ");
|
output_string.push(' ');
|
||||||
output_string.push_str(&get_padded_string(
|
output_string.push_str(&get_padded_string(
|
||||||
headers[i].clone(),
|
headers[i].clone(),
|
||||||
column_widths[i],
|
column_widths[i],
|
||||||
' ',
|
' ',
|
||||||
));
|
));
|
||||||
output_string.push_str(" ");
|
output_string.push(' ');
|
||||||
} else {
|
} else {
|
||||||
output_string.push_str(headers[i].as_str());
|
output_string.push_str(headers[i].as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
output_string.push_str("|");
|
output_string.push('|');
|
||||||
}
|
}
|
||||||
|
|
||||||
output_string.push_str("\n|");
|
output_string.push_str("\n|");
|
||||||
@ -150,43 +150,43 @@ fn get_output_string(
|
|||||||
#[allow(clippy::needless_range_loop)]
|
#[allow(clippy::needless_range_loop)]
|
||||||
for i in 0..headers.len() {
|
for i in 0..headers.len() {
|
||||||
if pretty {
|
if pretty {
|
||||||
output_string.push_str(" ");
|
output_string.push(' ');
|
||||||
output_string.push_str(&get_padded_string(
|
output_string.push_str(&get_padded_string(
|
||||||
String::from("-"),
|
String::from("-"),
|
||||||
column_widths[i],
|
column_widths[i],
|
||||||
'-',
|
'-',
|
||||||
));
|
));
|
||||||
output_string.push_str(" ");
|
output_string.push(' ');
|
||||||
} else {
|
} else {
|
||||||
output_string.push_str("-");
|
output_string.push('-');
|
||||||
}
|
}
|
||||||
|
|
||||||
output_string.push_str("|");
|
output_string.push('|');
|
||||||
}
|
}
|
||||||
|
|
||||||
output_string.push_str("\n");
|
output_string.push('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
for row in rows {
|
for row in rows {
|
||||||
if !headers.is_empty() {
|
if !headers.is_empty() {
|
||||||
output_string.push_str("|");
|
output_string.push('|');
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0..row.len() {
|
for i in 0..row.len() {
|
||||||
if pretty {
|
if pretty {
|
||||||
output_string.push_str(" ");
|
output_string.push(' ');
|
||||||
output_string.push_str(&get_padded_string(row[i].clone(), column_widths[i], ' '));
|
output_string.push_str(&get_padded_string(row[i].clone(), column_widths[i], ' '));
|
||||||
output_string.push_str(" ");
|
output_string.push(' ');
|
||||||
} else {
|
} else {
|
||||||
output_string.push_str(row[i].as_str());
|
output_string.push_str(row[i].as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if !headers.is_empty() {
|
if !headers.is_empty() {
|
||||||
output_string.push_str("|");
|
output_string.push('|');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
output_string.push_str("\n");
|
output_string.push('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
output_string
|
output_string
|
||||||
|
@ -157,7 +157,7 @@ async fn process_row(
|
|||||||
None => OutputStream::one(Err(ShellError::labeled_error(
|
None => OutputStream::one(Err(ShellError::labeled_error(
|
||||||
"update could not find place to insert column",
|
"update could not find place to insert column",
|
||||||
"column name",
|
"column name",
|
||||||
field.maybe_span().unwrap_or_else(|| tag.span),
|
field.maybe_span().unwrap_or(tag.span),
|
||||||
))),
|
))),
|
||||||
},
|
},
|
||||||
Value { value: _, ref tag } => {
|
Value { value: _, ref tag } => {
|
||||||
@ -166,7 +166,7 @@ async fn process_row(
|
|||||||
None => OutputStream::one(Err(ShellError::labeled_error(
|
None => OutputStream::one(Err(ShellError::labeled_error(
|
||||||
"update could not find place to insert column",
|
"update could not find place to insert column",
|
||||||
"column name",
|
"column name",
|
||||||
field.maybe_span().unwrap_or_else(|| tag.span),
|
field.maybe_span().unwrap_or(tag.span),
|
||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ pub fn get_documentation(
|
|||||||
let mut long_desc = String::new();
|
let mut long_desc = String::new();
|
||||||
|
|
||||||
long_desc.push_str(&cmd.usage());
|
long_desc.push_str(&cmd.usage());
|
||||||
long_desc.push_str("\n");
|
long_desc.push('\n');
|
||||||
|
|
||||||
let mut subcommands = vec![];
|
let mut subcommands = vec![];
|
||||||
if !config.no_subcommands {
|
if !config.no_subcommands {
|
||||||
@ -144,7 +144,7 @@ pub fn get_documentation(
|
|||||||
|
|
||||||
let mut one_liner = String::new();
|
let mut one_liner = String::new();
|
||||||
one_liner.push_str(&signature.name);
|
one_liner.push_str(&signature.name);
|
||||||
one_liner.push_str(" ");
|
one_liner.push(' ');
|
||||||
|
|
||||||
for positional in &signature.positional {
|
for positional in &signature.positional {
|
||||||
match &positional.0 {
|
match &positional.0 {
|
||||||
@ -175,7 +175,7 @@ pub fn get_documentation(
|
|||||||
long_desc.push_str("\nSubcommands:\n");
|
long_desc.push_str("\nSubcommands:\n");
|
||||||
subcommands.sort();
|
subcommands.sort();
|
||||||
long_desc.push_str(&subcommands.join("\n"));
|
long_desc.push_str(&subcommands.join("\n"));
|
||||||
long_desc.push_str("\n");
|
long_desc.push('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
if !signature.positional.is_empty() || signature.rest_positional.is_some() {
|
if !signature.positional.is_empty() || signature.rest_positional.is_some() {
|
||||||
@ -205,7 +205,7 @@ pub fn get_documentation(
|
|||||||
long_desc.push_str("\nExamples:");
|
long_desc.push_str("\nExamples:");
|
||||||
}
|
}
|
||||||
for example in examples {
|
for example in examples {
|
||||||
long_desc.push_str("\n");
|
long_desc.push('\n');
|
||||||
long_desc.push_str(" ");
|
long_desc.push_str(" ");
|
||||||
long_desc.push_str(example.description);
|
long_desc.push_str(example.description);
|
||||||
|
|
||||||
@ -218,7 +218,7 @@ pub fn get_documentation(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
long_desc.push_str("\n");
|
long_desc.push('\n');
|
||||||
|
|
||||||
long_desc
|
long_desc
|
||||||
}
|
}
|
||||||
|
2
crates/nu-cli/src/env/environment_syncer.rs
vendored
2
crates/nu-cli/src/env/environment_syncer.rs
vendored
@ -50,7 +50,7 @@ impl EnvironmentSyncer {
|
|||||||
|
|
||||||
pub fn did_config_change(&mut self) -> bool {
|
pub fn did_config_change(&mut self) -> bool {
|
||||||
let config = self.config.lock();
|
let config = self.config.lock();
|
||||||
config.is_modified().unwrap_or_else(|_| false)
|
config.is_modified().unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reload(&mut self) {
|
pub fn reload(&mut self) {
|
||||||
|
2
crates/nu-cli/src/env/host.rs
vendored
2
crates/nu-cli/src/env/host.rs
vendored
@ -126,7 +126,7 @@ impl Host for BasicHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn width(&self) -> usize {
|
fn width(&self) -> usize {
|
||||||
let (mut term_width, _) = term_size::dimensions().unwrap_or_else(|| (80, 20));
|
let (mut term_width, _) = term_size::dimensions().unwrap_or((80, 20));
|
||||||
term_width -= 1;
|
term_width -= 1;
|
||||||
term_width
|
term_width
|
||||||
}
|
}
|
||||||
|
@ -191,8 +191,8 @@ pub fn test_anchors(cmd: Command) -> Result<(), ShellError> {
|
|||||||
|
|
||||||
/// Parse and run a nushell pipeline
|
/// Parse and run a nushell pipeline
|
||||||
fn parse_line(line: &str, ctx: &mut EvaluationContext) -> Result<ClassifiedBlock, ShellError> {
|
fn parse_line(line: &str, ctx: &mut EvaluationContext) -> Result<ClassifiedBlock, ShellError> {
|
||||||
let line = if line.ends_with('\n') {
|
let line = if let Some(line) = line.strip_suffix('\n') {
|
||||||
&line[..line.len() - 1]
|
line
|
||||||
} else {
|
} else {
|
||||||
line
|
line
|
||||||
};
|
};
|
||||||
|
@ -156,7 +156,7 @@ pub fn scan(paths: Vec<std::path::PathBuf>) -> Result<Vec<crate::commands::Comma
|
|||||||
|
|
||||||
if is_valid_name && is_executable {
|
if is_valid_name && is_executable {
|
||||||
trace!(target: "nu::load", "plugin infrastructure -> Trying {:?}", path.display());
|
trace!(target: "nu::load", "plugin infrastructure -> Trying {:?}", path.display());
|
||||||
build_plugin_command(&path).unwrap_or_else(|_| None)
|
build_plugin_command(&path).unwrap_or(None)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
//If this fails for you, check for any special unicode characters in your ~ path
|
//If this fails for you, check for any special unicode characters in your ~ path
|
||||||
assert!(actual.out.chars().filter(|c| c.clone() == '/').count() == 2);
|
assert!(actual.out.chars().filter(|c| *c == '/').count() == 2);
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
assert!(actual.out.contains("home"));
|
assert!(actual.out.contains("home"));
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
|
@ -33,29 +33,17 @@ fn figures_out_intelligently_where_to_write_out_with_metadata() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn writes_out_csv() {
|
fn writes_out_csv() {
|
||||||
Playground::setup("save_test_2", |dirs, sandbox| {
|
Playground::setup("save_test_2", |dirs, sandbox| {
|
||||||
sandbox.with_files(vec![FileWithContent(
|
sandbox.with_files(vec![]);
|
||||||
"cargo_sample.json",
|
|
||||||
r#"
|
|
||||||
{
|
|
||||||
"package": {
|
|
||||||
"name": "nu",
|
|
||||||
"version": "0.14",
|
|
||||||
"description": "A new type of shell",
|
|
||||||
"license": "MIT",
|
|
||||||
"edition": "2018"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
)]);
|
|
||||||
|
|
||||||
let expected_file = dirs.test().join("cargo_sample.csv");
|
let expected_file = dirs.test().join("cargo_sample.csv");
|
||||||
|
|
||||||
nu!(
|
nu!(
|
||||||
cwd: dirs.root(),
|
cwd: dirs.root(),
|
||||||
"open save_test_2/cargo_sample.json | get package | save save_test_2/cargo_sample.csv",
|
r#"echo [[name, version, description, license, edition]; [nu, "0.14", "A new type of shell", "MIT", "2018"]] | save save_test_2/cargo_sample.csv"#,
|
||||||
);
|
);
|
||||||
|
|
||||||
let actual = file_contents(expected_file);
|
let actual = file_contents(expected_file);
|
||||||
|
println!("{}", actual);
|
||||||
assert!(actual.contains("nu,0.14,A new type of shell,MIT,2018"));
|
assert!(actual.contains("nu,0.14,A new type of shell,MIT,2018"));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -150,18 +150,24 @@ fn uniq_counting() {
|
|||||||
| from json
|
| from json
|
||||||
| wrap item
|
| wrap item
|
||||||
| uniq --count
|
| uniq --count
|
||||||
|
| where item == A
|
||||||
|
| get count
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
let expected = nu!(
|
assert_eq!(actual.out, "2");
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
cwd: "tests/fixtures/formats", pipeline(
|
cwd: "tests/fixtures/formats", pipeline(
|
||||||
r#"
|
r#"
|
||||||
echo '[{"item": "A", "count": 2}, {"item": "B", "count": 1}]'
|
echo '["A", "B", "A"]'
|
||||||
| from json
|
| from json
|
||||||
|
| wrap item
|
||||||
|
| uniq --count
|
||||||
|
| where item == B
|
||||||
|
| get count
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
print!("{}", actual.out);
|
assert_eq!(actual.out, "1");
|
||||||
print!("{}", expected.out);
|
|
||||||
assert_eq!(actual.out, expected.out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
15
crates/nu-json/Cargo.toml
Normal file
15
crates/nu-json/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["The Nu Project Contributors", "Christian Zangl <laktak@cdak.net>"]
|
||||||
|
description = "Fork of serde-hjson"
|
||||||
|
edition = "2018"
|
||||||
|
license = "MIT"
|
||||||
|
name = "nu-json"
|
||||||
|
version = "0.22.0"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = "^0.8.0"
|
||||||
|
num-traits = "~0.1.32"
|
||||||
|
regex = "^1.0"
|
||||||
|
lazy_static = "1"
|
29
crates/nu-json/LICENSE
Normal file
29
crates/nu-json/LICENSE
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 The Rust Project Developers
|
||||||
|
Copyright (c) 2016 Christian Zangl
|
||||||
|
Copyright (c) 2020 The Nu Project Contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any
|
||||||
|
person obtaining a copy of this software and associated
|
||||||
|
documentation files (the "Software"), to deal in the
|
||||||
|
Software without restriction, including without
|
||||||
|
limitation the rights to use, copy, modify, merge,
|
||||||
|
publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software
|
||||||
|
is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice
|
||||||
|
shall be included in all copies or substantial portions
|
||||||
|
of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||||
|
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||||
|
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||||
|
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
121
crates/nu-json/src/builder.rs
Normal file
121
crates/nu-json/src/builder.rs
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
use serde::ser;
|
||||||
|
|
||||||
|
use crate::value::{self, Map, Value};
|
||||||
|
|
||||||
|
/// This structure provides a simple interface for constructing a JSON array.
|
||||||
|
pub struct ArrayBuilder {
|
||||||
|
array: Vec<Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ArrayBuilder {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArrayBuilder {
|
||||||
|
/// Construct an `ObjectBuilder`.
|
||||||
|
pub fn new() -> ArrayBuilder {
|
||||||
|
ArrayBuilder { array: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the constructed `Value`.
|
||||||
|
pub fn unwrap(self) -> Value {
|
||||||
|
Value::Array(self.array)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert a value into the array.
|
||||||
|
pub fn push<T: ser::Serialize>(mut self, v: T) -> ArrayBuilder {
|
||||||
|
self.array.push(value::to_value(&v));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates and passes an `ArrayBuilder` into a closure, then inserts the resulting array into
|
||||||
|
/// this array.
|
||||||
|
pub fn push_array<F>(mut self, f: F) -> ArrayBuilder
|
||||||
|
where
|
||||||
|
F: FnOnce(ArrayBuilder) -> ArrayBuilder,
|
||||||
|
{
|
||||||
|
let builder = ArrayBuilder::new();
|
||||||
|
self.array.push(f(builder).unwrap());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates and passes an `ArrayBuilder` into a closure, then inserts the resulting object into
|
||||||
|
/// this array.
|
||||||
|
pub fn push_object<F>(mut self, f: F) -> ArrayBuilder
|
||||||
|
where
|
||||||
|
F: FnOnce(ObjectBuilder) -> ObjectBuilder,
|
||||||
|
{
|
||||||
|
let builder = ObjectBuilder::new();
|
||||||
|
self.array.push(f(builder).unwrap());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This structure provides a simple interface for constructing a JSON object.
|
||||||
|
pub struct ObjectBuilder {
|
||||||
|
object: Map<String, Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ObjectBuilder {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectBuilder {
|
||||||
|
/// Construct an `ObjectBuilder`.
|
||||||
|
pub fn new() -> ObjectBuilder {
|
||||||
|
ObjectBuilder { object: Map::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the constructed `Value`.
|
||||||
|
pub fn unwrap(self) -> Value {
|
||||||
|
Value::Object(self.object)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert a key-value pair into the object.
|
||||||
|
pub fn insert<S, V>(mut self, key: S, value: V) -> ObjectBuilder
|
||||||
|
where
|
||||||
|
S: Into<String>,
|
||||||
|
V: ser::Serialize,
|
||||||
|
{
|
||||||
|
self.object.insert(key.into(), value::to_value(&value));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates and passes an `ObjectBuilder` into a closure, then inserts the resulting array into
|
||||||
|
/// this object.
|
||||||
|
pub fn insert_array<S, F>(mut self, key: S, f: F) -> ObjectBuilder
|
||||||
|
where
|
||||||
|
S: Into<String>,
|
||||||
|
F: FnOnce(ArrayBuilder) -> ArrayBuilder,
|
||||||
|
{
|
||||||
|
let builder = ArrayBuilder::new();
|
||||||
|
self.object.insert(key.into(), f(builder).unwrap());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates and passes an `ObjectBuilder` into a closure, then inserts the resulting object into
|
||||||
|
/// this object.
|
||||||
|
pub fn insert_object<S, F>(mut self, key: S, f: F) -> ObjectBuilder
|
||||||
|
where
|
||||||
|
S: Into<String>,
|
||||||
|
F: FnOnce(ObjectBuilder) -> ObjectBuilder,
|
||||||
|
{
|
||||||
|
let builder = ObjectBuilder::new();
|
||||||
|
self.object.insert(key.into(), f(builder).unwrap());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
951
crates/nu-json/src/de.rs
Normal file
951
crates/nu-json/src/de.rs
Normal file
@ -0,0 +1,951 @@
|
|||||||
|
//! Hjson Deserialization
|
||||||
|
//!
|
||||||
|
//! This module provides for Hjson deserialization with the type `Deserializer`.
|
||||||
|
|
||||||
|
use std::char;
|
||||||
|
use std::io;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
use serde::de;
|
||||||
|
|
||||||
|
use super::error::{Error, ErrorCode, Result};
|
||||||
|
use super::util::StringReader;
|
||||||
|
use super::util::{Number, ParseNumber};
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
Normal,
|
||||||
|
Root,
|
||||||
|
Keyname,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A structure that deserializes Hjson into Rust values.
|
||||||
|
pub struct Deserializer<Iter: Iterator<Item = u8>> {
|
||||||
|
rdr: StringReader<Iter>,
|
||||||
|
str_buf: Vec<u8>,
|
||||||
|
state: State,
|
||||||
|
}
|
||||||
|
|
||||||
|
// macro_rules! try_or_invalid {
|
||||||
|
// ($self_:expr, $e:expr) => {
|
||||||
|
// match $e {
|
||||||
|
// Some(v) => v,
|
||||||
|
// None => { return Err($self_.error(ErrorCode::InvalidNumber)); }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl<Iter> Deserializer<Iter>
|
||||||
|
where
|
||||||
|
Iter: Iterator<Item = u8>,
|
||||||
|
{
|
||||||
|
/// Creates the Hjson parser from an `std::iter::Iterator`.
|
||||||
|
#[inline]
|
||||||
|
pub fn new(rdr: Iter) -> Deserializer<Iter> {
|
||||||
|
Deserializer {
|
||||||
|
rdr: StringReader::new(rdr),
|
||||||
|
str_buf: Vec::with_capacity(128),
|
||||||
|
state: State::Normal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates the Hjson parser from an `std::iter::Iterator`.
|
||||||
|
#[inline]
|
||||||
|
pub fn new_for_root(rdr: Iter) -> Deserializer<Iter> {
|
||||||
|
let mut res = Deserializer::new(rdr);
|
||||||
|
res.state = State::Root;
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `Deserializer::end` method should be called after a value has been fully deserialized.
|
||||||
|
/// This allows the `Deserializer` to validate that the input stream is at the end or that it
|
||||||
|
/// only has trailing whitespace.
|
||||||
|
#[inline]
|
||||||
|
pub fn end(&mut self) -> Result<()> {
|
||||||
|
self.rdr.parse_whitespace()?;
|
||||||
|
if self.rdr.eof()? {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(self.rdr.error(ErrorCode::TrailingCharacters))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_punctuator_char(&mut self, ch: u8) -> bool {
|
||||||
|
matches!(ch, b'{' | b'}' | b'[' | b']' | b',' | b':')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_keyname<V>(&mut self, mut visitor: V) -> Result<V::Value>
|
||||||
|
where
|
||||||
|
V: de::Visitor,
|
||||||
|
{
|
||||||
|
// quotes for keys are optional in Hjson
|
||||||
|
// unless they include {}[],: or whitespace.
|
||||||
|
// assume whitespace was already eaten
|
||||||
|
|
||||||
|
self.str_buf.clear();
|
||||||
|
|
||||||
|
let mut space: Option<usize> = None;
|
||||||
|
loop {
|
||||||
|
let ch = self.rdr.next_char_or_null()?;
|
||||||
|
|
||||||
|
if ch == b':' {
|
||||||
|
if self.str_buf.is_empty() {
|
||||||
|
return Err(self.rdr.error(ErrorCode::Custom(
|
||||||
|
"Found ':' but no key name (for an empty key name use quotes)".to_string(),
|
||||||
|
)));
|
||||||
|
} else if space.is_some()
|
||||||
|
&& space.expect("Internal error: json parsing") != self.str_buf.len()
|
||||||
|
{
|
||||||
|
return Err(self.rdr.error(ErrorCode::Custom(
|
||||||
|
"Found whitespace in your key name (use quotes to include)".to_string(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
self.rdr.uneat_char(ch);
|
||||||
|
let s = str::from_utf8(&self.str_buf).expect("Internal error: json parsing");
|
||||||
|
return visitor.visit_str(s);
|
||||||
|
} else if ch <= b' ' {
|
||||||
|
if ch == 0 {
|
||||||
|
return Err(self.rdr.error(ErrorCode::EOFWhileParsingObject));
|
||||||
|
} else if space.is_none() {
|
||||||
|
space = Some(self.str_buf.len());
|
||||||
|
}
|
||||||
|
} else if self.is_punctuator_char(ch) {
|
||||||
|
return Err(self.rdr.error(ErrorCode::Custom("Found a punctuator where a key name was expected (check your syntax or use quotes if the key name includes {}[],: or whitespace)".to_string())));
|
||||||
|
} else {
|
||||||
|
self.str_buf.push(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_value<V>(&mut self, mut visitor: V) -> Result<V::Value>
|
||||||
|
where
|
||||||
|
V: de::Visitor,
|
||||||
|
{
|
||||||
|
self.rdr.parse_whitespace()?;
|
||||||
|
|
||||||
|
if self.rdr.eof()? {
|
||||||
|
return Err(self.rdr.error(ErrorCode::EOFWhileParsingValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.state {
|
||||||
|
State::Keyname => {
|
||||||
|
self.state = State::Normal;
|
||||||
|
return self.parse_keyname(visitor);
|
||||||
|
}
|
||||||
|
State::Root => {
|
||||||
|
self.state = State::Normal;
|
||||||
|
return visitor.visit_map(MapVisitor::new(self, true));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = match self.rdr.peek_or_null()? {
|
||||||
|
/*
|
||||||
|
b'-' => {
|
||||||
|
self.rdr.eat_char();
|
||||||
|
self.parse_integer(false, visitor)
|
||||||
|
}
|
||||||
|
b'0' ..= b'9' => {
|
||||||
|
self.parse_integer(true, visitor)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
b'"' => {
|
||||||
|
self.rdr.eat_char();
|
||||||
|
self.parse_string()?;
|
||||||
|
let s = str::from_utf8(&self.str_buf).expect("Internal error: json parsing");
|
||||||
|
visitor.visit_str(s)
|
||||||
|
}
|
||||||
|
b'[' => {
|
||||||
|
self.rdr.eat_char();
|
||||||
|
visitor.visit_seq(SeqVisitor::new(self))
|
||||||
|
}
|
||||||
|
b'{' => {
|
||||||
|
self.rdr.eat_char();
|
||||||
|
visitor.visit_map(MapVisitor::new(self, false))
|
||||||
|
}
|
||||||
|
b'\x00' => Err(self.rdr.error(ErrorCode::ExpectedSomeValue)),
|
||||||
|
_ => self.parse_tfnns(visitor),
|
||||||
|
};
|
||||||
|
|
||||||
|
match value {
|
||||||
|
Ok(value) => Ok(value),
|
||||||
|
Err(Error::Syntax(code, _, _)) => Err(self.rdr.error(code)),
|
||||||
|
Err(err) => Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_ident(&mut self, ident: &[u8]) -> Result<()> {
|
||||||
|
for c in ident {
|
||||||
|
if Some(*c) != self.rdr.next_char()? {
|
||||||
|
return Err(self.rdr.error(ErrorCode::ExpectedSomeIdent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_tfnns<V>(&mut self, mut visitor: V) -> Result<V::Value>
|
||||||
|
where
|
||||||
|
V: de::Visitor,
|
||||||
|
{
|
||||||
|
// Hjson strings can be quoteless
|
||||||
|
// returns string, true, false, or null.
|
||||||
|
self.str_buf.clear();
|
||||||
|
|
||||||
|
let first = self.rdr.peek()?.expect("Internal error: json parsing");
|
||||||
|
|
||||||
|
if self.is_punctuator_char(first) {
|
||||||
|
return Err(self.rdr.error(ErrorCode::PunctuatorInQlString));
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let ch = self.rdr.next_char_or_null()?;
|
||||||
|
|
||||||
|
let is_eol = ch == b'\r' || ch == b'\n' || ch == b'\x00';
|
||||||
|
let is_comment = ch == b'#'
|
||||||
|
|| if ch == b'/' {
|
||||||
|
let next = self.rdr.peek_or_null()?;
|
||||||
|
next == b'/' || next == b'*'
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
if is_eol || is_comment || ch == b',' || ch == b'}' || ch == b']' {
|
||||||
|
let chf = self.str_buf[0];
|
||||||
|
match chf {
|
||||||
|
b'f' => {
|
||||||
|
if str::from_utf8(&self.str_buf)
|
||||||
|
.expect("Internal error: json parsing")
|
||||||
|
.trim()
|
||||||
|
== "false"
|
||||||
|
{
|
||||||
|
self.rdr.uneat_char(ch);
|
||||||
|
return visitor.visit_bool(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b'n' => {
|
||||||
|
if str::from_utf8(&self.str_buf)
|
||||||
|
.expect("Internal error: json parsing")
|
||||||
|
.trim()
|
||||||
|
== "null"
|
||||||
|
{
|
||||||
|
self.rdr.uneat_char(ch);
|
||||||
|
return visitor.visit_unit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b't' => {
|
||||||
|
if str::from_utf8(&self.str_buf)
|
||||||
|
.expect("Internal error: json parsing")
|
||||||
|
.trim()
|
||||||
|
== "true"
|
||||||
|
{
|
||||||
|
self.rdr.uneat_char(ch);
|
||||||
|
return visitor.visit_bool(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if chf == b'-' || chf >= b'0' && chf <= b'9' {
|
||||||
|
let mut pn = ParseNumber::new(self.str_buf.iter().cloned());
|
||||||
|
match pn.parse(false) {
|
||||||
|
Ok(Number::F64(v)) => {
|
||||||
|
self.rdr.uneat_char(ch);
|
||||||
|
return visitor.visit_f64(v);
|
||||||
|
}
|
||||||
|
Ok(Number::U64(v)) => {
|
||||||
|
self.rdr.uneat_char(ch);
|
||||||
|
return visitor.visit_u64(v);
|
||||||
|
}
|
||||||
|
Ok(Number::I64(v)) => {
|
||||||
|
self.rdr.uneat_char(ch);
|
||||||
|
return visitor.visit_i64(v);
|
||||||
|
}
|
||||||
|
Err(_) => {} // not a number, continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if is_eol {
|
||||||
|
// remove any whitespace at the end (ignored in quoteless strings)
|
||||||
|
return visitor.visit_str(
|
||||||
|
str::from_utf8(&self.str_buf)
|
||||||
|
.expect("Internal error: json parsing")
|
||||||
|
.trim(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.str_buf.push(ch);
|
||||||
|
|
||||||
|
if self.str_buf == vec![b'\''; 3] {
|
||||||
|
return self.parse_ml_string(visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_hex_escape(&mut self) -> Result<u16> {
|
||||||
|
let mut i = 0;
|
||||||
|
let mut n = 0u16;
|
||||||
|
while i < 4 && !(self.rdr.eof()?) {
|
||||||
|
n = match self.rdr.next_char_or_null()? {
|
||||||
|
c @ b'0'..=b'9' => n * 16_u16 + ((c as u16) - (b'0' as u16)),
|
||||||
|
b'a' | b'A' => n * 16_u16 + 10_u16,
|
||||||
|
b'b' | b'B' => n * 16_u16 + 11_u16,
|
||||||
|
b'c' | b'C' => n * 16_u16 + 12_u16,
|
||||||
|
b'd' | b'D' => n * 16_u16 + 13_u16,
|
||||||
|
b'e' | b'E' => n * 16_u16 + 14_u16,
|
||||||
|
b'f' | b'F' => n * 16_u16 + 15_u16,
|
||||||
|
_ => {
|
||||||
|
return Err(self.rdr.error(ErrorCode::InvalidEscape));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error out if we didn't parse 4 digits.
|
||||||
|
if i != 4 {
|
||||||
|
return Err(self.rdr.error(ErrorCode::InvalidEscape));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ml_skip_white(&mut self) -> Result<bool> {
|
||||||
|
match self.rdr.peek_or_null()? {
|
||||||
|
b' ' | b'\t' | b'\r' => {
|
||||||
|
self.rdr.eat_char();
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
_ => Ok(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ml_skip_indent(&mut self, indent: usize) -> Result<()> {
|
||||||
|
let mut skip = indent;
|
||||||
|
while self.ml_skip_white()? && skip > 0 {
|
||||||
|
skip -= 1;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_ml_string<V>(&mut self, mut visitor: V) -> Result<V::Value>
|
||||||
|
where
|
||||||
|
V: de::Visitor,
|
||||||
|
{
|
||||||
|
self.str_buf.clear();
|
||||||
|
|
||||||
|
// Parse a multiline string value.
|
||||||
|
let mut triple = 0;
|
||||||
|
|
||||||
|
// we are at ''' +1 - get indent
|
||||||
|
let (_, col) = self.rdr.pos();
|
||||||
|
let indent = col - 4;
|
||||||
|
|
||||||
|
// skip white/to (newline)
|
||||||
|
while self.ml_skip_white()? {}
|
||||||
|
if self.rdr.peek_or_null()? == b'\n' {
|
||||||
|
self.rdr.eat_char();
|
||||||
|
self.ml_skip_indent(indent)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When parsing multiline string values, we must look for ' characters.
|
||||||
|
loop {
|
||||||
|
if self.rdr.eof()? {
|
||||||
|
return Err(self.rdr.error(ErrorCode::EOFWhileParsingString));
|
||||||
|
} // todo error("Bad multiline string");
|
||||||
|
let ch = self.rdr.next_char_or_null()?;
|
||||||
|
|
||||||
|
if ch == b'\'' {
|
||||||
|
triple += 1;
|
||||||
|
if triple == 3 {
|
||||||
|
if self.str_buf.last() == Some(&b'\n') {
|
||||||
|
self.str_buf.pop();
|
||||||
|
}
|
||||||
|
let res = str::from_utf8(&self.str_buf).expect("Internal error: json parsing");
|
||||||
|
//todo if (self.str_buf.slice(-1) === '\n') self.str_buf=self.str_buf.slice(0, -1); // remove last EOL
|
||||||
|
return visitor.visit_str(res);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while triple > 0 {
|
||||||
|
self.str_buf.push(b'\'');
|
||||||
|
triple -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ch != b'\r' {
|
||||||
|
self.str_buf.push(ch);
|
||||||
|
}
|
||||||
|
if ch == b'\n' {
|
||||||
|
self.ml_skip_indent(indent)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_string(&mut self) -> Result<()> {
|
||||||
|
self.str_buf.clear();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let ch = match self.rdr.next_char()? {
|
||||||
|
Some(ch) => ch,
|
||||||
|
None => {
|
||||||
|
return Err(self.rdr.error(ErrorCode::EOFWhileParsingString));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match ch {
|
||||||
|
b'"' => {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
b'\\' => {
|
||||||
|
let ch = match self.rdr.next_char()? {
|
||||||
|
Some(ch) => ch,
|
||||||
|
None => {
|
||||||
|
return Err(self.rdr.error(ErrorCode::EOFWhileParsingString));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match ch {
|
||||||
|
b'"' => self.str_buf.push(b'"'),
|
||||||
|
b'\\' => self.str_buf.push(b'\\'),
|
||||||
|
b'/' => self.str_buf.push(b'/'),
|
||||||
|
b'b' => self.str_buf.push(b'\x08'),
|
||||||
|
b'f' => self.str_buf.push(b'\x0c'),
|
||||||
|
b'n' => self.str_buf.push(b'\n'),
|
||||||
|
b'r' => self.str_buf.push(b'\r'),
|
||||||
|
b't' => self.str_buf.push(b'\t'),
|
||||||
|
b'u' => {
|
||||||
|
let c = match self.decode_hex_escape()? {
|
||||||
|
0xDC00..=0xDFFF => {
|
||||||
|
return Err(self
|
||||||
|
.rdr
|
||||||
|
.error(ErrorCode::LoneLeadingSurrogateInHexEscape));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-BMP characters are encoded as a sequence of
|
||||||
|
// two hex escapes, representing UTF-16 surrogates.
|
||||||
|
n1 @ 0xD800..=0xDBFF => {
|
||||||
|
match (self.rdr.next_char()?, self.rdr.next_char()?) {
|
||||||
|
(Some(b'\\'), Some(b'u')) => (),
|
||||||
|
_ => {
|
||||||
|
return Err(self
|
||||||
|
.rdr
|
||||||
|
.error(ErrorCode::UnexpectedEndOfHexEscape));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let n2 = self.decode_hex_escape()?;
|
||||||
|
|
||||||
|
if n2 < 0xDC00 || n2 > 0xDFFF {
|
||||||
|
return Err(self
|
||||||
|
.rdr
|
||||||
|
.error(ErrorCode::LoneLeadingSurrogateInHexEscape));
|
||||||
|
}
|
||||||
|
|
||||||
|
let n = (((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32)
|
||||||
|
+ 0x1_0000;
|
||||||
|
|
||||||
|
match char::from_u32(n as u32) {
|
||||||
|
Some(c) => c,
|
||||||
|
None => {
|
||||||
|
return Err(self
|
||||||
|
.rdr
|
||||||
|
.error(ErrorCode::InvalidUnicodeCodePoint));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n => match char::from_u32(n as u32) {
|
||||||
|
Some(c) => c,
|
||||||
|
None => {
|
||||||
|
return Err(self
|
||||||
|
.rdr
|
||||||
|
.error(ErrorCode::InvalidUnicodeCodePoint));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME: this allocation is required in order to be compatible with stable
|
||||||
|
// rust, which doesn't support encoding a `char` into a stack buffer.
|
||||||
|
let mut buf = String::new();
|
||||||
|
buf.push(c);
|
||||||
|
self.str_buf.extend(buf.bytes());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(self.rdr.error(ErrorCode::InvalidEscape));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ch => {
|
||||||
|
self.str_buf.push(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_object_colon(&mut self) -> Result<()> {
|
||||||
|
self.rdr.parse_whitespace()?;
|
||||||
|
|
||||||
|
match self.rdr.next_char()? {
|
||||||
|
Some(b':') => Ok(()),
|
||||||
|
Some(_) => Err(self.rdr.error(ErrorCode::ExpectedColon)),
|
||||||
|
None => Err(self.rdr.error(ErrorCode::EOFWhileParsingObject)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Iter> de::Deserializer for Deserializer<Iter>
|
||||||
|
where
|
||||||
|
Iter: Iterator<Item = u8>,
|
||||||
|
{
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deserialize<V>(&mut self, visitor: V) -> Result<V::Value>
|
||||||
|
where
|
||||||
|
V: de::Visitor,
|
||||||
|
{
|
||||||
|
self.parse_value(visitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses a `null` as a None, and any other values as a `Some(...)`.
|
||||||
|
#[inline]
|
||||||
|
fn deserialize_option<V>(&mut self, mut visitor: V) -> Result<V::Value>
|
||||||
|
where
|
||||||
|
V: de::Visitor,
|
||||||
|
{
|
||||||
|
self.rdr.parse_whitespace()?;
|
||||||
|
|
||||||
|
match self.rdr.peek_or_null()? {
|
||||||
|
b'n' => {
|
||||||
|
self.rdr.eat_char();
|
||||||
|
self.parse_ident(b"ull")?;
|
||||||
|
visitor.visit_none()
|
||||||
|
}
|
||||||
|
_ => visitor.visit_some(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses a newtype struct as the underlying value.
|
||||||
|
#[inline]
|
||||||
|
fn deserialize_newtype_struct<V>(&mut self, _name: &str, mut visitor: V) -> Result<V::Value>
|
||||||
|
where
|
||||||
|
V: de::Visitor,
|
||||||
|
{
|
||||||
|
visitor.visit_newtype_struct(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_to_deserialize! {
|
||||||
|
deserialize_bool();
|
||||||
|
deserialize_usize();
|
||||||
|
deserialize_u8();
|
||||||
|
deserialize_u16();
|
||||||
|
deserialize_u32();
|
||||||
|
deserialize_u64();
|
||||||
|
deserialize_isize();
|
||||||
|
deserialize_i8();
|
||||||
|
deserialize_i16();
|
||||||
|
deserialize_i32();
|
||||||
|
deserialize_i64();
|
||||||
|
deserialize_f32();
|
||||||
|
deserialize_f64();
|
||||||
|
deserialize_char();
|
||||||
|
deserialize_str();
|
||||||
|
deserialize_string();
|
||||||
|
deserialize_unit();
|
||||||
|
deserialize_seq();
|
||||||
|
deserialize_seq_fixed_size(len: usize);
|
||||||
|
deserialize_bytes();
|
||||||
|
deserialize_map();
|
||||||
|
deserialize_unit_struct(name: &'static str);
|
||||||
|
deserialize_tuple_struct(name: &'static str, len: usize);
|
||||||
|
deserialize_struct(name: &'static str, fields: &'static [&'static str]);
|
||||||
|
deserialize_struct_field();
|
||||||
|
deserialize_tuple(len: usize);
|
||||||
|
deserialize_enum(name: &'static str, variants: &'static [&'static str]);
|
||||||
|
deserialize_ignored_any();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SeqVisitor<'a, Iter: 'a + Iterator<Item = u8>> {
|
||||||
|
de: &'a mut Deserializer<Iter>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Iter: Iterator<Item = u8>> SeqVisitor<'a, Iter> {
|
||||||
|
fn new(de: &'a mut Deserializer<Iter>) -> Self {
|
||||||
|
SeqVisitor { de }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Iter> de::SeqVisitor for SeqVisitor<'a, Iter>
|
||||||
|
where
|
||||||
|
Iter: Iterator<Item = u8>,
|
||||||
|
{
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn visit<T>(&mut self) -> Result<Option<T>>
|
||||||
|
where
|
||||||
|
T: de::Deserialize,
|
||||||
|
{
|
||||||
|
self.de.rdr.parse_whitespace()?;
|
||||||
|
|
||||||
|
match self.de.rdr.peek()? {
|
||||||
|
Some(b']') => {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Some(_) => {}
|
||||||
|
None => {
|
||||||
|
return Err(self.de.rdr.error(ErrorCode::EOFWhileParsingList));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = de::Deserialize::deserialize(self.de)?;
|
||||||
|
|
||||||
|
// in Hjson the comma is optional and trailing commas are allowed
|
||||||
|
self.de.rdr.parse_whitespace()?;
|
||||||
|
if self.de.rdr.peek()? == Some(b',') {
|
||||||
|
self.de.rdr.eat_char();
|
||||||
|
self.de.rdr.parse_whitespace()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end(&mut self) -> Result<()> {
|
||||||
|
self.de.rdr.parse_whitespace()?;
|
||||||
|
|
||||||
|
match self.de.rdr.next_char()? {
|
||||||
|
Some(b']') => Ok(()),
|
||||||
|
Some(_) => Err(self.de.rdr.error(ErrorCode::TrailingCharacters)),
|
||||||
|
None => Err(self.de.rdr.error(ErrorCode::EOFWhileParsingList)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MapVisitor<'a, Iter: 'a + Iterator<Item = u8>> {
|
||||||
|
de: &'a mut Deserializer<Iter>,
|
||||||
|
first: bool,
|
||||||
|
root: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Iter: Iterator<Item = u8>> MapVisitor<'a, Iter> {
|
||||||
|
fn new(de: &'a mut Deserializer<Iter>, root: bool) -> Self {
|
||||||
|
MapVisitor {
|
||||||
|
de,
|
||||||
|
first: true,
|
||||||
|
root,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Iter> de::MapVisitor for MapVisitor<'a, Iter>
|
||||||
|
where
|
||||||
|
Iter: Iterator<Item = u8>,
|
||||||
|
{
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn visit_key<K>(&mut self) -> Result<Option<K>>
|
||||||
|
where
|
||||||
|
K: de::Deserialize,
|
||||||
|
{
|
||||||
|
self.de.rdr.parse_whitespace()?;
|
||||||
|
|
||||||
|
if self.first {
|
||||||
|
self.first = false;
|
||||||
|
} else if self.de.rdr.peek()? == Some(b',') {
|
||||||
|
// in Hjson the comma is optional and trailing commas are allowed
|
||||||
|
self.de.rdr.eat_char();
|
||||||
|
self.de.rdr.parse_whitespace()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.de.rdr.peek()? {
|
||||||
|
Some(b'}') => return Ok(None), // handled later for root
|
||||||
|
Some(_) => {}
|
||||||
|
None => {
|
||||||
|
if self.root {
|
||||||
|
return Ok(None);
|
||||||
|
} else {
|
||||||
|
return Err(self.de.rdr.error(ErrorCode::EOFWhileParsingObject));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.de.rdr.peek()? {
|
||||||
|
Some(ch) => {
|
||||||
|
self.de.state = if ch == b'"' {
|
||||||
|
State::Normal
|
||||||
|
} else {
|
||||||
|
State::Keyname
|
||||||
|
};
|
||||||
|
Ok(Some(de::Deserialize::deserialize(self.de)?))
|
||||||
|
}
|
||||||
|
None => Err(self.de.rdr.error(ErrorCode::EOFWhileParsingValue)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_value<V>(&mut self) -> Result<V>
|
||||||
|
where
|
||||||
|
V: de::Deserialize,
|
||||||
|
{
|
||||||
|
self.de.parse_object_colon()?;
|
||||||
|
|
||||||
|
Ok(de::Deserialize::deserialize(self.de)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end(&mut self) -> Result<()> {
|
||||||
|
self.de.rdr.parse_whitespace()?;
|
||||||
|
|
||||||
|
match self.de.rdr.next_char()? {
|
||||||
|
Some(b'}') => {
|
||||||
|
if !self.root {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(self.de.rdr.error(ErrorCode::TrailingCharacters))
|
||||||
|
} // todo
|
||||||
|
}
|
||||||
|
Some(_) => Err(self.de.rdr.error(ErrorCode::TrailingCharacters)),
|
||||||
|
None => {
|
||||||
|
if self.root {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(self.de.rdr.error(ErrorCode::EOFWhileParsingObject))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn missing_field<V>(&mut self, field: &'static str) -> Result<V>
|
||||||
|
where
|
||||||
|
V: de::Deserialize,
|
||||||
|
{
|
||||||
|
struct MissingFieldDeserializer(&'static str);
|
||||||
|
|
||||||
|
impl de::Deserializer for MissingFieldDeserializer {
|
||||||
|
type Error = de::value::Error;
|
||||||
|
|
||||||
|
fn deserialize<V>(&mut self, _visitor: V) -> std::result::Result<V::Value, Self::Error>
|
||||||
|
where
|
||||||
|
V: de::Visitor,
|
||||||
|
{
|
||||||
|
let &mut MissingFieldDeserializer(field) = self;
|
||||||
|
Err(de::value::Error::MissingField(field))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_option<V>(
|
||||||
|
&mut self,
|
||||||
|
mut visitor: V,
|
||||||
|
) -> std::result::Result<V::Value, Self::Error>
|
||||||
|
where
|
||||||
|
V: de::Visitor,
|
||||||
|
{
|
||||||
|
visitor.visit_none()
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_to_deserialize! {
|
||||||
|
deserialize_bool();
|
||||||
|
deserialize_usize();
|
||||||
|
deserialize_u8();
|
||||||
|
deserialize_u16();
|
||||||
|
deserialize_u32();
|
||||||
|
deserialize_u64();
|
||||||
|
deserialize_isize();
|
||||||
|
deserialize_i8();
|
||||||
|
deserialize_i16();
|
||||||
|
deserialize_i32();
|
||||||
|
deserialize_i64();
|
||||||
|
deserialize_f32();
|
||||||
|
deserialize_f64();
|
||||||
|
deserialize_char();
|
||||||
|
deserialize_str();
|
||||||
|
deserialize_string();
|
||||||
|
deserialize_unit();
|
||||||
|
deserialize_seq();
|
||||||
|
deserialize_seq_fixed_size(len: usize);
|
||||||
|
deserialize_bytes();
|
||||||
|
deserialize_map();
|
||||||
|
deserialize_unit_struct(name: &'static str);
|
||||||
|
deserialize_newtype_struct(name: &'static str);
|
||||||
|
deserialize_tuple_struct(name: &'static str, len: usize);
|
||||||
|
deserialize_struct(name: &'static str, fields: &'static [&'static str]);
|
||||||
|
deserialize_struct_field();
|
||||||
|
deserialize_tuple(len: usize);
|
||||||
|
deserialize_enum(name: &'static str, variants: &'static [&'static str]);
|
||||||
|
deserialize_ignored_any();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut de = MissingFieldDeserializer(field);
|
||||||
|
Ok(de::Deserialize::deserialize(&mut de)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Iter> de::VariantVisitor for Deserializer<Iter>
|
||||||
|
where
|
||||||
|
Iter: Iterator<Item = u8>,
|
||||||
|
{
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn visit_variant<V>(&mut self) -> Result<V>
|
||||||
|
where
|
||||||
|
V: de::Deserialize,
|
||||||
|
{
|
||||||
|
let val = de::Deserialize::deserialize(self)?;
|
||||||
|
self.parse_object_colon()?;
|
||||||
|
Ok(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_unit(&mut self) -> Result<()> {
|
||||||
|
de::Deserialize::deserialize(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_newtype<T>(&mut self) -> Result<T>
|
||||||
|
where
|
||||||
|
T: de::Deserialize,
|
||||||
|
{
|
||||||
|
de::Deserialize::deserialize(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_tuple<V>(&mut self, _len: usize, visitor: V) -> Result<V::Value>
|
||||||
|
where
|
||||||
|
V: de::Visitor,
|
||||||
|
{
|
||||||
|
de::Deserializer::deserialize(self, visitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_struct<V>(&mut self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
|
||||||
|
where
|
||||||
|
V: de::Visitor,
|
||||||
|
{
|
||||||
|
de::Deserializer::deserialize(self, visitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/// Iterator that deserializes a stream into multiple Hjson values.
|
||||||
|
pub struct StreamDeserializer<T, Iter>
|
||||||
|
where
|
||||||
|
Iter: Iterator<Item = u8>,
|
||||||
|
T: de::Deserialize,
|
||||||
|
{
|
||||||
|
deser: Deserializer<Iter>,
|
||||||
|
_marker: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, Iter> StreamDeserializer<T, Iter>
|
||||||
|
where
|
||||||
|
Iter: Iterator<Item = u8>,
|
||||||
|
T: de::Deserialize,
|
||||||
|
{
|
||||||
|
/// Returns an `Iterator` of decoded Hjson values from an iterator over
|
||||||
|
/// `Iterator<Item=u8>`.
|
||||||
|
pub fn new(iter: Iter) -> StreamDeserializer<T, Iter> {
|
||||||
|
StreamDeserializer {
|
||||||
|
deser: Deserializer::new(iter),
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, Iter> Iterator for StreamDeserializer<T, Iter>
|
||||||
|
where
|
||||||
|
Iter: Iterator<Item = u8>,
|
||||||
|
T: de::Deserialize,
|
||||||
|
{
|
||||||
|
type Item = Result<T>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Result<T>> {
|
||||||
|
// skip whitespaces, if any
|
||||||
|
// this helps with trailing whitespaces, since whitespaces between
|
||||||
|
// values are handled for us.
|
||||||
|
if let Err(e) = self.deser.rdr.parse_whitespace() {
|
||||||
|
return Some(Err(e));
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.deser.rdr.eof() {
|
||||||
|
Ok(true) => None,
|
||||||
|
Ok(false) => match de::Deserialize::deserialize(&mut self.deser) {
|
||||||
|
Ok(v) => Some(Ok(v)),
|
||||||
|
Err(e) => Some(Err(e)),
|
||||||
|
},
|
||||||
|
Err(e) => Some(Err(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/// Decodes a Hjson value from an iterator over an iterator
|
||||||
|
/// `Iterator<Item=u8>`.
|
||||||
|
pub fn from_iter<I, T>(iter: I) -> Result<T>
|
||||||
|
where
|
||||||
|
I: Iterator<Item = io::Result<u8>>,
|
||||||
|
T: de::Deserialize,
|
||||||
|
{
|
||||||
|
let fold: io::Result<Vec<_>> = iter.collect();
|
||||||
|
if let Err(e) = fold {
|
||||||
|
return Err(Error::Io(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
let bytes = fold.expect("Internal error: json parsing");
|
||||||
|
|
||||||
|
// deserialize tries first to decode with legacy support (new_for_root)
|
||||||
|
// and then with the standard method if this fails.
|
||||||
|
// todo: add compile switch
|
||||||
|
|
||||||
|
// deserialize and make sure the whole stream has been consumed
|
||||||
|
let mut de = Deserializer::new_for_root(bytes.iter().cloned());
|
||||||
|
let value = match de::Deserialize::deserialize(&mut de).and_then(|x| {
|
||||||
|
de.end()?;
|
||||||
|
Ok(x)
|
||||||
|
}) {
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(_) => {
|
||||||
|
let mut de2 = Deserializer::new(bytes.iter().cloned());
|
||||||
|
match de::Deserialize::deserialize(&mut de2).and_then(|x| {
|
||||||
|
de2.end()?;
|
||||||
|
Ok(x)
|
||||||
|
}) {
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* without legacy support:
|
||||||
|
// deserialize and make sure the whole stream has been consumed
|
||||||
|
let mut de = Deserializer::new(bytes.iter().map(|b| *b));
|
||||||
|
let value = match de::Deserialize::deserialize(&mut de)
|
||||||
|
.and_then(|x| { de.end()); Ok(x) })
|
||||||
|
{
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decodes a Hjson value from a `std::io::Read`.
|
||||||
|
pub fn from_reader<R, T>(rdr: R) -> Result<T>
|
||||||
|
where
|
||||||
|
R: io::Read,
|
||||||
|
T: de::Deserialize,
|
||||||
|
{
|
||||||
|
from_iter(rdr.bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decodes a Hjson value from a byte slice `&[u8]`.
|
||||||
|
pub fn from_slice<T>(v: &[u8]) -> Result<T>
|
||||||
|
where
|
||||||
|
T: de::Deserialize,
|
||||||
|
{
|
||||||
|
from_iter(v.iter().map(|byte| Ok(*byte)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decodes a Hjson value from a `&str`.
|
||||||
|
pub fn from_str<T>(s: &str) -> Result<T>
|
||||||
|
where
|
||||||
|
T: de::Deserialize,
|
||||||
|
{
|
||||||
|
from_slice(s.as_bytes())
|
||||||
|
}
|
243
crates/nu-json/src/error.rs
Normal file
243
crates/nu-json/src/error.rs
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
//! JSON Errors
|
||||||
|
//!
|
||||||
|
//! This module is centered around the `Error` and `ErrorCode` types, which represents all possible
|
||||||
|
//! `serde_hjson` errors.
|
||||||
|
|
||||||
|
use std::error;
|
||||||
|
use std::fmt;
|
||||||
|
use std::io;
|
||||||
|
use std::result;
|
||||||
|
use std::string::FromUtf8Error;
|
||||||
|
|
||||||
|
use serde::de;
|
||||||
|
use serde::ser;
|
||||||
|
|
||||||
|
/// The errors that can arise while parsing a JSON stream.
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub enum ErrorCode {
|
||||||
|
/// Catchall for syntax error messages
|
||||||
|
Custom(String),
|
||||||
|
|
||||||
|
/// Incorrect type from value
|
||||||
|
InvalidType(de::Type),
|
||||||
|
|
||||||
|
/// Incorrect value
|
||||||
|
InvalidValue(String),
|
||||||
|
|
||||||
|
/// Invalid length
|
||||||
|
InvalidLength(usize),
|
||||||
|
|
||||||
|
/// Unknown variant in an enum.
|
||||||
|
UnknownVariant(String),
|
||||||
|
|
||||||
|
/// Unknown field in struct.
|
||||||
|
UnknownField(String),
|
||||||
|
|
||||||
|
/// Struct is missing a field.
|
||||||
|
MissingField(&'static str),
|
||||||
|
|
||||||
|
/// EOF while parsing a list.
|
||||||
|
EOFWhileParsingList,
|
||||||
|
|
||||||
|
/// EOF while parsing an object.
|
||||||
|
EOFWhileParsingObject,
|
||||||
|
|
||||||
|
/// EOF while parsing a string.
|
||||||
|
EOFWhileParsingString,
|
||||||
|
|
||||||
|
/// EOF while parsing a JSON value.
|
||||||
|
EOFWhileParsingValue,
|
||||||
|
|
||||||
|
/// Expected this character to be a `':'`.
|
||||||
|
ExpectedColon,
|
||||||
|
|
||||||
|
/// Expected this character to be either a `','` or a `]`.
|
||||||
|
ExpectedListCommaOrEnd,
|
||||||
|
|
||||||
|
/// Expected this character to be either a `','` or a `}`.
|
||||||
|
ExpectedObjectCommaOrEnd,
|
||||||
|
|
||||||
|
/// Expected to parse either a `true`, `false`, or a `null`.
|
||||||
|
ExpectedSomeIdent,
|
||||||
|
|
||||||
|
/// Expected this character to start a JSON value.
|
||||||
|
ExpectedSomeValue,
|
||||||
|
|
||||||
|
/// Invalid hex escape code.
|
||||||
|
InvalidEscape,
|
||||||
|
|
||||||
|
/// Invalid number.
|
||||||
|
InvalidNumber,
|
||||||
|
|
||||||
|
/// Invalid unicode code point.
|
||||||
|
InvalidUnicodeCodePoint,
|
||||||
|
|
||||||
|
/// Object key is not a string.
|
||||||
|
KeyMustBeAString,
|
||||||
|
|
||||||
|
/// Lone leading surrogate in hex escape.
|
||||||
|
LoneLeadingSurrogateInHexEscape,
|
||||||
|
|
||||||
|
/// JSON has non-whitespace trailing characters after the value.
|
||||||
|
TrailingCharacters,
|
||||||
|
|
||||||
|
/// Unexpected end of hex excape.
|
||||||
|
UnexpectedEndOfHexEscape,
|
||||||
|
|
||||||
|
/// Found a punctuator character when expecting a quoteless string.
|
||||||
|
PunctuatorInQlString,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for ErrorCode {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
//use std::fmt::Debug;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
ErrorCode::Custom(ref msg) => write!(f, "{}", msg),
|
||||||
|
ErrorCode::InvalidType(ref ty) => write!(f, "invalid type: {:?}", ty),
|
||||||
|
ErrorCode::InvalidValue(ref msg) => write!(f, "invalid value: {}", msg),
|
||||||
|
ErrorCode::InvalidLength(ref len) => write!(f, "invalid value length {}", len),
|
||||||
|
ErrorCode::UnknownVariant(ref variant) => write!(f, "unknown variant \"{}\"", variant),
|
||||||
|
ErrorCode::UnknownField(ref field) => write!(f, "unknown field \"{}\"", field),
|
||||||
|
ErrorCode::MissingField(ref field) => write!(f, "missing field \"{}\"", field),
|
||||||
|
ErrorCode::EOFWhileParsingList => "EOF while parsing a list".fmt(f),
|
||||||
|
ErrorCode::EOFWhileParsingObject => "EOF while parsing an object".fmt(f),
|
||||||
|
ErrorCode::EOFWhileParsingString => "EOF while parsing a string".fmt(f),
|
||||||
|
ErrorCode::EOFWhileParsingValue => "EOF while parsing a value".fmt(f),
|
||||||
|
ErrorCode::ExpectedColon => "expected `:`".fmt(f),
|
||||||
|
ErrorCode::ExpectedListCommaOrEnd => "expected `,` or `]`".fmt(f),
|
||||||
|
ErrorCode::ExpectedObjectCommaOrEnd => "expected `,` or `}`".fmt(f),
|
||||||
|
ErrorCode::ExpectedSomeIdent => "expected ident".fmt(f),
|
||||||
|
ErrorCode::ExpectedSomeValue => "expected value".fmt(f),
|
||||||
|
ErrorCode::InvalidEscape => "invalid escape".fmt(f),
|
||||||
|
ErrorCode::InvalidNumber => "invalid number".fmt(f),
|
||||||
|
ErrorCode::InvalidUnicodeCodePoint => "invalid unicode code point".fmt(f),
|
||||||
|
ErrorCode::KeyMustBeAString => "key must be a string".fmt(f),
|
||||||
|
ErrorCode::LoneLeadingSurrogateInHexEscape => {
|
||||||
|
"lone leading surrogate in hex escape".fmt(f)
|
||||||
|
}
|
||||||
|
ErrorCode::TrailingCharacters => "trailing characters".fmt(f),
|
||||||
|
ErrorCode::UnexpectedEndOfHexEscape => "unexpected end of hex escape".fmt(f),
|
||||||
|
ErrorCode::PunctuatorInQlString => {
|
||||||
|
"found a punctuator character when expecting a quoteless string".fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This type represents all possible errors that can occur when serializing or deserializing a
|
||||||
|
/// value into JSON.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
/// The JSON value had some syntatic error.
|
||||||
|
Syntax(ErrorCode, usize, usize),
|
||||||
|
|
||||||
|
/// Some IO error occurred when serializing or deserializing a value.
|
||||||
|
Io(io::Error),
|
||||||
|
|
||||||
|
/// Some UTF8 error occurred while serializing or deserializing a value.
|
||||||
|
FromUtf8(FromUtf8Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::Error for Error {
|
||||||
|
fn cause(&self) -> Option<&dyn error::Error> {
|
||||||
|
match *self {
|
||||||
|
Error::Io(ref error) => Some(error),
|
||||||
|
Error::FromUtf8(ref error) => Some(error),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Error::Syntax(ref code, line, col) => {
|
||||||
|
write!(fmt, "{:?} at line {} column {}", code, line, col)
|
||||||
|
}
|
||||||
|
Error::Io(ref error) => fmt::Display::fmt(error, fmt),
|
||||||
|
Error::FromUtf8(ref error) => fmt::Display::fmt(error, fmt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for Error {
|
||||||
|
fn from(error: io::Error) -> Error {
|
||||||
|
Error::Io(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FromUtf8Error> for Error {
|
||||||
|
fn from(error: FromUtf8Error) -> Error {
|
||||||
|
Error::FromUtf8(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<de::value::Error> for Error {
|
||||||
|
fn from(error: de::value::Error) -> Error {
|
||||||
|
match error {
|
||||||
|
de::value::Error::Custom(e) => Error::Syntax(ErrorCode::Custom(e), 0, 0),
|
||||||
|
de::value::Error::EndOfStream => de::Error::end_of_stream(),
|
||||||
|
de::value::Error::InvalidType(ty) => Error::Syntax(ErrorCode::InvalidType(ty), 0, 0),
|
||||||
|
de::value::Error::InvalidValue(msg) => {
|
||||||
|
Error::Syntax(ErrorCode::InvalidValue(msg), 0, 0)
|
||||||
|
}
|
||||||
|
de::value::Error::InvalidLength(len) => {
|
||||||
|
Error::Syntax(ErrorCode::InvalidLength(len), 0, 0)
|
||||||
|
}
|
||||||
|
de::value::Error::UnknownVariant(variant) => {
|
||||||
|
Error::Syntax(ErrorCode::UnknownVariant(variant), 0, 0)
|
||||||
|
}
|
||||||
|
de::value::Error::UnknownField(field) => {
|
||||||
|
Error::Syntax(ErrorCode::UnknownField(field), 0, 0)
|
||||||
|
}
|
||||||
|
de::value::Error::MissingField(field) => {
|
||||||
|
Error::Syntax(ErrorCode::MissingField(field), 0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl de::Error for Error {
|
||||||
|
fn custom<T: Into<String>>(msg: T) -> Error {
|
||||||
|
Error::Syntax(ErrorCode::Custom(msg.into()), 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end_of_stream() -> Error {
|
||||||
|
Error::Syntax(ErrorCode::EOFWhileParsingValue, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invalid_type(ty: de::Type) -> Error {
|
||||||
|
Error::Syntax(ErrorCode::InvalidType(ty), 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invalid_value(msg: &str) -> Error {
|
||||||
|
Error::Syntax(ErrorCode::InvalidValue(msg.to_owned()), 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invalid_length(len: usize) -> Error {
|
||||||
|
Error::Syntax(ErrorCode::InvalidLength(len), 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unknown_variant(variant: &str) -> Error {
|
||||||
|
Error::Syntax(ErrorCode::UnknownVariant(String::from(variant)), 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unknown_field(field: &str) -> Error {
|
||||||
|
Error::Syntax(ErrorCode::UnknownField(String::from(field)), 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn missing_field(field: &'static str) -> Error {
|
||||||
|
Error::Syntax(ErrorCode::MissingField(field), 0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ser::Error for Error {
|
||||||
|
/// Raised when there is general error when deserializing a type.
|
||||||
|
fn custom<T: Into<String>>(msg: T) -> Error {
|
||||||
|
Error::Syntax(ErrorCode::Custom(msg.into()), 0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper alias for `Result` objects that return a JSON `Error`.
|
||||||
|
pub type Result<T> = result::Result<T, Error>;
|
38
crates/nu-json/src/forward.rs
Normal file
38
crates/nu-json/src/forward.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#[macro_export]
|
||||||
|
/// Create a function to forward a specific serialize call to the generic deserialize
|
||||||
|
macro_rules! forward_to_deserialize {
|
||||||
|
($(
|
||||||
|
$name:ident ( $( $arg:ident : $ty:ty ),* );
|
||||||
|
)*) => {
|
||||||
|
$(
|
||||||
|
forward_to_deserialize!{
|
||||||
|
func: $name ( $( $arg: $ty ),* );
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
|
||||||
|
(func: deserialize_enum ( $( $arg:ident : $ty:ty ),* );) => {
|
||||||
|
fn deserialize_enum<V>(
|
||||||
|
&mut self,
|
||||||
|
$(_: $ty,)*
|
||||||
|
_visitor: V,
|
||||||
|
) -> ::std::result::Result<V::Value, Self::Error>
|
||||||
|
where V: ::serde::de::EnumVisitor
|
||||||
|
{
|
||||||
|
Err(::serde::de::Error::invalid_type(::serde::de::Type::Enum))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(func: $name:ident ( $( $arg:ident : $ty:ty ),* );) => {
|
||||||
|
#[inline]
|
||||||
|
fn $name<V>(
|
||||||
|
&mut self,
|
||||||
|
$(_: $ty,)*
|
||||||
|
visitor: V,
|
||||||
|
) -> ::std::result::Result<V::Value, Self::Error>
|
||||||
|
where V: ::serde::de::Visitor
|
||||||
|
{
|
||||||
|
self.deserialize(visitor)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
16
crates/nu-json/src/lib.rs
Normal file
16
crates/nu-json/src/lib.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
pub use self::de::{
|
||||||
|
from_iter, from_reader, from_slice, from_str, Deserializer, StreamDeserializer,
|
||||||
|
};
|
||||||
|
pub use self::error::{Error, ErrorCode, Result};
|
||||||
|
pub use self::ser::{to_string, to_vec, to_writer, Serializer};
|
||||||
|
pub use self::value::{from_value, to_value, Map, Value};
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod forward;
|
||||||
|
|
||||||
|
pub mod builder;
|
||||||
|
pub mod de;
|
||||||
|
pub mod error;
|
||||||
|
pub mod ser;
|
||||||
|
mod util;
|
||||||
|
pub mod value;
|
1058
crates/nu-json/src/ser.rs
Normal file
1058
crates/nu-json/src/ser.rs
Normal file
File diff suppressed because it is too large
Load Diff
328
crates/nu-json/src/util.rs
Normal file
328
crates/nu-json/src/util.rs
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
use std::io;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
use super::error::{Error, ErrorCode, Result};
|
||||||
|
|
||||||
|
pub struct StringReader<Iter: Iterator<Item = u8>> {
|
||||||
|
iter: Iter,
|
||||||
|
line: usize,
|
||||||
|
col: usize,
|
||||||
|
ch: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Iter> StringReader<Iter>
|
||||||
|
where
|
||||||
|
Iter: Iterator<Item = u8>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
pub fn new(iter: Iter) -> Self {
|
||||||
|
StringReader {
|
||||||
|
iter,
|
||||||
|
line: 1,
|
||||||
|
col: 0,
|
||||||
|
ch: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<io::Result<u8>> {
|
||||||
|
match self.iter.next() {
|
||||||
|
None => None,
|
||||||
|
Some(b'\n') => {
|
||||||
|
self.line += 1;
|
||||||
|
self.col = 0;
|
||||||
|
Some(Ok(b'\n'))
|
||||||
|
}
|
||||||
|
Some(c) => {
|
||||||
|
self.col += 1;
|
||||||
|
Some(Ok(c))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pos(&mut self) -> (usize, usize) {
|
||||||
|
(self.line, self.col)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eof(&mut self) -> Result<bool> {
|
||||||
|
Ok(self.peek()?.is_none())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek_next(&mut self, idx: usize) -> Result<Option<u8>> {
|
||||||
|
while self.ch.len() <= idx {
|
||||||
|
match self.next() {
|
||||||
|
Some(Err(err)) => return Err(Error::Io(err)),
|
||||||
|
Some(Ok(ch)) => self.ch.push(ch),
|
||||||
|
None => return Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Some(self.ch[idx]))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek(&mut self) -> Result<Option<u8>> {
|
||||||
|
self.peek_next(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek_or_null(&mut self) -> Result<u8> {
|
||||||
|
Ok(self.peek()?.unwrap_or(b'\x00'))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eat_char(&mut self) -> u8 {
|
||||||
|
self.ch.remove(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uneat_char(&mut self, ch: u8) {
|
||||||
|
self.ch.insert(0, ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_char(&mut self) -> Result<Option<u8>> {
|
||||||
|
match self.ch.first() {
|
||||||
|
Some(&ch) => {
|
||||||
|
self.eat_char();
|
||||||
|
Ok(Some(ch))
|
||||||
|
}
|
||||||
|
None => match self.next() {
|
||||||
|
Some(Err(err)) => Err(Error::Io(err)),
|
||||||
|
Some(Ok(ch)) => Ok(Some(ch)),
|
||||||
|
None => Ok(None),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_char_or_null(&mut self) -> Result<u8> {
|
||||||
|
Ok(self.next_char()?.unwrap_or(b'\x00'))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eat_line(&mut self) -> Result<()> {
|
||||||
|
loop {
|
||||||
|
match self.peek()? {
|
||||||
|
Some(b'\n') | None => return Ok(()),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
self.eat_char();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_whitespace(&mut self) -> Result<()> {
|
||||||
|
loop {
|
||||||
|
match self.peek_or_null()? {
|
||||||
|
b' ' | b'\n' | b'\t' | b'\r' => {
|
||||||
|
self.eat_char();
|
||||||
|
}
|
||||||
|
b'#' => self.eat_line()?,
|
||||||
|
b'/' => {
|
||||||
|
match self.peek_next(1)? {
|
||||||
|
Some(b'/') => self.eat_line()?,
|
||||||
|
Some(b'*') => {
|
||||||
|
self.eat_char();
|
||||||
|
self.eat_char();
|
||||||
|
while !(self.peek()?.unwrap_or(b'*') == b'*'
|
||||||
|
&& self.peek_next(1)?.unwrap_or(b'/') == b'/')
|
||||||
|
{
|
||||||
|
self.eat_char();
|
||||||
|
}
|
||||||
|
self.eat_char();
|
||||||
|
self.eat_char();
|
||||||
|
}
|
||||||
|
Some(_) => {
|
||||||
|
self.eat_char();
|
||||||
|
}
|
||||||
|
None => return Err(self.error(ErrorCode::TrailingCharacters)), //todo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error(&mut self, reason: ErrorCode) -> Error {
|
||||||
|
Error::Syntax(reason, self.line, self.col)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Number {
|
||||||
|
I64(i64),
|
||||||
|
U64(u64),
|
||||||
|
F64(f64),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ParseNumber<Iter: Iterator<Item = u8>> {
|
||||||
|
rdr: StringReader<Iter>,
|
||||||
|
result: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// macro_rules! try_or_invalid {
|
||||||
|
// ($e:expr) => {
|
||||||
|
// match $e {
|
||||||
|
// Some(v) => v,
|
||||||
|
// None => { return Err(Error::Syntax(ErrorCode::InvalidNumber, 0, 0)); }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl<Iter: Iterator<Item = u8>> ParseNumber<Iter> {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(iter: Iter) -> Self {
|
||||||
|
ParseNumber {
|
||||||
|
rdr: StringReader::new(iter),
|
||||||
|
result: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(&mut self, stop_at_next: bool) -> Result<Number> {
|
||||||
|
match self.try_parse() {
|
||||||
|
Ok(()) => {
|
||||||
|
self.rdr.parse_whitespace()?;
|
||||||
|
|
||||||
|
let mut ch = self.rdr.next_char_or_null()?;
|
||||||
|
|
||||||
|
if stop_at_next {
|
||||||
|
let ch2 = self.rdr.peek_or_null()?;
|
||||||
|
// end scan if we find a punctuator character like ,}] or a comment
|
||||||
|
if ch == b','
|
||||||
|
|| ch == b'}'
|
||||||
|
|| ch == b']'
|
||||||
|
|| ch == b'#'
|
||||||
|
|| ch == b'/' && (ch2 == b'/' || ch2 == b'*')
|
||||||
|
{
|
||||||
|
ch = b'\x00';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match ch {
|
||||||
|
b'\x00' => {
|
||||||
|
let res =
|
||||||
|
str::from_utf8(&self.result).expect("Internal error: json parsing");
|
||||||
|
|
||||||
|
let mut is_float = false;
|
||||||
|
for ch in res.chars() {
|
||||||
|
if ch == '.' || ch == 'e' || ch == 'E' {
|
||||||
|
is_float = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_float {
|
||||||
|
Ok(Number::F64(
|
||||||
|
res.parse::<f64>().expect("Internal error: json parsing"),
|
||||||
|
))
|
||||||
|
} else if res.starts_with('-') {
|
||||||
|
Ok(Number::I64(
|
||||||
|
res.parse::<i64>().expect("Internal error: json parsing"),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(Number::U64(
|
||||||
|
res.parse::<u64>().expect("Internal error: json parsing"),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(Error::Syntax(ErrorCode::InvalidNumber, 0, 0)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_parse(&mut self) -> Result<()> {
|
||||||
|
if self.rdr.peek_or_null()? == b'-' {
|
||||||
|
self.result.push(self.rdr.eat_char());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut has_value = false;
|
||||||
|
|
||||||
|
if self.rdr.peek_or_null()? == b'0' {
|
||||||
|
self.result.push(self.rdr.eat_char());
|
||||||
|
has_value = true;
|
||||||
|
// There can be only one leading '0'.
|
||||||
|
if let b'0'..=b'9' = self.rdr.peek_or_null()? {
|
||||||
|
return Err(Error::Syntax(ErrorCode::InvalidNumber, 0, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match self.rdr.peek_or_null()? {
|
||||||
|
b'0'..=b'9' => {
|
||||||
|
self.result.push(self.rdr.eat_char());
|
||||||
|
has_value = true;
|
||||||
|
}
|
||||||
|
b'.' => {
|
||||||
|
if !has_value {
|
||||||
|
return Err(Error::Syntax(ErrorCode::InvalidNumber, 0, 0));
|
||||||
|
}
|
||||||
|
self.rdr.eat_char();
|
||||||
|
return self.try_decimal();
|
||||||
|
}
|
||||||
|
b'e' | b'E' => {
|
||||||
|
if !has_value {
|
||||||
|
return Err(Error::Syntax(ErrorCode::InvalidNumber, 0, 0));
|
||||||
|
}
|
||||||
|
self.rdr.eat_char();
|
||||||
|
return self.try_exponent();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if !has_value {
|
||||||
|
return Err(Error::Syntax(ErrorCode::InvalidNumber, 0, 0));
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_decimal(&mut self) -> Result<()> {
|
||||||
|
self.result.push(b'.');
|
||||||
|
|
||||||
|
// Make sure a digit follows the decimal place.
|
||||||
|
match self.rdr.next_char_or_null()? {
|
||||||
|
c @ b'0'..=b'9' => {
|
||||||
|
self.result.push(c);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(Error::Syntax(ErrorCode::InvalidNumber, 0, 0));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
while let b'0'..=b'9' = self.rdr.peek_or_null()? {
|
||||||
|
self.result.push(self.rdr.eat_char());
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.rdr.peek_or_null()? {
|
||||||
|
b'e' | b'E' => {
|
||||||
|
self.rdr.eat_char();
|
||||||
|
self.try_exponent()
|
||||||
|
}
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_exponent(&mut self) -> Result<()> {
|
||||||
|
self.result.push(b'e');
|
||||||
|
|
||||||
|
match self.rdr.peek_or_null()? {
|
||||||
|
b'+' => {
|
||||||
|
self.result.push(self.rdr.eat_char());
|
||||||
|
}
|
||||||
|
b'-' => {
|
||||||
|
self.result.push(self.rdr.eat_char());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make sure a digit follows the exponent place.
|
||||||
|
match self.rdr.next_char_or_null()? {
|
||||||
|
c @ b'0'..=b'9' => {
|
||||||
|
self.result.push(c);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(Error::Syntax(ErrorCode::InvalidNumber, 0, 0));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
while let b'0'..=b'9' = self.rdr.peek_or_null()? {
|
||||||
|
self.result.push(self.rdr.eat_char());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
1349
crates/nu-json/src/value.rs
Normal file
1349
crates/nu-json/src/value.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -307,7 +307,7 @@ pub fn format_primitive(primitive: &Primitive, field_name: Option<&String>) -> S
|
|||||||
);
|
);
|
||||||
|
|
||||||
for member in members {
|
for member in members {
|
||||||
f.push_str(".");
|
f.push('.');
|
||||||
f.push_str(&member.display())
|
f.push_str(&member.display())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ use std::cmp::Ordering;
|
|||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::hash::Hasher;
|
use std::hash::Hasher;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
/// A "Text" is like a string except that it can be cheaply cloned.
|
/// A "Text" is like a string except that it can be cheaply cloned.
|
||||||
/// You can also "extract" subtexts quite cheaply. You can also deref
|
/// You can also "extract" subtexts quite cheaply. You can also deref
|
||||||
@ -12,7 +11,7 @@ use std::sync::Arc;
|
|||||||
/// Used to represent the value of an input file.
|
/// Used to represent the value of an input file.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Text {
|
pub struct Text {
|
||||||
text: Arc<String>,
|
text: String,
|
||||||
start: usize,
|
start: usize,
|
||||||
end: usize,
|
end: usize,
|
||||||
}
|
}
|
||||||
@ -39,11 +38,11 @@ impl Text {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Arc<String>> for Text {
|
impl From<&str> for Text {
|
||||||
fn from(text: Arc<String>) -> Self {
|
fn from(text: &str) -> Self {
|
||||||
let end = text.len();
|
let end = text.len();
|
||||||
Self {
|
Self {
|
||||||
text,
|
text: text.to_string(),
|
||||||
start: 0,
|
start: 0,
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
@ -58,20 +57,13 @@ impl AsRef<str> for Text {
|
|||||||
|
|
||||||
impl From<String> for Text {
|
impl From<String> for Text {
|
||||||
fn from(text: String) -> Self {
|
fn from(text: String) -> Self {
|
||||||
Text::from(Arc::new(text))
|
let end = text.len();
|
||||||
|
Self {
|
||||||
|
text,
|
||||||
|
start: 0,
|
||||||
|
end,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&String> for Text {
|
|
||||||
fn from(text: &String) -> Self {
|
|
||||||
Text::from(text.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for Text {
|
|
||||||
fn from(text: &str) -> Self {
|
|
||||||
Text::from(text.to_string())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&Text> for Text {
|
impl From<&Text> for Text {
|
||||||
|
@ -508,7 +508,7 @@ pub fn forgiving_insert_data_at_column_path(
|
|||||||
}
|
}
|
||||||
UnspannedPathMember::Int(int) => {
|
UnspannedPathMember::Int(int) => {
|
||||||
let mut rows = vec![];
|
let mut rows = vec![];
|
||||||
let size = int.to_usize().unwrap_or_else(|| 0);
|
let size = int.to_usize().unwrap_or(0);
|
||||||
|
|
||||||
for _ in 0..=size {
|
for _ in 0..=size {
|
||||||
rows.push(
|
rows.push(
|
||||||
|
@ -149,7 +149,7 @@ impl RenderContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn update(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
pub fn update(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let terminal_size = crossterm::terminal::size().unwrap_or_else(|_| (80, 24));
|
let terminal_size = crossterm::terminal::size().unwrap_or((80, 24));
|
||||||
|
|
||||||
if (self.width != terminal_size.0 as usize) || (self.height != terminal_size.1 as usize) {
|
if (self.width != terminal_size.0 as usize) || (self.height != terminal_size.1 as usize) {
|
||||||
let _ = std::io::stdout().execute(crossterm::cursor::Hide);
|
let _ = std::io::stdout().execute(crossterm::cursor::Hide);
|
||||||
|
@ -119,9 +119,8 @@ impl<'a> Line<'a> {
|
|||||||
.marker(marker)
|
.marker(marker)
|
||||||
.graph_type(GraphType::Line)
|
.graph_type(GraphType::Line)
|
||||||
.style(
|
.style(
|
||||||
Style::default().fg(*DEFAULT_LINE_COLORS
|
Style::default()
|
||||||
.get(idx)
|
.fg(*DEFAULT_LINE_COLORS.get(idx).unwrap_or(&DEFAULT_COLOR)),
|
||||||
.unwrap_or_else(|| &DEFAULT_COLOR)),
|
|
||||||
)
|
)
|
||||||
.data(data_series)
|
.data(data_series)
|
||||||
})
|
})
|
||||||
|
@ -284,7 +284,7 @@ impl SubCommand {
|
|||||||
let formatter = if self.format.is_some() {
|
let formatter = if self.format.is_some() {
|
||||||
let default = String::from("%b-%Y");
|
let default = String::from("%b-%Y");
|
||||||
|
|
||||||
let string_fmt = self.format.as_ref().unwrap_or_else(|| &default);
|
let string_fmt = self.format.as_ref().unwrap_or(&default);
|
||||||
|
|
||||||
Some(nu_data::utils::helpers::date_formatter(
|
Some(nu_data::utils::helpers::date_formatter(
|
||||||
string_fmt.to_string(),
|
string_fmt.to_string(),
|
||||||
@ -331,7 +331,7 @@ impl SubCommand {
|
|||||||
let formatter = if self.format.is_some() {
|
let formatter = if self.format.is_some() {
|
||||||
let default = String::from("%b-%Y");
|
let default = String::from("%b-%Y");
|
||||||
|
|
||||||
let string_fmt = self.format.as_ref().unwrap_or_else(|| &default);
|
let string_fmt = self.format.as_ref().unwrap_or(&default);
|
||||||
|
|
||||||
Some(nu_data::utils::helpers::date_formatter(
|
Some(nu_data::utils::helpers::date_formatter(
|
||||||
string_fmt.to_string(),
|
string_fmt.to_string(),
|
||||||
|
@ -282,7 +282,7 @@ impl SubCommand {
|
|||||||
let formatter = if self.format.is_some() {
|
let formatter = if self.format.is_some() {
|
||||||
let default = String::from("%b-%Y");
|
let default = String::from("%b-%Y");
|
||||||
|
|
||||||
let string_fmt = self.format.as_ref().unwrap_or_else(|| &default);
|
let string_fmt = self.format.as_ref().unwrap_or(&default);
|
||||||
|
|
||||||
Some(nu_data::utils::helpers::date_formatter(
|
Some(nu_data::utils::helpers::date_formatter(
|
||||||
string_fmt.to_string(),
|
string_fmt.to_string(),
|
||||||
@ -329,7 +329,7 @@ impl SubCommand {
|
|||||||
let formatter = if self.format.is_some() {
|
let formatter = if self.format.is_some() {
|
||||||
let default = String::from("%b-%Y");
|
let default = String::from("%b-%Y");
|
||||||
|
|
||||||
let string_fmt = self.format.as_ref().unwrap_or_else(|| &default);
|
let string_fmt = self.format.as_ref().unwrap_or(&default);
|
||||||
|
|
||||||
Some(nu_data::utils::helpers::date_formatter(
|
Some(nu_data::utils::helpers::date_formatter(
|
||||||
string_fmt.to_string(),
|
string_fmt.to_string(),
|
||||||
|
@ -13,7 +13,7 @@ impl TextView {
|
|||||||
|
|
||||||
#[allow(clippy::cognitive_complexity)]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
pub fn view_text_value(value: &Value) {
|
pub fn view_text_value(value: &Value) {
|
||||||
let (mut term_width, _) = term_size::dimensions().unwrap_or_else(|| (80, 20));
|
let (mut term_width, _) = term_size::dimensions().unwrap_or((80, 20));
|
||||||
let mut tab_width: u64 = 4;
|
let mut tab_width: u64 = 4;
|
||||||
let mut colored_output = true;
|
let mut colored_output = true;
|
||||||
let mut true_color = true;
|
let mut true_color = true;
|
||||||
|
Loading…
Reference in New Issue
Block a user