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:
Jonathan Turner 2020-11-22 13:37:16 +13:00 committed by GitHub
parent 63d4df9810
commit 930f9f0063
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 5176 additions and 952 deletions

1788
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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")]

View File

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

View File

@ -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
View 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
View 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.

View 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
View 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
View 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>;

View 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
View 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

File diff suppressed because it is too large Load Diff

328
crates/nu-json/src/util.rs Normal file
View 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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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