Skip to content

Commit 82d8635

Browse files
authored
Refactor (#12)
* keep lib.rs simple * refactor project structure * use stable and check all * fix wasm
1 parent df2e0c3 commit 82d8635

File tree

11 files changed

+238
-197
lines changed

11 files changed

+238
-197
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ jobs:
1919
uses: actions-rs/toolchain@v1
2020
with:
2121
profile: minimal
22-
toolchain: nightly-2019-12-04
22+
toolchain: stable
2323
components: rustfmt
2424
target: wasm32-unknown-unknown
2525
default: true
2626
- name: Check format
2727
run: cargo fmt --all -- --check
2828
- name: Check Wasm
29-
run: cargo check --no-default-features --target=wasm32-unknown-unknown
29+
run: cargo check --no-default-features --target=wasm32-unknown-unknown --all
3030
- name: Build
3131
run: cargo build --locked
3232
- name: Run tests

Cargo.toml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,18 @@ categories = [
1212
]
1313

1414
[dependencies]
15-
paste = "0.1"
15+
lite-parser = { path = "parser", default-features = false }
1616
num-traits = { version = "0.2", optional = true, default-features = false }
1717

1818
[features]
1919
default = ["std"]
20-
std = []
20+
std = [
21+
"lite-parser/std"
22+
]
2123
# Enables converting values to floats in no-`std` environment
2224
float = ["num-traits"]
25+
26+
[workspace]
27+
members = [
28+
"parser"
29+
]

parser/Cargo.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
name = "lite-parser"
3+
version = "0.1.0"
4+
authors = ["Bryan Chen <[email protected]>"]
5+
description = "Simple parser library. Wasm / no_std ready."
6+
license = "Apache-2.0"
7+
edition = "2018"
8+
keywords = [ "parser", "wasm", "no_std" ]
9+
repository = "https://github.com/xlc/lite-json"
10+
categories = [
11+
"no-std",
12+
]
13+
14+
[dependencies]
15+
paste = "0.1"
16+
17+
[features]
18+
default = ["std"]
19+
std = []

src/impls.rs renamed to parser/src/impls.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use crate::parser::{Error, Position};
1+
use crate::traits::{Error, Position};
22

33
#[cfg(not(feature = "std"))]
44
extern crate alloc;
55

66
#[cfg(not(feature = "std"))]
7-
use crate::parser::alloc::fmt::Formatter;
7+
use alloc::fmt::Formatter;
88
#[cfg(not(feature = "std"))]
99
use alloc::vec::Vec;
1010

parser/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pub mod impls;
2+
pub mod parser;
3+
pub mod traits;
4+
pub use paste;

src/parser.rs renamed to parser/src/parser.rs

Lines changed: 6 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,12 @@
1+
#[cfg(not(feature = "std"))]
12
pub extern crate alloc;
23

34
use crate::impls::{SimpleError, SimplePosition};
5+
use crate::traits::{Error, Input, Position, ResultOf};
46
#[cfg(not(feature = "std"))]
57
use alloc::vec::Vec;
68
use core::marker::PhantomData;
79

8-
pub trait Position: core::ops::Sub<Self, Output = i32> + Copy {
9-
fn index(&self) -> u32;
10-
fn line(&self) -> u32;
11-
fn column(&self) -> u32;
12-
}
13-
14-
pub trait Error {
15-
type Position;
16-
fn reasons(&self) -> &[(Self::Position, &'static str)];
17-
fn add_reason(self, position: Self::Position, reason: &'static str) -> Self;
18-
}
19-
20-
pub trait Input: Default {
21-
type Position: Position;
22-
type Error: Error<Position = Self::Position>;
23-
fn next(&self, pos: Self::Position) -> Result<(char, Self::Position), Self::Error>;
24-
fn next_range(
25-
&self,
26-
start: Self::Position,
27-
counts: u32,
28-
) -> Result<(&str, Self::Position), Self::Error>;
29-
fn error_at(&self, pos: Self::Position, reason: &'static str) -> Self::Error;
30-
fn is_end(&self, pos: Self::Position) -> bool;
31-
}
32-
33-
pub type ResultOf<I, O> = Result<(O, <I as Input>::Position), <I as Input>::Error>;
34-
3510
#[cfg_attr(feature = "std", derive(Debug, PartialEq, Eq))]
3611
#[derive(Clone)]
3712
pub struct ParserOptions {
@@ -264,7 +239,7 @@ macro_rules! literals {
264239
$( #[ $attr:meta ] )*
265240
$vis:vis $name:ident => $($value:tt)+
266241
) => (
267-
paste::item! {
242+
$crate::paste::item! {
268243
$vis struct [< $name Predicate >];
269244
impl $crate::parser::Predicate<char> for [< $name Predicate >] {
270245
fn eval(c: &char) -> bool {
@@ -291,11 +266,11 @@ macro_rules! parsers {
291266
) => {
292267
$(
293268
$vis struct $name;
294-
impl<I: $crate::parser::Input> $crate::parser::Parser<I> for $name {
269+
impl<I: $crate::traits::Input> $crate::parser::Parser<I> for $name {
295270
type Output = $output_type;
296-
fn parse(input: &I, current: I::Position, context: &ParserContext) -> $crate::parser::ResultOf<I, Self::Output> {
271+
fn parse(input: &I, current: I::Position, context: &ParserContext) -> $crate::traits::ResultOf<I, Self::Output> {
297272
let ($output, pos) = <$type as $crate::parser::Parser<I>>::parse(input, current, context)
298-
.map_err(|e| <I::Error as $crate::parser::Error>::add_reason(e, current, stringify!($name)))?;
273+
.map_err(|e| <I::Error as $crate::traits::Error>::add_reason(e, current, stringify!($name)))?;
299274
let res = $body;
300275
Ok((res, pos))
301276
}

parser/src/traits.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
pub trait Position: core::ops::Sub<Self, Output = i32> + Copy {
2+
fn index(&self) -> u32;
3+
fn line(&self) -> u32;
4+
fn column(&self) -> u32;
5+
}
6+
7+
pub trait Error {
8+
type Position;
9+
fn reasons(&self) -> &[(Self::Position, &'static str)];
10+
fn add_reason(self, position: Self::Position, reason: &'static str) -> Self;
11+
}
12+
13+
pub trait Input: Default {
14+
type Position: Position;
15+
type Error: Error<Position = Self::Position>;
16+
fn next(&self, pos: Self::Position) -> Result<(char, Self::Position), Self::Error>;
17+
fn next_range(
18+
&self,
19+
start: Self::Position,
20+
counts: u32,
21+
) -> Result<(&str, Self::Position), Self::Error>;
22+
fn error_at(&self, pos: Self::Position, reason: &'static str) -> Self::Error;
23+
fn is_end(&self, pos: Self::Position) -> bool;
24+
}
25+
26+
pub type ResultOf<I, O> = Result<(O, <I as Input>::Position), <I as Input>::Error>;

src/json.rs

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use alloc::vec::Vec;
77
#[cfg(not(feature = "std"))]
88
use alloc::string::ToString;
99

10+
use crate::traits::Serialize;
11+
1012
#[cfg_attr(feature = "std", derive(Debug))]
1113
#[derive(Clone, PartialEq)]
1214
pub struct NumberValue {
@@ -48,20 +50,6 @@ pub enum JsonValue {
4850
Null,
4951
}
5052

51-
pub trait Serialize {
52-
fn serialize(&self) -> Vec<u8> {
53-
let mut res = Vec::new();
54-
self.serialize_to(&mut res, 0, 0);
55-
res
56-
}
57-
fn format(&self, indent: u32) -> Vec<u8> {
58-
let mut res = Vec::new();
59-
self.serialize_to(&mut res, indent, 0);
60-
res
61-
}
62-
fn serialize_to(&self, buffer: &mut Vec<u8>, indent: u32, level: u32);
63-
}
64-
6553
impl Serialize for NumberValue {
6654
fn serialize_to(&self, buffer: &mut Vec<u8>, _indent: u32, _level: u32) {
6755
buffer.extend_from_slice(self.integer.to_string().as_bytes());

src/json_parser.rs

Lines changed: 145 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,16 @@ extern crate alloc;
55
use alloc::vec::Vec;
66

77
use crate::json::{JsonObject, JsonValue, NumberValue};
8-
use crate::parser::{
9-
Concat, Concat3, Either, Error, Input, OneOf, OneOrMore, Parser, ParserContext, ResultOf,
10-
ZeroOrMore, ZeroOrOne,
8+
use lite_parser::{
9+
impls::SimpleError,
10+
literals,
11+
parser::{
12+
Concat, Concat3, Either, OneOf, OneOrMore, Parser, ParserContext, ParserOptions,
13+
ZeroOrMore, ZeroOrOne,
14+
},
15+
parsers,
16+
traits::{Error, Input, ResultOf},
1117
};
12-
use crate::{literals, parsers};
1318

1419
use core::convert::TryInto;
1520

@@ -349,3 +354,139 @@ impl<I: Input> Parser<I> for Json {
349354
}
350355
}
351356
}
357+
358+
pub fn parse_json(input: &str) -> Result<JsonValue, SimpleError> {
359+
parse_json_with_options(input, Default::default())
360+
}
361+
362+
pub fn parse_json_with_options(
363+
input: &str,
364+
options: ParserOptions,
365+
) -> Result<JsonValue, SimpleError> {
366+
Json::parse(&input, Default::default(), &ParserContext::new(options)).map(|(ret, _)| ret)
367+
}
368+
369+
#[cfg(test)]
370+
mod tests {
371+
use super::*;
372+
use crate::NumberValue;
373+
use lite_parser::impls::SimplePosition;
374+
375+
#[test]
376+
fn it_works() {
377+
assert_eq!(
378+
parse_json(&r#"{ "test": 1, "test2": [1e-4, 2.041e2, true, false, null, "\"1\n\""] }"#),
379+
Ok(JsonValue::Object(vec![
380+
(
381+
vec!['t', 'e', 's', 't'],
382+
JsonValue::Number(NumberValue {
383+
integer: 1,
384+
fraction: 0,
385+
fraction_length: 0,
386+
exponent: 0
387+
})
388+
),
389+
(
390+
vec!['t', 'e', 's', 't', '2'],
391+
JsonValue::Array(vec![
392+
JsonValue::Number(NumberValue {
393+
integer: 1,
394+
fraction: 0,
395+
fraction_length: 0,
396+
exponent: -4,
397+
}),
398+
JsonValue::Number(NumberValue {
399+
integer: 2,
400+
fraction: 41,
401+
fraction_length: 3,
402+
exponent: 2,
403+
}),
404+
JsonValue::Boolean(true),
405+
JsonValue::Boolean(false),
406+
JsonValue::Null,
407+
JsonValue::String(vec!['\"', '1', 'n', '\"'])
408+
])
409+
)
410+
]))
411+
)
412+
}
413+
414+
#[test]
415+
fn it_should_consume_all() {
416+
assert_eq!(
417+
parse_json(&r#""1"a"#),
418+
Err(SimpleError {
419+
reasons: vec![(
420+
SimplePosition {
421+
index: 3,
422+
line: 0,
423+
column: 3
424+
},
425+
"Expect end of input"
426+
)]
427+
})
428+
)
429+
}
430+
431+
#[test]
432+
fn it_accepts_nest_level() {
433+
assert_eq!(
434+
parse_json_with_options(
435+
&r#"{ "test": 1 }"#,
436+
ParserOptions {
437+
max_nest_level: Some(1)
438+
}
439+
),
440+
Ok(JsonValue::Object(vec![(
441+
vec!['t', 'e', 's', 't'],
442+
JsonValue::Number(NumberValue {
443+
integer: 1,
444+
fraction: 0,
445+
fraction_length: 0,
446+
exponent: 0
447+
})
448+
),]))
449+
);
450+
}
451+
452+
#[test]
453+
fn it_accepts_more_nest_level() {
454+
assert_eq!(
455+
parse_json_with_options(
456+
&r#"{ "test": { "a": [ {} ] } }"#,
457+
ParserOptions {
458+
max_nest_level: Some(5)
459+
}
460+
),
461+
Ok(JsonValue::Object(vec![(
462+
vec!['t', 'e', 's', 't'],
463+
JsonValue::Object(vec![(
464+
vec!['a'],
465+
JsonValue::Array(vec![JsonValue::Object(vec![])])
466+
)])
467+
)]))
468+
);
469+
}
470+
471+
#[test]
472+
fn it_error_on_too_deep_nest() {
473+
assert_eq!(
474+
parse_json_with_options(
475+
&r#"{ "test": { "a": [ {} ] } }"#,
476+
ParserOptions {
477+
max_nest_level: Some(3)
478+
}
479+
),
480+
Err(SimpleError {
481+
reasons: vec![(
482+
SimplePosition {
483+
index: 0,
484+
line: 0,
485+
column: 0
486+
},
487+
"Value"
488+
)]
489+
})
490+
);
491+
}
492+
}

0 commit comments

Comments
 (0)