├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── RELEASE.md ├── TODO.md ├── benches └── json_bench.rs ├── check.sh ├── docs ├── Posits4.pdf ├── _archive │ ├── PITCHME.md │ └── PITCHME.yaml ├── rfc6901-jptr.txt └── rfc8259-json.txt ├── examples ├── macro.rs └── mixed_integer.rs ├── jsondata-derive ├── Cargo.toml ├── LICENSE └── src │ └── lib.rs ├── perf.sh ├── rustfmt.toml ├── src ├── error.rs ├── jptr.rs ├── jptr_test.rs ├── json.rs ├── json_qc.rs ├── json_test.rs ├── jsons.rs ├── jsons_test.rs ├── lex.rs ├── lib.rs ├── num.rs ├── ops.rs ├── ops_test.rs ├── parse.rs └── property.rs ├── template ├── css │ └── PITCHME.css ├── img │ ├── batman.png │ ├── bg │ │ ├── black.jpg │ │ ├── blue.jpg │ │ ├── gray.jpg │ │ ├── green.jpg │ │ ├── orange.jpg │ │ ├── pink.jpg │ │ ├── purple.jpg │ │ ├── white.jpg │ │ └── yellow.jpg │ ├── cityscape.png │ ├── contact-1.png │ ├── contact-2.png │ ├── dataflow.png │ ├── design.png │ ├── developer.jpg │ ├── einstein.png │ ├── friday.gif │ ├── geek.gif │ ├── geek.png │ ├── grass.png │ ├── headphones.jpg │ ├── logo.png │ ├── lovelace.jpg │ ├── moon.jpg │ ├── olaf.png │ ├── pencils.jpg │ ├── phone.jpg │ ├── presenter.jpg │ ├── profile │ │ ├── abby.jpg │ │ ├── berry.jpg │ │ ├── ronny.jpg │ │ └── wendy.jpg │ ├── questions-1.png │ ├── questions-2.png │ ├── questions-3.png │ ├── questions-4.png │ ├── questions2.png │ ├── quotes.jpg │ ├── ribbon.png │ ├── snowman.gif │ ├── spotlight.png │ ├── thanks.jpg │ └── tip.png ├── jpt-eg.png ├── md │ ├── about │ │ └── PITCHME.md │ ├── announcement │ │ └── PITCHME.md │ ├── boxed-text │ │ └── PITCHME.md │ ├── code-presenting │ │ └── PITCHME.md │ ├── header-footer │ │ └── PITCHME.md │ ├── image │ │ └── PITCHME.md │ ├── list-content │ │ └── PITCHME.md │ ├── quotation │ │ └── PITCHME.md │ ├── sidebar │ │ └── PITCHME.md │ ├── sidebox │ │ └── PITCHME.md │ ├── split-screen │ │ └── PITCHME.md │ └── wrap-up │ │ └── PITCHME.md └── src │ └── go │ └── server.go └── testdata ├── qc_strings.jsons ├── stream1.jsons ├── stream11.jsons ├── stream2.jsons ├── stream3.jsons ├── test_simple.jsons ├── test_simple.jsons.ref └── test_simple.jsons.ref.jsons /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | target 3 | Cargo.lock 4 | .vimsession 5 | tarpaulin-report.html 6 | core 7 | test.out 8 | flamegraph.svg 9 | perf.out 10 | check.out 11 | perf.data 12 | perf.data.old 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | matrix: 4 | include: 5 | - rust: stable 6 | script: 7 | - cargo +stable build --verbose 8 | - cargo +stable doc 9 | - rust: nightly 10 | before_script: 11 | - cargo +nightly install cargo-audit 12 | script: 13 | - cargo +nightly build --verbose 14 | - cargo +nightly audit 15 | - cargo +nightly test --verbose 16 | - cargo +nightly bench --verbose 17 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "jsondata" 3 | version = "0.8.1" 4 | description = "JSON processing package for document databases" 5 | repository = "https://github.com/bnclabs/jsondata" 6 | documentation = "https://docs.rs/jsondata" 7 | keywords = ["json", "json5"] 8 | categories = ["parsing", "encoding"] 9 | authors = ["prataprc "] 10 | license = "MIT" 11 | edition = "2021" 12 | readme = "README.md" 13 | exclude = ["template/**"] 14 | 15 | [profile.release] 16 | debug = true 17 | 18 | [profile.bench] 19 | debug = true 20 | 21 | [lib] 22 | name = "jsondata" 23 | 24 | [dependencies] 25 | lazy_static = "1.2.0" 26 | unicode_reader = "0.1.1" 27 | jsondata-derive = { path = "jsondata-derive", version = "=0.1.0"} 28 | 29 | [dev-dependencies] 30 | quickcheck = "1.0.3" 31 | 32 | [badges] 33 | maintenance = { status = "actively-developed" } 34 | travis-ci = { repository = "bnclabs/jsondata" } 35 | 36 | [[example]] 37 | name = "macro" 38 | crate-type = ["bin"] 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | # ... build ... 3 | cargo +stable build 4 | cd jsondata-derive; cargo +stable build 5 | cargo +nightly build 6 | cd jsondata-derive; cargo +nightly build 7 | # 8 | # ... test ... 9 | cargo +stable test --no-run 10 | cd jsondata-derive; cargo +stable test --no-run 11 | cargo +nightly test --no-run 12 | cd jsondata-derive; cargo +nightly test --no-run 13 | # 14 | # ... bench ... 15 | cargo +nightly bench --no-run 16 | cd jsondata-derive; cargo +nightly bench --no-run 17 | # 18 | # ... doc ... 19 | cargo +stable doc 20 | cd jsondata-derive; cargo +stable bench --no-run 21 | cargo +nightly doc 22 | cd jsondata-derive; cargo +nightly bench --no-run 23 | # 24 | # ... meta commands ... 25 | cargo +nightly clippy --all-targets --all-features 26 | cd jsondata-derive; cargo +nightly clippy --all-targets --all-features 27 | 28 | test: 29 | # ... test stable ... 30 | cargo +stable test 31 | cd jsondata-derive; cargo +stable test 32 | cargo +stable run --example macro 33 | cargo +stable run --example mixed_integer 34 | # ... test nightly ... 35 | cargo +nightly test 36 | cd jsondata-derive; cargo +nightly test 37 | cargo +nightly run --example macro 38 | cargo +nightly run --example mixed_integer 39 | 40 | bench: 41 | # ... bench stable ... 42 | # TODO: cargo +stable bench 43 | # TODO: cd jsondata-derive; cargo +stable bench 44 | # ... bench nightly ... 45 | cargo +nightly bench 46 | cd jsondata-derive; cargo +nightly bench 47 | 48 | flamegraph: 49 | echo "not an executable" 50 | 51 | prepare: build test bench 52 | check.sh check.out 53 | perf.sh perf.out 54 | 55 | clean: 56 | cargo clean 57 | rm -f check.out perf.out flamegraph.svg perf.data perf.data.old 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Why yet another JSON package in Rust ? 2 | ====================================== 3 | 4 | [![Rustdoc](https://img.shields.io/badge/rustdoc-hosted-blue.svg)](https://docs.rs/jsondata) 5 | [![GitPitch](https://gitpitch.com/assets/badge.svg)](https://gitpitch.com/bnclabs/jsondata/master?grs=github) 6 | [![Build Status](https://travis-ci.org/bnclabs/jsondata.svg?branch=master)](https://travis-ci.org/bnclabs/jsondata) 7 | 8 | This crate makes several trade-offs that are tuned for big-data 9 | and document database. 10 | 11 | * [x] Support for 128-bit signed integers. 12 | * [x] Deferred conversion for JSON numbers. 13 | * [x] Serialization from Rust native type to JSON text. 14 | * [x] De-serialization from JSON text to Rust native type. 15 | * [x] CRUD operation on JSON documents, using [JSON Pointer][jptr]. 16 | * [x] Sorted keys in property object. 17 | * [x] Streaming JSON parser. 18 | * [x] Support [JSON5](http://json5.org) standard. 19 | * [x] Common arithmetic and logic operations. 20 | * [x] Sortable JSON. 21 | 22 | **Useful links** 23 | 24 | * **[API Documentation](https://docs.rs/jsondata)** 25 | * [JSON Pointer][jptr]. 26 | * [JSON5][json5]. 27 | * Rust [internal discussion][rust1] on f64 -> integer. 28 | * [Json sort order][json-sort-order]. 29 | * [Json operations][json-ops]. 30 | * [Release notes](./RELEASE.md). 31 | 32 | Deferred conversion for numbers 33 | =============================== 34 | 35 | Converting JSON numbers to Rust native type is not always desired. Especially 36 | in the context of big-data where data is stored in JSON format and we need to 37 | lookup, only, specific fields within the document. 38 | 39 | This implementation provides deferred conversion for JSON numbers that leads 40 | to a **[performance improvement of upto 30%][commit-deferred]**. 41 | 42 | **Caveat**: If numerical text is greated than 128 characters, deferred conversion 43 | won't work, that is, text shall be parsed immediately. 44 | 45 | CRUD operations on JSON document 46 | ================================ 47 | 48 | Using Json Pointer it is possible to identify a specific field nested within 49 | a JSON document. For Example, with below document: 50 | 51 | ```json 52 | { 53 | "age": 26, 54 | "eyeColor": "green", 55 | "name": "Leon Robertson", 56 | "gender": "male", 57 | "company": "AEORA", 58 | "phone": "+1 (835) 447-2960", 59 | "tags": [ "officia", "reprehenderit", "magna" ], 60 | "friends": [ 61 | { 62 | "id": 0, 63 | "name": "Glenda Chan" 64 | } 65 | ] 66 | } 67 | ``` 68 | 69 | * **/age** shall point to value ``26``. 70 | * **/tags** shall point to value ``[ "officia", "reprehenderit", "magna" ]``. 71 | * **/tags/0** shall point to value ``"officia"``. 72 | * **/friends** shall point to value ``[{"id": 0, "name": "Glenda Chan"}]``. 73 | * **/friends/name** shall point to value ``"Glenda Chan"``. 74 | 75 | **List of operations** 76 | 77 | * [x] Get a field nested within a JSON document using [JSON Pointer][jptr]. 78 | * [x] Set a field nested within a JSON document. 79 | * [x] Delete a field nested within a JSON document. 80 | * [x] Append string or array field withing a JSON document. 81 | 82 | JSON5 83 | ===== 84 | 85 | * [x] Object keys may be an ECMAScript 5.1 IdentifierName. 86 | * [x] Objects may have a single trailing comma. 87 | * [x] Arrays may have a single trailing comma. 88 | * [ ] Strings may be single quoted. 89 | * [ ] Strings may span multiple lines by escaping new line characters. 90 | * [ ] Strings may include character escapes. 91 | * [x] Numbers may be hexadecimal. 92 | * [x] Numbers may have a leading or trailing decimal point. 93 | * [x] Numbers may be IEEE 754 positive infinity, negative infinity, and NaN. 94 | * [x] Numbers may begin with an explicit plus sign. 95 | * [ ] Single and multi-line comments are allowed. 96 | * [x] Additional white space characters are allowed. 97 | 98 | **[Track this feature](https://github.com/bnclabs/jsondata/issues/4)**. 99 | 100 | Sortable JSON 101 | ============= 102 | 103 | * **Null** type shall sort before all other types. 104 | * **Boolean** type shall sort after Null type. 105 | * **Number** type shall sort after Boolean type. 106 | * f64 values that are <= -2^127 will sort before all i128 integers. 107 | * f64 values that are >= 2^127-1 will sort after all i128 integers. 108 | * NaN, Not a Number, values shall sort after all i128 integers 109 | * **-Infinity** shall sort before all numbers. 110 | * **+Infinity** shall sort after all numbers. 111 | * **NaN** shall sort after +Infinity. 112 | * **String** type shall sort after Number type. 113 | * **Array** type shall sort after String type. 114 | * **Object** type shall sort after Array type. 115 | * All (key,value) pairs within the object shall be presorted based 116 | on the key. 117 | * When comparing two objects, comparison shall start from first key 118 | and proceed to the last key. 119 | * If two keys are equal at a given position within the objects, then 120 | its corresponding values shall be compared. 121 | * When one object is a subset of another object, as in, if one object 122 | contain all the (key,value) properties that the other object has 123 | then it shall sort before the other object. 124 | 125 | **Useful links** 126 | 127 | - **[A detailed description of JSON sort order][json-sort-order]**. 128 | - Rust-lang [issue#46298](https://github.com/rust-lang/rust/issues/46298) and 129 | [issue#10184](https://github.com/rust-lang/rust/issues/10184), 130 | discussing saturating cast of f64 -> integer. 131 | - Rust [internal discussion][rust1] on f64 -> integer. 132 | - Unicode collation [TR10](http://unicode.org/reports/tr10). 133 | - [ICU collation](http://userguide.icu-project.org/collation). 134 | - Floating point [Total ordering][fp-total-order] 135 | - Total ordering for floating point in [stackoverflow][sf1]. 136 | - [Total ordering thread](https://users.rust-lang.org/t/sort-order-for-json/24166) 137 | in http://users.rust-lang.org. 138 | - A good [blog][blog1] on floating point, to get started. 139 | 140 | Operations on JSON documents 141 | ============================ 142 | 143 | * Arithmetic operations, ADD, SUB, MUL, DIV, REM, NEG. 144 | * Bitwise operations, SHL, SHR, BITAND, BITOR, BITXOR. 145 | * Logical operations, NOT, AND, OR. 146 | * Index operations. 147 | * Range operations. 148 | 149 | **[Detailed description can be found here][json-ops].** 150 | 151 | Contribution 152 | ------------ 153 | 154 | * Simple workflow. Fork - Modify - Pull request. 155 | * Before creating a PR, 156 | * Run `make build` to confirm all versions of build is passing with 157 | 0 warnings and 0 errors. 158 | * Run `check.sh` with 0 warnings, 0 errors and all testcases passing. 159 | * Run `perf.sh` with 0 warnings, 0 errors and all testcases passing. 160 | * [Install][spellcheck] and run `cargo spellcheck` to remove common spelling mistakes. 161 | * [Developer certificate of origin][dco] is preferred. 162 | 163 | [commit-deferred]: https://github.com/bnclabs/jsondata/commit/70e6dedf0121f16e130f224daaa23948f5a5d782 164 | [json5]: http://json5.org 165 | [jptr]: https://tools.ietf.org/html/rfc6901 166 | [#1]: https://github.com/bnclabs/jsondata/issues/1 167 | [#3]: https://github.com/bnclabs/jsondata/issues/3 168 | [#4]: https://github.com/bnclabs/jsondata/issues/4 169 | [#13]: https://github.com/bnclabs/jsondata/issues/13 170 | [json-sort-order]: https://prataprc.github.io/json-sort-order.html 171 | [json-ops]: https://prataprc.github.io/json-operations.html 172 | [rust1]: https://internals.rust-lang.org/t/help-us-benchmark-saturating-float-casts/6231 173 | [sf1]: https://stackoverflow.com/questions/8341395/what-is-a-subnormal-floating-point-number 174 | [fp-total-order]: https://en.m.wikipedia.org/wiki/IEEE_754#Total-ordering_predicate 175 | [blog1]: https://steve.hollasch.net/cgindex/coding/ieeefloat.html 176 | [MIT license]: https://opensource.org/licenses/MIT 177 | [spellcheck]: https://github.com/drahnr/cargo-spellcheck 178 | [dco]: https://developercertificate.org/ 179 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | 0.8.1 2 | ===== 3 | 4 | * Instead of using `val.into()` and `val.try_into()` to convert between types, 5 | use `::from(val)` and `::try_from(val)` to convert between types. 6 | * Remove `#![feature(total_cmp)]` 7 | * Now `jsondata` is available on stable release channel. 8 | * Make Integral::Text and Floating::Text to hold onto 128 character number-text. 9 | Anything greater shall be parsed, that is, deferred conversion won't work 10 | for numerical text greater than 128 characters. 11 | * Cleanup 12 | 13 | Note: valgrind still doesn't work on mac-m1. 14 | 15 | 0.8.0 16 | ===== 17 | 18 | * **Breaking change**: Json method rename `typename()` method to `type_name()`. 19 | * implement `err_at!()` macro and Debug for Error type. 20 | * parse: Json parser, test fix and refactoring. 21 | * optimize `parse_identifier()` implementation. 22 | * fix testcase for parsing float number. 23 | * refactor `num.rs`. 24 | * replace `search_by_key()` with `[T].binary_search_by()` for `Property` array. 25 | `stdlib` has an equivalent implementation to find a key in a sorted 26 | array. Use that instead of `search_by_key()`. 27 | * ops.rs: where possible capture error instead of panic. 28 | * reimplement traits for Json type using `macro_rules`. 29 | * rustdoc. 30 | * update CI scripts. 31 | 32 | 0.7.0 33 | ===== 34 | 35 | * move license back to MIT 36 | * Json type: export typename() API. 37 | * implement conversion traits from primitive types to Json. 38 | * Error type: implement Display trait. 39 | * Error type: add InvalidType enum-variant. 40 | * JsonSerialize: procedural macro. 41 | * idiomatic rust API. 42 | * clippy fixes. 43 | 44 | 0.6.2 45 | ===== 46 | 47 | Breaking type change. 48 | - Integral and Floating types are implemented as enum, instead of struct. 49 | 50 | - Fix "as ..." conversions to `try_into().unwrap()` 51 | - Implement From, From, From for Json type. 52 | 53 | 0.6.1 54 | ===== 55 | 56 | - rustdoc 57 | - clippy fixes 58 | 59 | 0.6.0 60 | ===== 61 | 62 | Breaking API Change: 63 | 64 | Previously APIs returning Result had its Err variant as String type. 65 | Now we are implementing a proper Error type and returning that as the 66 | Err variant. 67 | 68 | 0.5.0 69 | ===== 70 | 71 | - Documentation. 72 | - Move license to AGPL-3.0 73 | 74 | 0.4.0 75 | ===== 76 | 77 | * Streaming JSON parser. 78 | * Fixes to travis, stable and nightly rust channels. 79 | * Implement PartialOrd for Json type. 80 | * Total ordering for Json type. 81 | * Implement Arithmetic/logical/bitwise traits for Json type. 82 | * Implement Range operation for Json type. 83 | 84 | 0.3.0 85 | ===== 86 | 87 | * JSON5 support. Most of JSON5 specification, especially those 88 | that are relevant for big-data document database, are implemented. 89 | * Added release-checklist 90 | * Bug fixes 91 | * Implement AsRef and AsMut traits for Json type. 92 | * Travis-CI integration for ``clippy``. 93 | 94 | 0.2.0 95 | ===== 96 | 97 | * CRUD operation on JSON documents, using JSON Pointer. 98 | 99 | 0.1.0 100 | ===== 101 | 102 | * Support for 128-bit signed integers. 103 | * Deferred conversion for JSON numbers. 104 | * Serialization from Rust native type to JSON text. 105 | * De-serialization from JSON text to Rust native type. 106 | * Sorted keys in property object. 107 | 108 | Code Review checklist 109 | ===================== 110 | 111 | * [ ] Review and check for un-necessary copy, and allocations. 112 | * [ ] Review resize calls on `Vec`. 113 | * [ ] Review (as ...) type casting, to panic on data loss. 114 | * [ ] Reduce trait constraints for Type parameters on public APIs. 115 | * [ ] Public APIs can be as generic as possible. Check whether there 116 | is a scope for `AsRef` or `Borrow` constraints. 117 | * [ ] Document error variants. 118 | * [ ] Check for dangling links in rustdoc. 119 | 120 | Refer to [release-checklist][release-checklist]. 121 | 122 | [release-checklist]: https://prataprc.github.io/rust-crates-release-checklist.html 123 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | * rename procedural macro to something short and nice. 2 | * jsondata-derive and jsondata version/releases should be in-sync with each other 3 | * Use arbitrary based test case generation and fuzzy testing. 4 | -------------------------------------------------------------------------------- /benches/json_bench.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 R Pratap Chakravarthy. 2 | 3 | #![feature(test)] 4 | extern crate test; 5 | 6 | use std::fmt::Write; 7 | use test::Bencher; 8 | 9 | use jsondata::Json; 10 | 11 | #[bench] 12 | fn bench_null(b: &mut Bencher) { 13 | b.iter(|| "null".parse::().unwrap()); 14 | } 15 | 16 | #[bench] 17 | fn bench_bool(b: &mut Bencher) { 18 | b.iter(|| "false".parse::().unwrap()); 19 | } 20 | 21 | #[bench] 22 | fn bench_num(b: &mut Bencher) { 23 | b.iter(|| "123121.2234234".parse::().unwrap()); 24 | } 25 | 26 | #[bench] 27 | fn bench_hexnum(b: &mut Bencher) { 28 | b.iter(|| "0x1235abcd".parse::().unwrap()); 29 | } 30 | 31 | #[bench] 32 | fn bench_string(b: &mut Bencher) { 33 | let s = r#""汉语 / 漢語; Hàn\b \tyǔ ""#; 34 | b.iter(|| s.parse::().unwrap()); 35 | } 36 | 37 | #[bench] 38 | fn bench_array(b: &mut Bencher) { 39 | let s = r#" [null,true,false,10,"tru\"e"]"#; 40 | b.iter(|| s.parse::().unwrap()); 41 | } 42 | 43 | #[bench] 44 | fn bench_map(b: &mut Bencher) { 45 | let s = r#"{"a": null,"b":true,"c":false,"d\"":-10E-1,"e":"tru\"e"}"#; 46 | b.iter(|| s.parse::().unwrap()); 47 | } 48 | 49 | #[bench] 50 | fn bench_null_to_json(b: &mut Bencher) { 51 | let val = "null".parse::().unwrap(); 52 | let mut outs = String::with_capacity(64); 53 | b.iter(|| { 54 | outs.clear(); 55 | write!(outs, "{}", val) 56 | }); 57 | } 58 | 59 | #[bench] 60 | fn bench_bool_to_json(b: &mut Bencher) { 61 | let val = "false".parse::().unwrap(); 62 | let mut outs = String::with_capacity(64); 63 | b.iter(|| { 64 | outs.clear(); 65 | write!(outs, "{}", val) 66 | }); 67 | } 68 | 69 | #[bench] 70 | fn bench_num_to_json(b: &mut Bencher) { 71 | let val = "10.2".parse::().unwrap(); 72 | let mut outs = String::with_capacity(64); 73 | b.iter(|| { 74 | outs.clear(); 75 | write!(outs, "{}", val) 76 | }); 77 | } 78 | 79 | #[bench] 80 | fn bench_string_to_json(b: &mut Bencher) { 81 | let inp = r#""汉语 / 漢語; Hàn\b \tyǔ ""#; 82 | let val = inp.parse::().unwrap(); 83 | let mut outs = String::with_capacity(64); 84 | b.iter(|| { 85 | outs.clear(); 86 | write!(outs, "{}", val) 87 | }); 88 | } 89 | 90 | #[bench] 91 | fn bench_array_to_json(b: &mut Bencher) { 92 | let inp = r#" [null,true,false,10,"tru\"e"]"#; 93 | let val = inp.parse::().unwrap(); 94 | let mut outs = String::with_capacity(64); 95 | b.iter(|| { 96 | outs.clear(); 97 | write!(outs, "{}", val) 98 | }); 99 | } 100 | 101 | #[bench] 102 | fn bench_map_to_json(b: &mut Bencher) { 103 | let inp = r#"{"a": null,"b":true,"c":false,"d\"":-10E-1,"e":"tru\"e"}"#; 104 | let val = inp.parse::().unwrap(); 105 | let mut outs = String::with_capacity(64); 106 | b.iter(|| { 107 | outs.clear(); 108 | write!(outs, "{}", val) 109 | }); 110 | } 111 | 112 | #[bench] 113 | fn bench_deferred(b: &mut Bencher) { 114 | let inp = r#" [10123.1231, 1231.123123, 1233.123123, 123.1231231, 12312e10]"#; 115 | b.iter(|| inp.parse::().unwrap()); 116 | } 117 | 118 | #[bench] 119 | fn bench_no_deferred(b: &mut Bencher) { 120 | let inp = r#" [10123.1231, 1231.123123, 1233.123123, 123.1231231, 12312e10]"#; 121 | b.iter(|| inp.parse::().unwrap().compute().unwrap()); 122 | } 123 | 124 | #[bench] 125 | fn bench_json5_num(b: &mut Bencher) { 126 | let inp = r#" -Infinity"#; 127 | b.iter(|| inp.parse::().unwrap().compute().unwrap()); 128 | } 129 | -------------------------------------------------------------------------------- /check.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | export RUST_BACKTRACE=full 4 | export RUSTFLAGS=-g 5 | 6 | exec > $1 7 | exec 2>&1 8 | 9 | set -o xtrace 10 | 11 | exec_prg() { 12 | for i in {0..5}; 13 | do 14 | # test nightly 15 | date; time cargo +nightly test --release -- --nocapture || exit $? 16 | date; time cargo +nightly test -- --nocapture || exit $? 17 | # test stable 18 | date; time cargo +stable test --release -- --nocapture || exit $? 19 | date; time cargo +stable test -- --nocapture || exit $? 20 | done 21 | } 22 | 23 | exec_prg 24 | -------------------------------------------------------------------------------- /docs/Posits4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/docs/Posits4.pdf -------------------------------------------------------------------------------- /docs/_archive/PITCHME.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # The Template Settings 3 | # 4 | # The default settings below are optimized for this template. 5 | # Override settings with your own custom values as needed. 6 | # 7 | # For further details, see https://gitpitch.com/docs/settings. 8 | # 9 | 10 | published : true 11 | 12 | # 13 | # Theme Setting 14 | # Doc: https://gitpitch.com/docs/settings/theme 15 | # 16 | theme : white 17 | 18 | # 19 | # Theme-Override Setting 20 | # Doc: https://gitpitch.com/docs/settings/custom-theme 21 | # 22 | theme-override : template/css/PITCHME.css 23 | 24 | # 25 | # Logo Setting 26 | # https://gitpitch.com/docs/settings/logo 27 | # 28 | logo : template/img/logo.png 29 | logo-position : top-left 30 | 31 | # 32 | # Footnote Setting 33 | # Doc: https://gitpitch.com/docs/settings/footnote 34 | # 35 | footnote : "JavaScrip Object Notation" 36 | 37 | # 38 | # Highlight (Code) Setting 39 | # Doc: https://gitpitch.com/docs/settings/highlight 40 | # 41 | highlight : github 42 | 43 | # 44 | # Layout Setting 45 | # Doc: https://gitpitch.com/docs/settings/layout 46 | # 47 | layout : center 48 | 49 | # 50 | # Transition Setting 51 | # This template uses GitPitch Snap-Layouts extensively. For this 52 | # reason THIS VALUE SHOULD NOT BE MODIFIED. See: 53 | # https://gitpitch.com/docs/layout-features/snap-layout-slide-transitions 54 | # 55 | transition : fade 56 | 57 | slide-number : true 58 | vertical-center : false 59 | 60 | -------------------------------------------------------------------------------- /docs/rfc6901-jptr.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Internet Engineering Task Force (IETF) P. Bryan, Ed. 8 | Request for Comments: 6901 Salesforce.com 9 | Category: Standards Track K. Zyp 10 | ISSN: 2070-1721 SitePen (USA) 11 | M. Nottingham, Ed. 12 | Akamai 13 | April 2013 14 | 15 | 16 | JavaScript Object Notation (JSON) Pointer 17 | 18 | Abstract 19 | 20 | JSON Pointer defines a string syntax for identifying a specific value 21 | within a JavaScript Object Notation (JSON) document. 22 | 23 | Status of This Memo 24 | 25 | This is an Internet Standards Track document. 26 | 27 | This document is a product of the Internet Engineering Task Force 28 | (IETF). It represents the consensus of the IETF community. It has 29 | received public review and has been approved for publication by the 30 | Internet Engineering Steering Group (IESG). Further information on 31 | Internet Standards is available in Section 2 of RFC 5741. 32 | 33 | Information about the current status of this document, any errata, 34 | and how to provide feedback on it may be obtained at 35 | http://www.rfc-editor.org/info/rfc6901. 36 | 37 | Copyright Notice 38 | 39 | Copyright (c) 2013 IETF Trust and the persons identified as the 40 | document authors. All rights reserved. 41 | 42 | This document is subject to BCP 78 and the IETF Trust's Legal 43 | Provisions Relating to IETF Documents 44 | (http://trustee.ietf.org/license-info) in effect on the date of 45 | publication of this document. Please review these documents 46 | carefully, as they describe your rights and restrictions with respect 47 | to this document. Code Components extracted from this document must 48 | include Simplified BSD License text as described in Section 4.e of 49 | the Trust Legal Provisions and are provided without warranty as 50 | described in the Simplified BSD License. 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | Bryan, et al. Standards Track [Page 1] 59 | 60 | RFC 6901 JSON Pointer April 2013 61 | 62 | 63 | Table of Contents 64 | 65 | 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2 66 | 2. Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . 2 67 | 3. Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 68 | 4. Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . 3 69 | 5. JSON String Representation . . . . . . . . . . . . . . . . . . 4 70 | 6. URI Fragment Identifier Representation . . . . . . . . . . . . 5 71 | 7. Error Handling . . . . . . . . . . . . . . . . . . . . . . . . 6 72 | 8. Security Considerations . . . . . . . . . . . . . . . . . . . . 6 73 | 9. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 7 74 | 10. References . . . . . . . . . . . . . . . . . . . . . . . . . . 7 75 | 10.1. Normative References . . . . . . . . . . . . . . . . . . . 7 76 | 10.2. Informative References . . . . . . . . . . . . . . . . . . 7 77 | 78 | 1. Introduction 79 | 80 | This specification defines JSON Pointer, a string syntax for 81 | identifying a specific value within a JavaScript Object Notation 82 | (JSON) document [RFC4627]. JSON Pointer is intended to be easily 83 | expressed in JSON string values as well as Uniform Resource 84 | Identifier (URI) [RFC3986] fragment identifiers. 85 | 86 | 2. Conventions 87 | 88 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 89 | "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this 90 | document are to be interpreted as described in [RFC2119]. 91 | 92 | This specification expresses normative syntax rules using Augmented 93 | Backus-Naur Form (ABNF) [RFC5234] notation. 94 | 95 | 3. Syntax 96 | 97 | A JSON Pointer is a Unicode string (see [RFC4627], Section 3) 98 | containing a sequence of zero or more reference tokens, each prefixed 99 | by a '/' (%x2F) character. 100 | 101 | Because the characters '~' (%x7E) and '/' (%x2F) have special 102 | meanings in JSON Pointer, '~' needs to be encoded as '~0' and '/' 103 | needs to be encoded as '~1' when these characters appear in a 104 | reference token. 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | Bryan, et al. Standards Track [Page 2] 115 | 116 | RFC 6901 JSON Pointer April 2013 117 | 118 | 119 | The ABNF syntax of a JSON Pointer is: 120 | 121 | json-pointer = *( "/" reference-token ) 122 | reference-token = *( unescaped / escaped ) 123 | unescaped = %x00-2E / %x30-7D / %x7F-10FFFF 124 | ; %x2F ('/') and %x7E ('~') are excluded from 'unescaped' 125 | escaped = "~" ( "0" / "1" ) 126 | ; representing '~' and '/', respectively 127 | 128 | It is an error condition if a JSON Pointer value does not conform to 129 | this syntax (see Section 7). 130 | 131 | Note that JSON Pointers are specified in characters, not as bytes. 132 | 133 | 4. Evaluation 134 | 135 | Evaluation of a JSON Pointer begins with a reference to the root 136 | value of a JSON document and completes with a reference to some value 137 | within the document. Each reference token in the JSON Pointer is 138 | evaluated sequentially. 139 | 140 | Evaluation of each reference token begins by decoding any escaped 141 | character sequence. This is performed by first transforming any 142 | occurrence of the sequence '~1' to '/', and then transforming any 143 | occurrence of the sequence '~0' to '~'. By performing the 144 | substitutions in this order, an implementation avoids the error of 145 | turning '~01' first into '~1' and then into '/', which would be 146 | incorrect (the string '~01' correctly becomes '~1' after 147 | transformation). 148 | 149 | The reference token then modifies which value is referenced according 150 | to the following scheme: 151 | 152 | o If the currently referenced value is a JSON object, the new 153 | referenced value is the object member with the name identified by 154 | the reference token. The member name is equal to the token if it 155 | has the same number of Unicode characters as the token and their 156 | code points are byte-by-byte equal. No Unicode character 157 | normalization is performed. If a referenced member name is not 158 | unique in an object, the member that is referenced is undefined, 159 | and evaluation fails (see below). 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | Bryan, et al. Standards Track [Page 3] 171 | 172 | RFC 6901 JSON Pointer April 2013 173 | 174 | 175 | o If the currently referenced value is a JSON array, the reference 176 | token MUST contain either: 177 | 178 | * characters comprised of digits (see ABNF below; note that 179 | leading zeros are not allowed) that represent an unsigned 180 | base-10 integer value, making the new referenced value the 181 | array element with the zero-based index identified by the 182 | token, or 183 | 184 | * exactly the single character "-", making the new referenced 185 | value the (nonexistent) member after the last array element. 186 | 187 | The ABNF syntax for array indices is: 188 | 189 | array-index = %x30 / ( %x31-39 *(%x30-39) ) 190 | ; "0", or digits without a leading "0" 191 | 192 | Implementations will evaluate each reference token against the 193 | document's contents and will raise an error condition if it fails to 194 | resolve a concrete value for any of the JSON pointer's reference 195 | tokens. For example, if an array is referenced with a non-numeric 196 | token, an error condition will be raised. See Section 7 for details. 197 | 198 | Note that the use of the "-" character to index an array will always 199 | result in such an error condition because by definition it refers to 200 | a nonexistent array element. Thus, applications of JSON Pointer need 201 | to specify how that character is to be handled, if it is to be 202 | useful. 203 | 204 | Any error condition for which a specific action is not defined by the 205 | JSON Pointer application results in termination of evaluation. 206 | 207 | 5. JSON String Representation 208 | 209 | A JSON Pointer can be represented in a JSON string value. Per 210 | [RFC4627], Section 2.5, all instances of quotation mark '"' (%x22), 211 | reverse solidus '\' (%x5C), and control (%x00-1F) characters MUST be 212 | escaped. 213 | 214 | Note that before processing a JSON string as a JSON Pointer, 215 | backslash escape sequences must be unescaped. 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | Bryan, et al. Standards Track [Page 4] 227 | 228 | RFC 6901 JSON Pointer April 2013 229 | 230 | 231 | For example, given the JSON document 232 | 233 | { 234 | "foo": ["bar", "baz"], 235 | "": 0, 236 | "a/b": 1, 237 | "c%d": 2, 238 | "e^f": 3, 239 | "g|h": 4, 240 | "i\\j": 5, 241 | "k\"l": 6, 242 | " ": 7, 243 | "m~n": 8 244 | } 245 | 246 | The following JSON strings evaluate to the accompanying values: 247 | 248 | "" // the whole document 249 | "/foo" ["bar", "baz"] 250 | "/foo/0" "bar" 251 | "/" 0 252 | "/a~1b" 1 253 | "/c%d" 2 254 | "/e^f" 3 255 | "/g|h" 4 256 | "/i\\j" 5 257 | "/k\"l" 6 258 | "/ " 7 259 | "/m~0n" 8 260 | 261 | 6. URI Fragment Identifier Representation 262 | 263 | A JSON Pointer can be represented in a URI fragment identifier by 264 | encoding it into octets using UTF-8 [RFC3629], while percent-encoding 265 | those characters not allowed by the fragment rule in [RFC3986]. 266 | 267 | Note that a given media type needs to specify JSON Pointer as its 268 | fragment identifier syntax explicitly (usually, in its registration 269 | [RFC6838]). That is, just because a document is JSON does not imply 270 | that JSON Pointer can be used as its fragment identifier syntax. In 271 | particular, the fragment identifier syntax for application/json is 272 | not JSON Pointer. 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | Bryan, et al. Standards Track [Page 5] 283 | 284 | RFC 6901 JSON Pointer April 2013 285 | 286 | 287 | Given the same example document as above, the following URI fragment 288 | identifiers evaluate to the accompanying values: 289 | 290 | # // the whole document 291 | #/foo ["bar", "baz"] 292 | #/foo/0 "bar" 293 | #/ 0 294 | #/a~1b 1 295 | #/c%25d 2 296 | #/e%5Ef 3 297 | #/g%7Ch 4 298 | #/i%5Cj 5 299 | #/k%22l 6 300 | #/%20 7 301 | #/m~0n 8 302 | 303 | 7. Error Handling 304 | 305 | In the event of an error condition, evaluation of the JSON Pointer 306 | fails to complete. 307 | 308 | Error conditions include, but are not limited to: 309 | 310 | o Invalid pointer syntax 311 | 312 | o A pointer that references a nonexistent value 313 | 314 | This specification does not define how errors are handled. An 315 | application of JSON Pointer SHOULD specify the impact and handling of 316 | each type of error. 317 | 318 | For example, some applications might stop pointer processing upon an 319 | error, while others may attempt to recover from missing values by 320 | inserting default ones. 321 | 322 | 8. Security Considerations 323 | 324 | A given JSON Pointer is not guaranteed to reference an actual JSON 325 | value. Therefore, applications using JSON Pointer should anticipate 326 | this situation by defining how a pointer that does not resolve ought 327 | to be handled. 328 | 329 | Note that JSON pointers can contain the NUL (Unicode U+0000) 330 | character. Care is needed not to misinterpret this character in 331 | programming languages that use NUL to mark the end of a string. 332 | 333 | 334 | 335 | 336 | 337 | 338 | Bryan, et al. Standards Track [Page 6] 339 | 340 | RFC 6901 JSON Pointer April 2013 341 | 342 | 343 | 9. Acknowledgements 344 | 345 | The following individuals contributed ideas, feedback, and wording to 346 | this specification: 347 | 348 | Mike Acar, Carsten Bormann, Tim Bray, Jacob Davies, Martin J. 349 | Duerst, Bjoern Hoehrmann, James H. Manger, Drew Perttula, and 350 | Julian Reschke. 351 | 352 | 10. References 353 | 354 | 10.1. Normative References 355 | 356 | [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 357 | Requirement Levels", BCP 14, RFC 2119, March 1997. 358 | 359 | [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO 360 | 10646", STD 63, RFC 3629, November 2003. 361 | 362 | [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform 363 | Resource Identifier (URI): Generic Syntax", STD 66, 364 | RFC 3986, January 2005. 365 | 366 | [RFC4627] Crockford, D., "The application/json Media Type for 367 | JavaScript Object Notation (JSON)", RFC 4627, July 2006. 368 | 369 | [RFC5234] Crocker, D. and P. Overell, "Augmented BNF for Syntax 370 | Specifications: ABNF", STD 68, RFC 5234, January 2008. 371 | 372 | 10.2. Informative References 373 | 374 | [RFC6838] Freed, N., Klensin, J., and T. Hansen, "Media Type 375 | Specifications and Registration Procedures", BCP 13, 376 | RFC 6838, January 2013. 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | Bryan, et al. Standards Track [Page 7] 395 | 396 | RFC 6901 JSON Pointer April 2013 397 | 398 | 399 | Authors' Addresses 400 | 401 | Paul C. Bryan (editor) 402 | Salesforce.com 403 | 404 | Phone: +1 604 783 1481 405 | EMail: pbryan@anode.ca 406 | 407 | 408 | Kris Zyp 409 | SitePen (USA) 410 | 411 | Phone: +1 650 968 8787 412 | EMail: kris@sitepen.com 413 | 414 | 415 | Mark Nottingham (editor) 416 | Akamai 417 | 418 | EMail: mnot@mnot.net 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | Bryan, et al. Standards Track [Page 8] 451 | 452 | -------------------------------------------------------------------------------- /examples/macro.rs: -------------------------------------------------------------------------------- 1 | //! Using jsondata macros 2 | 3 | use std::convert::TryInto; 4 | 5 | use jsondata::{Json, JsonSerialize}; 6 | 7 | #[derive(JsonSerialize, Default, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] 8 | #[allow(non_snake_case)] 9 | struct Parent { 10 | #[json(try_into = "i128")] 11 | field1: u8, 12 | #[json(from_str)] 13 | field2: i8, 14 | #[json(to_string)] 15 | field3: u16, 16 | field4: i16, 17 | field5: u32, 18 | field6: i32, 19 | field7: u64, 20 | field8: i64, 21 | field9: u128, 22 | field10: i128, 23 | field11: bool, 24 | field12: usize, 25 | field13: isize, 26 | field14: String, 27 | field15: Vec, 28 | field16: Child, 29 | } 30 | 31 | #[derive(JsonSerialize, Default, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] 32 | #[allow(non_snake_case)] 33 | struct Child { 34 | fIeld1: i128, 35 | another_fieldWithTuple: (String, i128), 36 | } 37 | 38 | #[derive(JsonSerialize, Default, Clone, Debug)] 39 | #[allow(non_snake_case)] 40 | struct Floats { 41 | field1: f32, 42 | field2: f64, 43 | } 44 | 45 | fn main() { 46 | let p_ref = { 47 | let c_ref = Child { 48 | fIeld1: 10, 49 | another_fieldWithTuple: ("hello".to_string(), 10000000), 50 | }; 51 | Parent { 52 | field1: 10, 53 | field2: -10, 54 | field3: 100, 55 | field4: -100, 56 | field5: 1000, 57 | field6: -1000, 58 | field7: 10000, 59 | field8: -10000, 60 | field9: 1000000, 61 | field10: -1000000, 62 | field11: true, 63 | field12: 100, 64 | field13: 102, 65 | field14: "hello world".to_string(), 66 | field15: vec![1, 2, 3, 4], 67 | field16: c_ref, 68 | } 69 | }; 70 | 71 | let ref_s = concat!( 72 | r#"{"field1":10,"field10":-1000000,"#, 73 | r#""field11":true,"field12":100,"field13":102,"field14":"hello world","#, 74 | r#""field15":[1,2,3,4],"#, 75 | r#""field16":{"another_fieldwithtuple":["hello",10000000],"field1":10},"#, 76 | r#""field2":"-10","field3":"100","#, 77 | r#""field4":-100,"field5":1000,"field6":-1000,"field7":10000,"#, 78 | r#""field8":-10000,"field9":1000000}"# 79 | ); 80 | let jval = Json::from(p_ref.clone()); 81 | let p = Parent::try_from(jval.clone()).unwrap(); 82 | assert_eq!(jval.to_string(), ref_s); 83 | assert_eq!(p, p_ref); 84 | 85 | println!("{}", jval); 86 | 87 | let f_ref = Floats { field1: 10.234_567, field2: -10.12312312312311 }; 88 | 89 | let ref_s = format!( 90 | "{{\"field1\":{:e},\"field2\":-1.012312312312311e1}}", 91 | f_ref.field1 as f64, 92 | ); 93 | 94 | let jval: Json = Json::try_from(f_ref.clone()).unwrap(); 95 | let f: Floats = Floats::try_from(jval.clone()).unwrap(); 96 | 97 | assert_eq!(jval.to_string(), ref_s); 98 | assert!((f.field1 - f_ref.field1).abs() < f32::EPSILON); 99 | assert!((f.field2 - f_ref.field2).abs() < f64::EPSILON); 100 | 101 | println!("{}", jval); 102 | } 103 | -------------------------------------------------------------------------------- /examples/mixed_integer.rs: -------------------------------------------------------------------------------- 1 | use jsondata::{Json, JsonSerialize}; 2 | 3 | use std::str::FromStr; 4 | 5 | #[derive(Debug, Clone, JsonSerialize)] 6 | struct U8 { 7 | a: u8, 8 | } 9 | 10 | #[derive(Debug, Clone, JsonSerialize)] 11 | struct F32 { 12 | a: f32, 13 | } 14 | 15 | #[derive(Debug, Clone, JsonSerialize)] 16 | struct F64 { 17 | a: f64, 18 | } 19 | 20 | #[derive(Debug, Clone, JsonSerialize)] 21 | struct Data { 22 | a: (u8, u8), 23 | b: (i8, i8), 24 | c: (u16, u16), 25 | d: (i16, i16), 26 | e: (u32, u32), 27 | f: (i32, i32), 28 | g: (u64, u64), 29 | h: (i64, i64), 30 | i: (usize, usize), 31 | j: (isize, isize), 32 | } 33 | 34 | fn main() { 35 | let data1 = Data { 36 | a: (0, 255), 37 | b: (-128, 127), 38 | c: (0, 65535), 39 | d: (-32768, 32767), 40 | e: (0, 4294967295), 41 | f: (-2147483648, 2147483647), 42 | g: (0, 18446744073709551615), 43 | h: (-9223372036854775808, 9223372036854775807), 44 | i: (0, 18446744073709551615), 45 | j: (-9223372036854775808, 9223372036854775807), 46 | }; 47 | 48 | let jval1 = Json::from(data1); 49 | let text = jval1.to_string(); 50 | let out = Data::try_from(Json::from_str(&text).unwrap()); 51 | assert!(out.is_ok()); 52 | println!("{:?}", out.unwrap()); 53 | 54 | let text = r#"{"a": 256}"#; 55 | let out = U8::try_from(Json::from_str(text).unwrap()); 56 | assert!(out.is_err()); 57 | 58 | let text = r#"{"a": 2.123456789}"#; 59 | let out = F32::try_from(Json::from_str(text).unwrap()); 60 | assert!(out.is_ok()); 61 | println!("{:?}", out.clone().unwrap()); 62 | assert!((out.unwrap().a - 2.123_456_7).abs() < 0.000001); 63 | 64 | let texts = [ 65 | r#"{"a": 0}"#, 66 | r#"{"a": -128}"#, 67 | r#"{"a": 127}"#, 68 | r#"{"a": 255}"#, 69 | r#"{"a": -32768}"#, 70 | r#"{"a": 32767}"#, 71 | r#"{"a": 65535}"#, 72 | r#"{"a": -2147483648}"#, 73 | r#"{"a": 2147483647}"#, 74 | r#"{"a": 4294967295}"#, 75 | r#"{"a": -9223372036854775808}"#, 76 | r#"{"a": 9223372036854775807}"#, 77 | r#"{"a": 18446744073709551615}"#, 78 | ]; 79 | for (i, text) in texts.iter().enumerate() { 80 | let out = F64::try_from(Json::from_str(text).unwrap()); 81 | match i { 82 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 => println!("{:?}", out.unwrap()), 83 | _ => assert!(out.is_err()), 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /jsondata-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "jsondata-derive" 3 | version = "0.1.0" 4 | description = "Macros for jsondata types" 5 | repository = "https://github.com/bnclabs/jsondata" 6 | documentation = "https://docs.rs/jsondata" 7 | keywords = ["json", "json5"] 8 | categories = ["parsing", "encoding"] 9 | authors = ["prataprc "] 10 | license = "MIT" 11 | edition = "2018" 12 | readme = "../README.md" 13 | 14 | [profile.release] 15 | debug = true 16 | 17 | [profile.bench] 18 | debug = true 19 | 20 | [dependencies] 21 | syn = { version = "1", features = ["full"] } 22 | quote = "1" 23 | proc-macro2 = "1" 24 | heck = "0.3.0" 25 | proc-macro-error = "0.4.7" 26 | 27 | [lib] 28 | proc-macro = true 29 | -------------------------------------------------------------------------------- /jsondata-derive/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /jsondata-derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use proc_macro2::TokenStream; 4 | use proc_macro_error::{abort_call_site, proc_macro_error}; 5 | use quote::quote; 6 | use syn::*; 7 | 8 | // TODO: handle generic-types and generic-lifetimes in struct. 9 | // TODO: implement JsonSerialize for enums. 10 | // TODO: implement JsonSerialize for tuple-struct. 11 | 12 | #[proc_macro_derive(JsonSerialize, attributes(json))] 13 | #[proc_macro_error] 14 | pub fn jsonize_type(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 15 | let input: DeriveInput = syn::parse(input).unwrap(); 16 | let gen = impl_jsonize_type(&input); 17 | proc_macro::TokenStream::from(gen) 18 | } 19 | 20 | fn impl_jsonize_type(input: &DeriveInput) -> TokenStream { 21 | let name = &input.ident; 22 | match &input.data { 23 | Data::Struct(ast) => match &ast.fields { 24 | Fields::Named(fields) => { 25 | let mut ts = from_type_to_json(name, fields); 26 | ts.extend(from_json_to_type(name, fields)); 27 | ts 28 | } 29 | _ => abort_call_site!("jsondata only supports named fields"), 30 | }, 31 | _ => abort_call_site!("jsondata only supports named structs"), 32 | } 33 | } 34 | 35 | fn from_type_to_json(name: &Ident, fields: &FieldsNamed) -> TokenStream { 36 | let croot = get_root_crate(); 37 | 38 | let mut token_builder = quote! {}; 39 | for field in fields.named.iter() { 40 | token_builder.extend(to_json_property(field)); 41 | } 42 | quote! { 43 | impl ::std::convert::From<#name> for #croot::Json { 44 | fn from(value: #name) -> #croot::Json { 45 | let mut props: Vec<#croot::Property> = vec![]; 46 | #token_builder; 47 | #croot::Json::new(props) 48 | } 49 | } 50 | } 51 | } 52 | 53 | fn to_json_property(field: &Field) -> TokenStream { 54 | let croot = get_root_crate(); 55 | 56 | match &field.ident { 57 | Some(field_name) => { 58 | let key = field_name.to_string().to_lowercase(); 59 | let is_from_str = get_from_str(&field.attrs); 60 | match (is_from_str, get_try_into(&field.attrs)) { 61 | (true, _) => quote! { 62 | let v = Json::from(value.#field_name.to_string()); 63 | props.push(#croot::Property::new(#key, v)); 64 | }, 65 | (false, Some(intr_type)) => quote! { 66 | let v: #intr_type = value.#field_name.try_into().unwrap(); 67 | let v = Json::from(v); 68 | props.push(#croot::Property::new(#key, v)); 69 | }, 70 | (false, None) => quote! { 71 | let v = value.#field_name.into(); 72 | props.push(#croot::Property::new(#key, v)); 73 | }, 74 | } 75 | } 76 | None => TokenStream::new(), 77 | } 78 | } 79 | 80 | fn get_from_str(attrs: &[syn::Attribute]) -> bool { 81 | if attrs.is_empty() { 82 | return false; 83 | } 84 | match attrs[0].parse_meta().unwrap() { 85 | syn::Meta::List(meta_list) => { 86 | let mut iter = meta_list.nested.iter(); 87 | 'outer: loop { 88 | if let Some(syn::NestedMeta::Meta(syn::Meta::Path(p))) = iter.next() { 89 | for seg in p.segments.iter() { 90 | match seg.ident.to_string().as_str() { 91 | "from_str" | "to_string" => break 'outer true, 92 | _ => (), 93 | } 94 | } 95 | } else { 96 | break 'outer false; 97 | } 98 | } 99 | } 100 | _ => false, 101 | } 102 | } 103 | 104 | fn get_try_into(attrs: &[syn::Attribute]) -> Option { 105 | if attrs.is_empty() { 106 | return None; 107 | } 108 | let nv = match attrs[0].parse_meta().unwrap() { 109 | syn::Meta::List(meta_list) => { 110 | let mut iter = meta_list.nested.iter(); 111 | loop { 112 | match iter.next()? { 113 | syn::NestedMeta::Meta(syn::Meta::NameValue(nv)) => { 114 | break Some(nv.clone()) 115 | } 116 | _ => continue, 117 | } 118 | } 119 | } 120 | _ => None, 121 | }?; 122 | 123 | let segs: Vec<&syn::PathSegment> = nv.path.segments.iter().collect(); 124 | match segs.first().unwrap().ident.to_string().as_str() { 125 | "try_into" => Some(match &nv.lit { 126 | syn::Lit::Str(s) => s.parse().unwrap(), 127 | _ => panic!("invalid literal"), 128 | }), 129 | _ => None, 130 | } 131 | } 132 | 133 | fn from_json_to_type(name: &Ident, fields: &FieldsNamed) -> TokenStream { 134 | let croot = get_root_crate(); 135 | 136 | let mut token_builder = quote! {}; 137 | for field in fields.named.iter() { 138 | token_builder.extend(to_type_field(field)); 139 | } 140 | 141 | quote! { 142 | impl ::std::convert::TryFrom<#croot::Json> for #name { 143 | type Error = #croot::Error; 144 | 145 | fn try_from(value: #croot::Json) -> ::std::result::Result<#name, Self::Error> { 146 | use ::std::convert::TryInto; 147 | use #croot::Error; 148 | 149 | Ok(#name { 150 | #token_builder 151 | }) 152 | } 153 | } 154 | } 155 | } 156 | 157 | fn to_type_field(field: &Field) -> TokenStream { 158 | let croot = get_root_crate(); 159 | 160 | match &field.ident { 161 | Some(field_name) => { 162 | let key = field_name.to_string().to_lowercase(); 163 | let is_from_str = get_from_str(&field.attrs); 164 | match (is_from_str, get_try_into(&field.attrs)) { 165 | (true, _) => quote! { 166 | #field_name: { 167 | let v: String = match value.get(&("/".to_string() + #key))?.try_into() { 168 | Ok(v) => Ok(v), 169 | Err(err) => #croot::err_at!(InvalidType, msg: "{}", #key.to_string()), 170 | }?; 171 | match v.parse() { 172 | Ok(v) => Ok(v), 173 | Err(err) => #croot::err_at!(InvalidType, msg: "{}", #key.to_string()), 174 | }? 175 | }, 176 | }, 177 | (false, Some(intr_type)) => quote! { 178 | #field_name: { 179 | let v: #intr_type = match value.get(&("/".to_string() + #key))?.try_into() { 180 | Ok(v) => Ok(v), 181 | Err(err) => #croot::err_at!(InvalidType, msg: "{}", #key.to_string()), 182 | }?; 183 | match v.try_into() { 184 | Ok(v) => Ok(v), 185 | Err(err) => #croot::err_at!(InvalidType, msg: "{}", #key.to_string()), 186 | }? 187 | }, 188 | }, 189 | (false, None) => quote! { 190 | #field_name: match value.get(&("/".to_string() + #key))?.try_into() { 191 | Ok(v) => Ok(v), 192 | Err(err) => { 193 | #croot::err_at!(InvalidType, msg: "{} err: {}", #key.to_string(), err) 194 | } 195 | }?, 196 | }, 197 | } 198 | } 199 | None => TokenStream::new(), 200 | } 201 | } 202 | 203 | fn get_root_crate() -> TokenStream { 204 | // TODO: can this be fixed ? 205 | let local = module_path!().ends_with("json_test"); 206 | if local { 207 | quote! { crate } 208 | } else { 209 | quote! { ::jsondata } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /perf.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | exec > $1 4 | exec 2>&1 5 | 6 | set -o xtrace 7 | 8 | PERF=$HOME/.cargo/target/release/perf 9 | 10 | date; time cargo +nightly bench -- --nocapture || exit $? 11 | date; valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes cargo +nightly test --release -- --nocapture || exit $? 12 | date; valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes cargo +nightly test -- --nocapture || exit $? 13 | date; valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes cargo +nightly bench -- --nocapture || exit $? 14 | 15 | date; time cargo +stable bench -- --nocapture || exit $? 16 | date; valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes cargo +stable test --release -- --nocapture || exit $? 17 | date; valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes cargo +stable test -- --nocapture || exit $? 18 | date; valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes cargo +stable bench -- --nocapture || exit $? 19 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 90 2 | array_width = 80 3 | attr_fn_like_width = 80 4 | chain_width = 80 5 | fn_call_width = 80 6 | single_line_if_else_max_width = 80 7 | struct_lit_width = 50 8 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2019 R Pratap Chakravarthy. All rights reserved. 2 | 3 | use std::{error, fmt, result}; 4 | 5 | /// Short form to compose Error values. 6 | /// 7 | /// Here are few possible ways: 8 | /// 9 | /// ```ignore 10 | /// use crate::Error; 11 | /// err_at!(ParseError, msg: "bad argument"); 12 | /// ``` 13 | /// 14 | /// ```ignore 15 | /// use crate::Error; 16 | /// err_at!(ParseError, std::io::read(buf)); 17 | /// ``` 18 | /// 19 | /// ```ignore 20 | /// use crate::Error; 21 | /// err_at!(ParseError, std::fs::read(file_path), "read failed"); 22 | /// ``` 23 | /// 24 | #[macro_export] 25 | macro_rules! err_at { 26 | ($v:ident, msg: $($arg:expr),+) => {{ 27 | let prefix = format!("{}:{}", file!(), line!()); 28 | Err(Error::$v(prefix, format!($($arg),+))) 29 | }}; 30 | ($v:ident, $e:expr) => {{ 31 | match $e { 32 | Ok(val) => Ok(val), 33 | Err(err) => { 34 | let prefix = format!("{}:{}", file!(), line!()); 35 | Err(Error::$v(prefix, format!("{}", err))) 36 | } 37 | } 38 | }}; 39 | ($v:ident, $e:expr, $($arg:expr),+) => {{ 40 | match $e { 41 | Ok(val) => Ok(val), 42 | Err(err) => { 43 | let prefix = format!("{}:{}", file!(), line!()); 44 | let msg = format!($($arg),+); 45 | Err(Error::$v(prefix, format!("{} {}", err, msg))) 46 | } 47 | } 48 | }}; 49 | } 50 | 51 | /// Enumeration of all possible errors that shall be returned by this package. 52 | /// 53 | /// Refer to individual methods and functions, returning [Result] type, for 54 | /// specific error handling. 55 | #[derive(Clone, PartialEq)] 56 | pub enum Error { 57 | /// Failed to parse JSON text. 58 | ParseFail(String, String), 59 | /// Failed to add two Json values. 60 | AddFail(String, String), 61 | /// Failed to subract two Json values. 62 | SubFail(String, String), 63 | /// Failed to multiply two Json values. 64 | MulFail(String, String), 65 | /// Failed to divide two Json values. 66 | DivFail(String, String), 67 | /// Failed to find reminder between two Json values. 68 | RemFail(String, String), 69 | /// Failed to negate Json value. 70 | NegFail(String, String), 71 | /// Failed to left shift Json value. 72 | ShlFail(String, String), 73 | /// Failed to right shift Json value. 74 | ShrFail(String, String), 75 | /// When indexing a Json array with out of bound index. 76 | IndexOutofBound(String, String), 77 | /// When attempting index an array with an invalid index. 78 | InvalidIndex(String, String), 79 | /// When attempting array operation, like range, index etc.., 80 | /// on non-array value. 81 | NotAnArray(String, String), 82 | /// When attempting lookup and indexing operations like, set, delete, 83 | /// append, index, etc.. on values that are neither array nor object. 84 | InvalidContainer(String, String), 85 | /// Not an expected type, arugment gives the found type. 86 | InvalidType(String, String), 87 | /// When trying to lookup a Json object with missing property. 88 | PropertyNotFound(String, String), 89 | /// While appending a non string value with Json string. 90 | AppendString(String, String), 91 | /// Found JSON text that looks like number, but not well formed. 92 | InvalidNumber(String, String), 93 | /// Failed processing json-pointer. 94 | JptrFail(String, String), 95 | /// std::io::Error returned by string processing API, while iterating 96 | /// on [`crate::Jsons`] stream of text. 97 | IoError(String, String), 98 | } 99 | 100 | impl fmt::Display for Error { 101 | fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { 102 | match self { 103 | Error::ParseFail(p, m) => write!(f, "{} ParseFail:{}", p, m), 104 | Error::AddFail(p, m) => write!(f, "{} AddFail:{}", p, m), 105 | Error::SubFail(p, m) => write!(f, "{} SubFail:{}", p, m), 106 | Error::MulFail(p, m) => write!(f, "{} MulFail:{}", p, m), 107 | Error::DivFail(p, m) => write!(f, "{} DivFail:{}", p, m), 108 | Error::RemFail(p, m) => write!(f, "{} RemFail:{}", p, m), 109 | Error::NegFail(p, m) => write!(f, "{} NegFail:{}", p, m), 110 | Error::ShlFail(p, m) => write!(f, "{} ShlFail:{}", p, m), 111 | Error::ShrFail(p, m) => write!(f, "{} ShrFail:{}", p, m), 112 | Error::IndexOutofBound(p, m) => write!(f, "{} IndexOutofBound:{}", p, m), 113 | Error::InvalidIndex(p, m) => write!(f, "{} InvalidIndex:{}", p, m), 114 | Error::NotAnArray(p, m) => write!(f, "{} NotAnArray:{}", p, m), 115 | Error::InvalidContainer(p, m) => write!(f, "{} InvalidContainer:{}", p, m), 116 | Error::InvalidType(p, m) => write!(f, "{} InvalidType:{}", p, m), 117 | Error::PropertyNotFound(p, m) => write!(f, "{} PropertyNotFound:{}", p, m), 118 | Error::AppendString(p, m) => write!(f, "{} AppendString:{}", p, m), 119 | Error::InvalidNumber(p, m) => write!(f, "{} InvalidNumber:{}", p, m), 120 | Error::JptrFail(p, m) => write!(f, "{} JptrFail:{}", p, m), 121 | Error::IoError(p, m) => write!(f, "{} IoError:{}", p, m), 122 | } 123 | } 124 | } 125 | 126 | impl fmt::Debug for Error { 127 | fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { 128 | write!(f, "{}", self) 129 | } 130 | } 131 | 132 | impl error::Error for Error {} 133 | -------------------------------------------------------------------------------- /src/jptr.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2019 R Pratap Chakravarthy. All rights reserved. 2 | 3 | //! Module jptr implements [JSON Pointer RFC spec.]. 4 | //! 5 | //! [JSON Pointer RFC spec.]: https://tools.ietf.org/html/rfc6901 6 | 7 | use crate::{json::Json, ops, Error, Result}; 8 | 9 | /// quote path fragment using backslash escape and tilde escape defined by the 10 | /// RFC specification. 11 | /// 12 | /// After escaping each path-fragment caller can join them with '/'. 13 | pub fn quote(fragment: &str) -> String { 14 | let mut outs = String::new(); 15 | for ch in fragment.chars() { 16 | match ch { 17 | // backslash escape 18 | '"' | '\\' | '\x00'..='\x1f' => { 19 | outs.push('\\'); 20 | outs.push(ch) 21 | } 22 | // tilde escape 23 | '~' => { 24 | outs.push('~'); 25 | outs.push('0') 26 | } 27 | '/' => { 28 | outs.push('~'); 29 | outs.push('1') 30 | } 31 | _ => outs.push(ch), 32 | } 33 | } 34 | outs 35 | } 36 | 37 | /// unquote path fragment for backslash and tilde escape defined by the 38 | /// RFC specification. 39 | /// 40 | /// After un-escaping each path-fragment caller can join them with '/'. 41 | pub fn unquote(fragment: &str) -> Result { 42 | let mut outs = String::new(); 43 | let (mut escaped, mut tilde) = (false, false); 44 | for ch in fragment.chars() { 45 | if escaped { 46 | escaped = false; 47 | outs.push(ch); 48 | continue; 49 | } else if tilde { 50 | tilde = false; 51 | match ch { 52 | '0' => outs.push('~'), 53 | '1' => outs.push('/'), 54 | _ => err_at!(JptrFail, msg: "invalid ~{}", ch)?, 55 | } 56 | continue; 57 | } 58 | 59 | match ch { 60 | '\\' => escaped = true, // backslash escape 61 | '~' => tilde = true, // tilde escape 62 | _ => outs.push(ch), 63 | } 64 | } 65 | Ok(outs) 66 | } 67 | 68 | pub(crate) fn fragments(path: &str) -> Result<(Vec, String)> { 69 | let mut frags: Vec = vec![]; 70 | let mut frag = String::new(); 71 | let mut state: (bool, bool) = (false, false); // (escaped, tilde) 72 | for ch in path.chars() { 73 | state = match ch { 74 | ch if state.0 => { 75 | frag.push(ch); 76 | (false, state.1) 77 | } 78 | '0' if state.1 => { 79 | frag.push('~'); 80 | (state.0, false) 81 | } 82 | '1' if state.1 => { 83 | frag.push('/'); 84 | (state.0, false) 85 | } 86 | ch if state.1 => err_at!(JptrFail, msg: "invalid ~{}", ch)?, 87 | '/' => { 88 | frags.push(frag.clone()); 89 | frag.truncate(0); 90 | (state.0, state.1) 91 | } 92 | '\\' => (true, state.1), 93 | '~' => (state.0, true), 94 | ch => { 95 | frag.push(ch); 96 | (state.0, state.1) 97 | } 98 | }; 99 | } 100 | Ok((frags, frag)) 101 | } 102 | 103 | pub(crate) fn lookup_mut<'a>( 104 | mut json: &'a mut Json, 105 | path: &str, 106 | ) -> Result<(&'a mut Json, String)> { 107 | let (frags, key) = fragments(path)?; 108 | for frag in frags { 109 | json = ops::index_mut(json, frag.as_str())? 110 | } 111 | Ok((json, key)) 112 | } 113 | 114 | pub(crate) fn lookup_ref<'a>( 115 | mut json_doc: &'a Json, 116 | path: &str, 117 | ) -> Result<(&'a Json, String)> { 118 | let (frags, key) = fragments(path)?; 119 | for frag in frags { 120 | json_doc = json_doc[frag.as_str()].to_result()?; 121 | } 122 | Ok((json_doc, key)) 123 | } 124 | 125 | pub(crate) fn fix_prefix(path: &str) -> Result<&str> { 126 | let mut chars = path.chars(); 127 | if chars.next().unwrap() == '/' { 128 | Ok(chars.as_str()) 129 | } else { 130 | err_at!(JptrFail, msg: "pointer should start with forward solidus") 131 | } 132 | } 133 | 134 | #[cfg(test)] 135 | #[path = "jptr_test.rs"] 136 | mod jptr_test; 137 | -------------------------------------------------------------------------------- /src/jptr_test.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2019 R Pratap Chakravarthy. All rights reserved. 2 | 3 | use crate::jptr::{quote, unquote}; 4 | use crate::{json::Json, property::Property}; 5 | 6 | #[test] 7 | fn test_quote() { 8 | let jptr = r#"data/~"\"#; 9 | let refv = r#"data~1~0\"\\"#.to_string(); 10 | assert_eq!(quote(jptr), refv); 11 | 12 | let jptr = r#"\x00\x01\x02\x03\x04\x05\x06"#; 13 | let refv = "\\\\x00\\\\x01\\\\x02\\\\x03\\\\x04\\\\x05\\\\x06".to_string(); 14 | assert_eq!(quote(jptr), refv); 15 | 16 | let jptr = r#"\x07\x08\x09\x0a\x0b\x0c\x0d"#; 17 | let refv = "\\\\x07\\\\x08\\\\x09\\\\x0a\\\\x0b\\\\x0c\\\\x0d".to_string(); 18 | assert_eq!(quote(jptr), refv); 19 | 20 | let jptr = r#"\x0e\x0f"#; 21 | let refv = "\\\\x0e\\\\x0f".to_string(); 22 | assert_eq!(quote(jptr), refv); 23 | } 24 | 25 | #[test] 26 | fn test_unquote() { 27 | let jptr = r#"data/~"\"#.to_string(); 28 | assert_eq!(unquote("e(&jptr)).unwrap(), jptr); 29 | 30 | let jptr = r#"\x00\x01\x02\x03\x04\x05\x06"#.to_string(); 31 | assert_eq!(unquote("e(&jptr)).unwrap(), jptr); 32 | 33 | let jptr = r#"\x07\x08\x09\x0a\x0b\x0c\x0d"#.to_string(); 34 | assert_eq!(unquote("e(&jptr)).unwrap(), jptr); 35 | 36 | let jptr = r#"\x0e\x0f"#.to_string(); 37 | assert_eq!(unquote("e(&jptr)).unwrap(), jptr); 38 | 39 | let jptr = "/my/path".to_string(); 40 | assert_eq!(unquote(&jptr).unwrap(), "/my/path".to_string()); 41 | 42 | assert_eq!(unquote(r#"/i\\j"#).unwrap(), r#"/i\j"#); 43 | assert_eq!(unquote(r#"/k\"l"#).unwrap(), r#"/k"l"#); 44 | } 45 | 46 | #[test] 47 | fn test_jptr_get() { 48 | let text = r#" 49 | { 50 | "foo": ["bar", "baz"], 51 | "": 0, 52 | "a/b": 1, 53 | "c%d": 2, 54 | "e^f": 3, 55 | "g|h": 4, 56 | "i\\j": 5, 57 | "k\"l": 6, 58 | " ": 7, 59 | "m~n": 8, 60 | "d": { "key1": "value" } 61 | } 62 | "#; 63 | let json: Json = text.parse().unwrap(); 64 | 65 | assert_eq!(json.get("").unwrap(), json); 66 | 67 | let refv = Json::new(vec![Json::new("bar"), Json::new("baz")]); 68 | assert_eq!(json.get("/foo").unwrap(), refv); 69 | 70 | assert_eq!(json.get("/foo/0").unwrap(), Json::new("bar")); 71 | assert_eq!(json.get("/").unwrap(), Json::new(0)); 72 | assert_eq!(json.get("/a~1b").unwrap(), Json::new(1)); 73 | assert_eq!(json.get("/c%d").unwrap(), Json::new(2)); 74 | assert_eq!(json.get("/e^f").unwrap(), Json::new(3)); 75 | assert_eq!(json.get("/g|h").unwrap(), Json::new(4)); 76 | assert_eq!(json.get(r#"/i\\j"#).unwrap(), Json::new(5)); 77 | assert_eq!(json.get(r#"/k\"l"#).unwrap(), Json::new(6)); 78 | assert_eq!(json.get("/ ").unwrap(), Json::new(7)); 79 | assert_eq!(json.get("/m~0n").unwrap(), Json::new(8)); 80 | assert_eq!(json.get("/d/key1").unwrap(), Json::new("value")); 81 | } 82 | 83 | #[test] 84 | fn test_jptr_set() { 85 | let text = r#" 86 | { 87 | "foo": ["bar", "baz"], 88 | "": 0, 89 | "a/b": 1, 90 | "c%d": 2, 91 | "e^f": 3, 92 | "g|h": 4, 93 | "i\\j": 5, 94 | "k\"l": 6, 95 | " ": 7, 96 | "m~n": 8 97 | } 98 | "#; 99 | let reft = r#" 100 | { 101 | "foo": [10, "baz"], 102 | "boo": 10, 103 | "": true, 104 | "a/b": true, 105 | "c%d": true, 106 | "e^f": null, 107 | "g|h": null, 108 | "i\\j": null, 109 | "k\"l": null, 110 | " ": "hello", 111 | "m~n": "world", 112 | "d": {"key1": "value"} 113 | } 114 | "#; 115 | let mut json: Json = text.parse().unwrap(); 116 | let refv: Json = reft.parse().unwrap(); 117 | 118 | json.set("/boo", Json::new(10)).unwrap(); 119 | json.set("/foo/0", Json::new(10)).unwrap(); 120 | json.set("/", Json::new(true)).unwrap(); 121 | json.set("/a~1b", Json::new(true)).unwrap(); 122 | json.set("/c%d", Json::new(true)).unwrap(); 123 | json.set("/e^f", Json::Null).unwrap(); 124 | json.set("/g|h", Json::Null).unwrap(); 125 | json.set(r#"/i\\j"#, Json::Null).unwrap(); 126 | json.set(r#"/k\"l"#, Json::Null).unwrap(); 127 | json.set("/ ", Json::new("hello")).unwrap(); 128 | json.set("/m~0n", Json::new("world")).unwrap(); 129 | 130 | json.set("/d", Json::new::>(Vec::new())).unwrap(); 131 | json.set("/d/key1", Json::new("value")).unwrap(); 132 | 133 | assert_eq!(json, refv); 134 | } 135 | 136 | #[test] 137 | fn test_jptr_append() { 138 | let text = r#" 139 | { 140 | "foo": ["bar", "baz"], 141 | "": 0, 142 | "a/b": 1, 143 | "c%d": 2, 144 | "e^f": 3, 145 | "g|h": 4, 146 | "i\\j": 5, 147 | "k\"l": 6, 148 | " ": "hello", 149 | "m~n": 8, 150 | "d" : {"key1": [10,20]} 151 | } 152 | "#; 153 | let reft = r#" 154 | { 155 | "foo": ["barjek", "baz", "goz"], 156 | "": 0, 157 | "a/b": 1, 158 | "c%d": 2, 159 | "e^f": 3, 160 | "g|h": 4, 161 | "i\\j": 5, 162 | "k\"l": 6, 163 | " ": "helloworkd", 164 | "m~n": 8, 165 | "d" : {"key1": [10,20, "workd"]} 166 | } 167 | "#; 168 | let mut json: Json = text.parse().unwrap(); 169 | let refv: Json = reft.parse().unwrap(); 170 | 171 | json.append("/foo", Json::new("goz")).unwrap(); 172 | json.append("/foo/0", Json::new("jek")).unwrap(); 173 | json.append("/ ", Json::new("workd")).unwrap(); 174 | json.append("/d/key1", Json::new("workd")).unwrap(); 175 | 176 | assert_eq!(json, refv); 177 | } 178 | 179 | #[test] 180 | fn test_jptr_delete() { 181 | let text = r#" 182 | { 183 | "foo": ["bar", "baz"], 184 | "": 0, 185 | "a/b": 1, 186 | "c%d": 2, 187 | "e^f": 3, 188 | "g|h": 4, 189 | "i\\j": 5, 190 | "k\"l": 6, 191 | " ": 7, 192 | "m~n": 8, 193 | "d" : {"key1": [10,20]} 194 | } 195 | "#; 196 | let reft = r#" 197 | { 198 | "foo": ["bar"], 199 | "a/b": 1, 200 | "c%d": 2, 201 | "e^f": 3, 202 | "g|h": 4, 203 | "i\\j": 5, 204 | "k\"l": 6, 205 | "m~n": 8, 206 | "d" : {"key1": [20]} 207 | } 208 | "#; 209 | let mut json: Json = text.parse().unwrap(); 210 | let refv: Json = reft.parse().unwrap(); 211 | 212 | json.delete("/foo/1").unwrap(); 213 | json.delete("/").unwrap(); 214 | json.delete("/ ").unwrap(); 215 | json.delete("/d/key1/0").unwrap(); 216 | 217 | assert_eq!(json, refv); 218 | } 219 | -------------------------------------------------------------------------------- /src/json_qc.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2019 R Pratap Chakravarthy. All rights reserved. 2 | 3 | use std::fmt::{Write}; 4 | 5 | use json::Json; 6 | use property::Property; 7 | use quickcheck::Arbitrary; 8 | 9 | pub enum JsonQC { 10 | Null, 11 | Bool(bool), 12 | Integer(i128), 13 | Float(f64), 14 | String(String), 15 | Array(Vec), 16 | Object(Vec), 17 | } 18 | 19 | impl Into for JsonQC { 20 | fn into(self) -> Json { 21 | match self { 22 | JsonQC::Null => Json::Null, 23 | JsonQC::Bool(val) => Json::Bool(val), 24 | JsonQC::Integer(val) => Json::Integer(val), 25 | JsonQC::Float(val) => Json::Float(val), 26 | JsonQC::String(val) => Json::String(val), 27 | JsonQC::Array(val) => Json::Array(val), 28 | JsonQC::Object(val) => Json::Object(val), 29 | } 30 | } 31 | } 32 | 33 | impl Arbitrary for JsonQC { 34 | fn arbitrary(g: &mut G) -> JsonQC { 35 | let r = g.next_u32(); 36 | match r % 7 { 37 | 0 => JsonQC::Null(), 38 | 1 => JsonQC::Bool(bool::arbitrary(g)), 39 | 2 => JsonQC::Integer(i128::arbitrary(g)), 40 | 3 => JsonQC::Float(f64::arbitrary(g)), 41 | 4 => JsonQC::String(JsonQC::arbitrary_string(g)), 42 | 5 => JsonQC::Array(JsonQC::arbitrary_array(g)), 43 | 6 => JsonQC::Object(JsonQC::arbitrary_object(g)), 44 | } 45 | } 46 | 47 | fn arbitrary_string(g: &mut G) -> String { 48 | let strings = include!("../testdata/qc_strings.jsons"); 49 | let r = g.next_u32(); 50 | match r % (strings.len() + 1) { 51 | 0..strings.len() => strings[r].clone(), 52 | strings.len() => String::arbitrary(g), 53 | } 54 | } 55 | 56 | fn arbitrary_array(g: &mut G) -> Vec { 57 | let r = g.next_u32(); 58 | let val = Vec::new(); 59 | (0..r).foreach(val.push(JsonQC::arbitrary(g))) 60 | } 61 | 62 | fn arbitrary_object(g: &mut G) -> Vec { 63 | let r = g.next_u32(); 64 | let val = Vec::new(); 65 | (0..r).foreach(val.push({ 66 | Property::new(JsonQC::arbitrary_string(g), JsonQC::arbitrary(g)); 67 | }) 68 | } 69 | } 70 | 71 | impl Display for JsonQC { 72 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 73 | match self { 74 | JsonQC::Null => write!(f, "null"), 75 | JsonQC::Bool(true) => write!(f, "true"), 76 | JsonQC::Bool(false) => write!(f, "false"), 77 | JsonQC::Integer(val) => write!(f, "{}", val), 78 | JsonQC::Float(val) => { 79 | if *val == f64::INFINITY { 80 | write!(f, "Infinity") 81 | } else if *val == f64::NEG_INFINITY { 82 | write!(f, "-Infinity") 83 | } else { 84 | write!(f, "{:e}", val) 85 | } 86 | }, 87 | JsonQC::String(val) => json::encode_string(f, &val) 88 | JsonQC::Array(val) => { 89 | if val.len() == 0 { 90 | write!(f, "[]") 91 | 92 | } else { 93 | write!(f, "[")?; 94 | for item in val[..val.len()-1].iter() { 95 | write!(f, "{},", item)?; 96 | } 97 | write!(f, "{}", val[val.len()-1])?; 98 | write!(f, "]") 99 | } 100 | }, 101 | JsonQC::Object(val) => { 102 | let val_len = val.len(); 103 | if val_len == 0 { 104 | write!(f, "{{}}") 105 | 106 | } else { 107 | write!(f, "{{")?; 108 | for (i, prop) in val.iter().enumerate() { 109 | Self::encode_string(f, prop.key_ref())?; 110 | write!(f, ":{}", prop.value_ref())?; 111 | if i < (val_len - 1) { write!(f, ",")?; } 112 | } 113 | write!(f, "}}") 114 | } 115 | } 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/json_test.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2019 R Pratap Chakravarthy. All rights reserved. 2 | 3 | use std::f64; 4 | 5 | use crate::property::Property; 6 | use crate::Json; 7 | 8 | #[test] 9 | fn test_json_constructor() { 10 | use self::Json; 11 | 12 | assert_eq!(Json::new(10), Json::Integer("10".try_into().unwrap())); 13 | } 14 | 15 | #[test] 16 | fn test_simple_jsons() { 17 | use self::Json::{Array, Bool, Null, Object, String}; 18 | 19 | let jsons = include!("../testdata/test_simple.jsons"); 20 | let refs = include!("../testdata/test_simple.jsons.ref"); 21 | 22 | for (i, json) in jsons.iter().enumerate() { 23 | let mut value: Json = json.parse().unwrap(); 24 | value.compute().unwrap(); 25 | assert_eq!(value, refs[i], "testcase {}", i); 26 | } 27 | } 28 | 29 | #[test] 30 | fn test_simple_jsons_ref() { 31 | use self::Json::{Array, Bool, Null, Object, String}; 32 | 33 | let jsons = include!("../testdata/test_simple.jsons"); 34 | let refs = include!("../testdata/test_simple.jsons.ref"); 35 | 36 | let value: Json = jsons[51].parse().unwrap(); 37 | assert_eq!(value, refs[51]); 38 | 39 | let ref_jsons = include!("../testdata/test_simple.jsons.ref.jsons"); 40 | for (i, r) in refs.iter().enumerate() { 41 | let s = format!("{}", r); 42 | //println!("{} {}", i, &s); 43 | assert_eq!(&s, ref_jsons[i], "testcase: {}", i); 44 | } 45 | } 46 | 47 | #[test] 48 | fn test_convert() { 49 | let js: Json = true.into(); 50 | assert_eq!(js, Json::Bool(true)); 51 | 52 | let js: Json = 1024.into(); 53 | assert_eq!(js, Json::Integer(1024.into())); 54 | 55 | let js: Json = 1024.2.into(); 56 | assert_eq!(js, Json::Float(1024.2.into())); 57 | 58 | let js: Json = "hello world".to_string().into(); 59 | assert_eq!(js, Json::String("hello world".to_string())); 60 | 61 | let js: Json = 10_usize.into(); 62 | assert_eq!(js, Json::Integer(10.into())); 63 | 64 | let js: Json = 10_u64.into(); 65 | assert_eq!(js, Json::Integer(10.into())); 66 | 67 | let js: Json = 10_i32.into(); 68 | assert_eq!(js, Json::Integer(10.into())); 69 | } 70 | 71 | #[test] 72 | fn test_deferred() { 73 | let inp = r#" [10123.1231, 1231.123123, 1233.123123, 123.1231231, 12312e10]"#; 74 | let value: Json = inp.parse().unwrap(); 75 | let refval = Json::Array(vec![ 76 | Json::Float("10123.1231".try_into().unwrap()), 77 | Json::Float("1231.123123".try_into().unwrap()), 78 | Json::Float("1233.123123".try_into().unwrap()), 79 | Json::Float("123.1231231".try_into().unwrap()), 80 | Json::Float("12312e10".try_into().unwrap()), 81 | ]); 82 | assert_eq!(value, refval); 83 | } 84 | 85 | #[test] 86 | fn test_validate_sorted() { 87 | let json = r#"{"z":1.2,"a":[2, {"x":"y"}, true],"c":[null],"d":true}"#; 88 | let mut value: Json = json.parse().unwrap(); 89 | 90 | assert_eq!(value.validate(), Ok(())); 91 | 92 | let mut props: Vec = Vec::new(); 93 | let prop = vec![Property::new("x", Json::new("y"))]; 94 | props.push(Property::new( 95 | "a", 96 | Json::new(vec![Json::new(2), Json::new(prop), Json::new(true)]), 97 | )); 98 | props.push(Property::new("c", Json::new(vec![Json::Null]))); 99 | props.push(Property::new("d", Json::new(true))); 100 | props.push(Property::new("z", Json::new(1))); 101 | 102 | assert_eq!(value, Json::new(props)); 103 | 104 | let json = r#"true"#; 105 | let mut value: Json = json.parse().unwrap(); 106 | assert_eq!(value.validate(), Ok(())); 107 | assert_eq!(value, Json::new(true)); 108 | } 109 | 110 | #[test] 111 | fn test_compute() { 112 | let json = r#"{"z":1,"a":[2, {"x":"y"}, true],"c":[null],"d":3}"#; 113 | let mut value: Json = json.parse().unwrap(); 114 | 115 | assert_eq!(value.compute(), Ok(())); 116 | 117 | let mut props: Vec = Vec::new(); 118 | let prop = vec![Property::new("x", Json::new("y"))]; 119 | let items = vec![Json::new(2), Json::new(prop), Json::new(true)]; 120 | props.push(Property::new("a", Json::new(items))); 121 | props.push(Property::new("c", Json::new(vec![Json::Null]))); 122 | props.push(Property::new("d", Json::new(3))); 123 | props.push(Property::new("z", Json::new(1))); 124 | 125 | assert_eq!(value, Json::new(props)); 126 | } 127 | 128 | #[test] 129 | fn test_type_name() { 130 | assert_eq!(Json::Null.type_name(), "null".to_string()); 131 | assert_eq!(Json::new(true).type_name(), "bool".to_string()); 132 | assert_eq!(Json::new(false).type_name(), "bool".to_string()); 133 | assert_eq!(Json::new(10).type_name(), "integer".to_string()); 134 | assert_eq!(Json::new(10.2).type_name(), "float".to_string()); 135 | assert_eq!(Json::new("hello").type_name(), "string".to_string()); 136 | 137 | let items: Vec = vec![]; 138 | assert_eq!(Json::new(items).type_name(), "array".to_string()); 139 | 140 | let props: Vec = vec![Property::new("a", Json::new(true))]; 141 | assert_eq!(Json::new(props).type_name(), "object".to_string()); 142 | } 143 | 144 | #[test] 145 | fn test_json5_whitespace() { 146 | let text = 147 | "\u{0009} \u{000a} \u{000b} \u{000c} ".to_string() + "\u{00a0} \r \t \n 0x1234"; 148 | let json: Json = text.parse().unwrap(); 149 | assert_eq!(json.to_integer(), Json::new(0x1234).to_integer()); 150 | } 151 | 152 | #[test] 153 | fn test_json5_num() { 154 | let mut json: Json = "0x1234".parse().unwrap(); 155 | json.compute().unwrap(); 156 | assert_eq!(json, Json::new(0x1234)); 157 | 158 | let mut json: Json = "1234.".parse().unwrap(); 159 | json.compute().unwrap(); 160 | assert_eq!(json.to_float(), Json::new(1234.0).to_float()); 161 | 162 | let mut json: Json = ".1234".parse().unwrap(); 163 | json.compute().unwrap(); 164 | assert_eq!(json, Json::new(0.1234)); 165 | 166 | let mut json: Json = ".1234.".parse().unwrap(); 167 | json.compute().unwrap_err(); 168 | assert_eq!(json.to_float(), None); 169 | 170 | let mut json: Json = "[Infinity, -Infinity, NaN]".parse().unwrap(); 171 | json.compute().unwrap(); 172 | let value = Json::new(vec![ 173 | Json::new(f64::INFINITY), 174 | Json::new(f64::NEG_INFINITY), 175 | Json::new(f64::NAN), 176 | ]); 177 | assert_eq!(json, value); 178 | 179 | let mut json: Json = " [ 0xdecaf, -0xC0FFEE ]".parse().unwrap(); 180 | json.compute().unwrap(); 181 | let value = Json::new(vec![Json::new(0xdecaf), Json::new(-0xC0_FFEE)]); 182 | assert_eq!(json, value); 183 | 184 | let mut json: Json = "[ 123, 123.456, .456, 123e-456 ]".parse().unwrap(); 185 | json.compute().unwrap(); 186 | let value = Json::new(vec![ 187 | Json::new(123), 188 | Json::new(123.456), 189 | Json::new(0.456), 190 | Json::new(123e-456), 191 | ]); 192 | assert_eq!(json, value); 193 | } 194 | 195 | #[test] 196 | fn test_json5_array() { 197 | let json: Json = "[]".parse().unwrap(); 198 | let value = Json::new::>(vec![]); 199 | assert_eq!(json, value); 200 | 201 | let mut json: Json = r#"[ 1, true, "three", ]"#.parse().unwrap(); 202 | json.compute().unwrap(); 203 | let value = Json::new(vec![Json::new(1), Json::new(true), Json::new("three")]); 204 | assert_eq!(json, value); 205 | 206 | let json: Json = r#"[ [1, true, "three"], [4, "five", 0x6], ]"#.parse().unwrap(); 207 | let value = Json::new(vec![ 208 | Json::new(vec![Json::new(1), Json::new(true), Json::new("three")]), 209 | Json::new(vec![Json::new(4), Json::new("five"), Json::new(0x6)]), 210 | ]); 211 | assert_eq!(json, value); 212 | } 213 | 214 | #[test] 215 | fn test_json5_object() { 216 | let json: Json = "{}".parse().unwrap(); 217 | let value = Json::new::>(vec![]); 218 | assert_eq!(json, value); 219 | 220 | let mut json: Json = "{ width: 1920, height: 1080, }".parse().unwrap(); 221 | json.compute().unwrap(); 222 | let value = Json::new(vec![ 223 | Property::new("height", 1080.into()), 224 | Property::new("width", 1920.into()), 225 | ]); 226 | assert_eq!(json, value); 227 | 228 | let mut json: Json = 229 | r#"{ image: { width: 1920, height: 1080, "aspect-ratio": "16:9", } }"# 230 | .parse() 231 | .unwrap(); 232 | json.compute().unwrap(); 233 | let props = Json::new(vec![ 234 | Property::new("aspect-ratio", "16:9".into()), 235 | Property::new("height", 1080.into()), 236 | Property::new("width", 1920.into()), 237 | ]); 238 | let value = Json::new(vec![Property::new("image", props)]); 239 | assert_eq!(json, value); 240 | 241 | let mut json: Json = 242 | r#"[ { name: "Joe", age: 27 }, { name: "Jane", age: 32 }, ]"#.parse().unwrap(); 243 | json.compute().unwrap(); 244 | let obj1 = Json::new::>(vec![ 245 | Property::new("age", 27.into()), 246 | Property::new("name", "Joe".into()), 247 | ]); 248 | let obj2 = Json::new::>(vec![ 249 | Property::new("age", 32.into()), 250 | Property::new("name", "Jane".into()), 251 | ]); 252 | let value = Json::new(vec![obj1, obj2]); 253 | assert_eq!(json, value); 254 | } 255 | 256 | #[test] 257 | fn test_partial_eq() { 258 | let a = Json::new(f64::INFINITY); 259 | let b = Json::new(f64::NEG_INFINITY); 260 | let c = Json::new(f64::NAN); 261 | let d = Json::new(0.2); 262 | 263 | assert!(a != b); 264 | assert!(a != c); 265 | assert!(a != d); 266 | assert!(b != a); 267 | assert!(b != c); 268 | assert!(b != d); 269 | assert!(c != a); 270 | assert!(c != b); 271 | assert!(c != d); 272 | assert!(d != a); 273 | assert!(d != b); 274 | assert!(d != c); 275 | 276 | assert!(Json::minbound() == Json::minbound()); 277 | assert!(Json::maxbound() == Json::maxbound()); 278 | assert!(Json::minbound() != Json::maxbound()); 279 | assert!(Json::maxbound() != Json::minbound()); 280 | } 281 | 282 | #[test] 283 | fn test_partial_ord1() { 284 | assert!(Json::Null < Json::new(true)); 285 | assert!(Json::Null < Json::new(false)); 286 | assert!(Json::Null < Json::new(10)); 287 | assert!(Json::Null < Json::new(1.0)); 288 | assert!(Json::Null < Json::new("hello world")); 289 | assert!(Json::Null < Json::new::>(vec![10.into()])); 290 | assert!( 291 | Json::Null < Json::new::>(vec![Property::new("key", 10.into())]) 292 | ); 293 | 294 | let value = Json::new(false); 295 | assert!(value > Json::Null); 296 | assert!(value == Json::new(false)); 297 | assert!(value < Json::new(true)); 298 | assert!(value < Json::new(10)); 299 | assert!(value < Json::new(1.0)); 300 | assert!(value < Json::new("hello world")); 301 | assert!(value < Json::new::>(vec![10.into()])); 302 | assert!(value < Json::new::>(vec![Property::new("key", 10.into())])); 303 | 304 | let value = Json::new(true); 305 | assert!(value > Json::Null); 306 | assert!(value > Json::new(false)); 307 | assert!(value == Json::new(true)); 308 | assert!(value < Json::new(10)); 309 | assert!(value < Json::new(1.0)); 310 | assert!(value < Json::new("hello world")); 311 | assert!(value < Json::new::>(vec![10.into()])); 312 | assert!(value < Json::new::>(vec![Property::new("key", 10.into())])); 313 | } 314 | 315 | #[test] 316 | fn test_partial_ord2() { 317 | let value = Json::new(10); 318 | assert!(value > Json::Null); 319 | assert!(value > Json::new(false)); 320 | assert!(value > Json::new(true)); 321 | assert!(value == Json::new(10)); 322 | assert!(value == Json::new(10.0)); 323 | assert!(value < Json::new("hello world")); 324 | assert!(value < Json::new::>(vec![10.into()])); 325 | assert!(value < Json::new::>(vec![Property::new("key", 10.into())])); 326 | 327 | let value = Json::new(10.0); 328 | assert!(value > Json::Null); 329 | assert!(value > Json::new(false)); 330 | assert!(value > Json::new(true)); 331 | assert!(value == Json::new(10)); 332 | assert!(value == Json::new(10.0)); 333 | assert!(value < Json::new("hello world")); 334 | assert!(value < Json::new::>(vec![10.into()])); 335 | assert!(value < Json::new::>(vec![Property::new("key", 10.into())])); 336 | 337 | let value = Json::new("hello world"); 338 | assert!(value > Json::Null); 339 | assert!(value > Json::new(false)); 340 | assert!(value > Json::new(true)); 341 | assert!(value > Json::new(10)); 342 | assert!(value > Json::new(10.0)); 343 | assert!(value == Json::new("hello world")); 344 | assert!(value < Json::new::>(vec![10.into()])); 345 | assert!(value < Json::new::>(vec![Property::new("key", 10.into())])); 346 | } 347 | 348 | #[test] 349 | fn test_partial_ord3() { 350 | let value: Json = "[10,20]".parse().unwrap(); 351 | assert!(value > Json::Null); 352 | assert!(value > Json::new(false)); 353 | assert!(value > Json::new(true)); 354 | assert!(value > Json::new(10)); 355 | assert!(value > Json::new(10.0)); 356 | assert!(value > Json::new("hello world")); 357 | assert!(value == Json::new::>(vec![10.into(), 20.into()])); 358 | assert!(value > Json::new::>(vec![10.into()])); 359 | assert!(Json::new::>(vec![10.into()]) < value); 360 | assert!(value < Json::new::>(vec![Property::new("key", 10.into())])); 361 | 362 | let value: Json = r#"{"key1": 10, "key2":20}"#.parse().unwrap(); 363 | assert!(value > Json::Null); 364 | assert!(value > Json::new(false)); 365 | assert!(value > Json::new(true)); 366 | assert!(value > Json::new(10)); 367 | assert!(value > Json::new(10.0)); 368 | assert!(value > Json::new("hello world")); 369 | assert!(value > Json::new::>(vec![10.into()])); 370 | assert!( 371 | value 372 | > Json::new::>(vec![ 373 | Property::new("key1", 10.into()), 374 | Property::new("key2", 10.into()) 375 | ]) 376 | ); 377 | assert!( 378 | value 379 | < Json::new::>(vec![ 380 | Property::new("key1", 20.into()), 381 | Property::new("key2", 10.into()) 382 | ]) 383 | ); 384 | assert!( 385 | value 386 | > Json::new::>(vec![ 387 | Property::new("key1", 5.into()), 388 | Property::new("key2", 10.into()) 389 | ]) 390 | ); 391 | assert!(value > Json::new::>(vec![Property::new("key1", 10.into())])); 392 | assert!(Json::new::>(vec![Property::new("key1", 10.into())]) < value); 393 | } 394 | 395 | #[test] 396 | fn test_partial_ord4() { 397 | let lhs: Json = "[]".parse().unwrap(); 398 | let rhs: Json = "[10]".parse().unwrap(); 399 | assert!(lhs < rhs); 400 | assert!(rhs > lhs); 401 | 402 | let lhs: Json = r#"{}"#.parse().unwrap(); 403 | let rhs: Json = r#"{"a": 10}"#.parse().unwrap(); 404 | assert!(lhs < rhs); 405 | assert!(rhs > lhs); 406 | 407 | let lhs: Json = r#"-1.0"#.parse().unwrap(); 408 | let rhs: Json = r#"1.0"#.parse().unwrap(); 409 | assert!(lhs < rhs); 410 | assert!(rhs > lhs); 411 | assert!(rhs != lhs); 412 | 413 | let lhs: Json = r#"-0.0"#.parse().unwrap(); 414 | let rhs: Json = r#"0.0"#.parse().unwrap(); 415 | assert!(lhs < rhs); 416 | assert!(rhs > lhs); 417 | assert!(lhs == rhs); 418 | assert!(lhs <= rhs); 419 | } 420 | 421 | #[test] 422 | fn test_bounds() { 423 | assert!(Json::minbound() == Json::minbound()); 424 | assert!(Json::minbound() < Json::Null); 425 | assert!(Json::minbound() < true.into()); 426 | assert!(Json::minbound() < false.into()); 427 | assert!(Json::minbound() < 10.into()); 428 | assert!(Json::minbound() < 10.2.into()); 429 | assert!(Json::minbound() < "hello world".into()); 430 | assert!(Json::minbound() < "[null]".parse().unwrap()); 431 | assert!(Json::minbound() < r#"{"key":10}"#.parse().unwrap()); 432 | assert!(Json::minbound() < Json::maxbound()); 433 | 434 | assert!(Json::maxbound() > Json::minbound()); 435 | assert!(Json::maxbound() > Json::Null); 436 | assert!(Json::maxbound() > true.into()); 437 | assert!(Json::maxbound() > false.into()); 438 | assert!(Json::maxbound() > 10.into()); 439 | assert!(Json::maxbound() > 10.2.into()); 440 | assert!(Json::maxbound() > "hello world".into()); 441 | assert!(Json::maxbound() > "[null]".parse().unwrap()); 442 | assert!(Json::maxbound() > r#"{"key":10}"#.parse().unwrap()); 443 | assert!(Json::maxbound() == Json::maxbound()); 444 | } 445 | 446 | #[test] 447 | fn test_boolean_coersion() { 448 | assert!(!bool::from(Json::Null)); 449 | assert!(!bool::from(Json::new(false))); 450 | assert!(bool::from(Json::new(true))); 451 | assert!(!bool::from(Json::new(0))); 452 | assert!(!bool::from(Json::new(0.0))); 453 | assert!(bool::from(Json::new(0.1))); 454 | assert!(bool::from(Json::new(-0.1))); 455 | assert!(bool::from(Json::new(-1))); 456 | assert!(!bool::from(Json::new(""))); 457 | assert!(bool::from(Json::new("hello"))); 458 | let value: Vec = vec![]; 459 | assert!(!bool::from(Json::new(value))); 460 | let value: Vec = vec![1.into()]; 461 | assert!(bool::from(Json::new(value))); 462 | let value: Vec = vec![]; 463 | assert!(!bool::from(Json::new(value))); 464 | let value: Vec = vec![Property::new("a", 10.into())]; 465 | assert!(bool::from(Json::new(value))); 466 | } 467 | -------------------------------------------------------------------------------- /src/jsons.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2019 R Pratap Chakravarthy. All rights reserved. 2 | 3 | use std::io; 4 | 5 | use unicode_reader::CodePoints; 6 | 7 | use crate::{json::Json, Error, Result}; 8 | 9 | /// Jsons can parse a stream of JSON text supplied by any [Read] instance. 10 | /// For Example: 11 | /// 12 | /// ``` 13 | /// let file = std::fs::File::open("testdata/stream1.jsons").unwrap(); 14 | /// let mut iter: jsondata::Jsons = file.into(); 15 | /// 16 | /// for json in iter { 17 | /// println!("{:?}", json) 18 | /// } 19 | /// ``` 20 | /// 21 | /// Note that the iterated value is of type ``Result``, 22 | /// where errors can be handled in following manner : 23 | /// 24 | /// ```ignore 25 | /// for json in iter { 26 | /// match json { 27 | /// Ok(value) if value.integer() > 100 => { 28 | /// /* handle Json value*/ 29 | /// }, 30 | /// Ok(value) if value.is_error() => { 31 | /// /* value.error() to fetch the error String */ 32 | /// }, 33 | /// Err(err) => { 34 | /// /* handle error returned by the Read instance */ 35 | /// }, 36 | /// } 37 | /// } 38 | /// ``` 39 | /// 40 | /// [Read]: std::io::Read 41 | pub struct Jsons 42 | where 43 | R: io::Read, 44 | { 45 | codes: CodePoints>, 46 | quant: String, 47 | } 48 | 49 | impl From for Jsons 50 | where 51 | R: io::Read, 52 | { 53 | fn from(input: R) -> Jsons { 54 | Jsons { 55 | codes: input.into(), 56 | quant: String::with_capacity(1024), 57 | } 58 | } 59 | } 60 | 61 | impl Iterator for Jsons 62 | where 63 | R: io::Read, 64 | { 65 | type Item = Result; 66 | 67 | fn next(&mut self) -> Option { 68 | let mut markers = String::new(); 69 | let mut ok_ch = self.read_whitespace()?; 70 | loop { 71 | let ch = match ok_ch { 72 | Ok(ch) => { 73 | //println!("{}", ch); 74 | self.quant.push(ch); 75 | match ch { 76 | '{' => markers.push('}'), 77 | '[' => markers.push(']'), 78 | '}' | ']' => loop { 79 | if let Some(m) = markers.pop() { 80 | if m == ch { 81 | break; 82 | } 83 | } else if markers.is_empty() { 84 | break; 85 | } 86 | }, 87 | '"' => match Jsons::read_string(self)? { 88 | Ok(_) => (), 89 | Err(err) => break Some(Err(err)), 90 | }, 91 | _ => (), 92 | } 93 | //println!("loop {:?} {}", self.quant.as_bytes(), ch); 94 | ch 95 | } 96 | Err(err) => break Some(Err(err)), 97 | }; 98 | let eov = ch.is_whitespace() || ch == '}' || ch == ']' || ch == '"'; 99 | if markers.is_empty() && eov { 100 | let res = match self.quant.parse() { 101 | Ok(json) => Some(Ok(json)), 102 | Err(s) => Some(Ok(Json::__Error(s))), 103 | }; 104 | //println!("quant {:?} {:?}", self.quant.as_bytes(), res); 105 | self.quant.truncate(0); 106 | break res; 107 | } 108 | ok_ch = match self.codes.next() { 109 | Some(res) => err_at!(IoError, res), 110 | None if !self.quant.is_empty() => { 111 | let res = match self.quant.parse() { 112 | Ok(json) => Some(Ok(json)), 113 | Err(s) => Some(Ok(Json::__Error(s))), 114 | }; 115 | //println!("quant {:?} {:?}", self.quant.as_bytes(), res); 116 | self.quant.truncate(0); 117 | break res; 118 | } 119 | None => break None, 120 | } 121 | } 122 | } 123 | } 124 | 125 | impl Jsons 126 | where 127 | R: io::Read, 128 | { 129 | fn read_string(&mut self) -> Option> { 130 | let mut escape = false; 131 | loop { 132 | match self.codes.next() { 133 | Some(Ok(ch)) if escape => { 134 | self.quant.push(ch); 135 | escape = false; 136 | } 137 | Some(Ok('\\')) => { 138 | self.quant.push('\\'); 139 | escape = true; 140 | } 141 | Some(Ok('"')) => { 142 | self.quant.push('"'); 143 | break Some(Ok(())); 144 | } 145 | Some(Ok(ch)) => self.quant.push(ch), 146 | Some(Err(err)) => break Some(err_at!(IoError, msg: "{}", err)), 147 | None => break Some(Ok(())), 148 | } 149 | } 150 | } 151 | 152 | fn read_whitespace(&mut self) -> Option> { 153 | loop { 154 | match self.codes.next()? { 155 | Ok(ch) if !ch.is_whitespace() => break Some(Ok(ch)), 156 | Ok(_) => (), 157 | res => break Some(err_at!(IoError, res)), 158 | } 159 | } 160 | } 161 | } 162 | 163 | #[cfg(test)] 164 | #[path = "jsons_test.rs"] 165 | mod jsons_test; 166 | -------------------------------------------------------------------------------- /src/jsons_test.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2019 R Pratap Chakravarthy. All rights reserved. 2 | 3 | use std::fs::File; 4 | 5 | use crate::{json::Json, jsons::Jsons, property::Property}; 6 | 7 | #[test] 8 | fn test_stream0() { 9 | let mut js: Jsons<&[u8]> = b"".as_ref().into(); 10 | assert!(js.next().is_none()); 11 | 12 | let mut js: Jsons<&[u8]> = b" \t \r \n ".as_ref().into(); 13 | assert!(js.next().is_none()); 14 | 15 | let mut js: Jsons<&[u8]> = b" 1".as_ref().into(); 16 | assert_eq!(js.next().unwrap().unwrap(), Json::new(1)); 17 | 18 | let mut js: Jsons<&[u8]> = b" n".as_ref().into(); 19 | let value = js.next().unwrap().unwrap(); 20 | assert!(value.is_error()); 21 | match value.to_error() { 22 | Some(err) => { 23 | assert!(err.to_string().contains("expected null at offset:0 line:1 col:1")) 24 | } 25 | _ => unreachable!(), 26 | } 27 | } 28 | 29 | #[test] 30 | fn test_stream1() { 31 | let file = File::open("testdata/stream1.jsons").unwrap(); 32 | let mut js: Jsons = file.into(); 33 | 34 | assert_eq!(js.next().unwrap().unwrap(), Json::new(1)); 35 | 36 | assert_eq!(js.next().unwrap().unwrap(), Json::Null); 37 | assert_eq!(js.next().unwrap().unwrap(), Json::new(true)); 38 | assert_eq!(js.next().unwrap().unwrap(), Json::new(false)); 39 | 40 | assert_eq!(js.next().unwrap().unwrap().to_integer(), Some(102)); 41 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(10.2)); 42 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(0.2)); 43 | 44 | assert_eq!(js.next().unwrap().unwrap().to_integer(), Some(0)); 45 | assert_eq!(js.next().unwrap().unwrap().to_integer(), Some(100)); 46 | assert_eq!(js.next().unwrap().unwrap().to_integer(), Some(1)); 47 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(0.0)); 48 | 49 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(2.0)); 50 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(0.2)); 51 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(0.02)); 52 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(0.0)); 53 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(0.0)); 54 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(20.0)); 55 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(20.0)); 56 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(200.0)); 57 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(0.0)); 58 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(0.2)); 59 | } 60 | 61 | #[test] 62 | fn test_stream11() { 63 | let file = File::open("testdata/stream11.jsons").unwrap(); 64 | let mut js: Jsons = file.into(); 65 | 66 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(0.2)); 67 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(2.0)); 68 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(0.0)); 69 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(0.2)); 70 | assert_eq!(js.next().unwrap().unwrap().to_integer(), Some(-102)); 71 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(-10.2)); 72 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(-0.2)); 73 | assert_eq!(js.next().unwrap().unwrap().to_integer(), Some(-0)); 74 | 75 | assert_eq!(js.next().unwrap().unwrap().to_integer(), Some(-100)); 76 | assert_eq!(js.next().unwrap().unwrap().to_integer(), Some(-1)); 77 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(-00.00)); 78 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(-2.00)); 79 | 80 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(-0.2)); 81 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(-0.02)); 82 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(-0.0)); 83 | assert_eq!(js.next().unwrap().unwrap().to_float(), Some(-20.0)); 84 | } 85 | 86 | #[test] 87 | fn test_stream2() { 88 | let file = File::open("testdata/stream2.jsons").unwrap(); 89 | let mut js: Jsons = file.into(); 90 | 91 | assert_eq!(js.next().unwrap().unwrap().as_str(), Some("hello\" \r\t")); 92 | 93 | assert_eq!(js.next().unwrap().unwrap().as_str(), Some("helloȴ\\ 𝄞")); 94 | 95 | assert_eq!( 96 | js.next().unwrap().unwrap().as_str(), 97 | Some("\'é\' character is one Unicode code point é while \'é\' e\u{301} ") 98 | ); 99 | 100 | assert_eq!(js.next().unwrap().unwrap(), Json::new::>(vec![])); 101 | assert_eq!(js.next().unwrap().unwrap(), Json::new(vec![Json::new(10)])); 102 | assert_eq!( 103 | js.next().unwrap().unwrap(), 104 | Json::new(vec![ 105 | Json::Null, 106 | true.into(), 107 | false.into(), 108 | 10.into(), 109 | "tru\"e".into(), 110 | ]) 111 | ); 112 | 113 | assert_eq!(js.next().unwrap().unwrap(), "汉语 / 漢語; Hàn\u{8} \tyǔ ".into()); 114 | } 115 | 116 | #[test] 117 | fn test_stream3() { 118 | let file = File::open("testdata/stream3.jsons").unwrap(); 119 | let mut js: Jsons = file.into(); 120 | 121 | assert_eq!( 122 | js.next().unwrap().unwrap(), 123 | Json::new(vec![ 124 | Json::Null, 125 | true.into(), 126 | false.into(), 127 | "hello\" \\ / \u{8} \u{c}\n\r\t".into() 128 | ]) 129 | ); 130 | assert_eq!( 131 | js.next().unwrap().unwrap(), 132 | Json::new::>(vec![ 133 | 102.into(), 134 | 10.2.into(), 135 | 0.2.into(), 136 | 0.into(), 137 | "helloȴ\\ 𝄞".into(), 138 | ]) 139 | ); 140 | 141 | assert_eq!( 142 | js.next().unwrap().unwrap(), 143 | Json::new::>(vec![ 144 | 100.into(), 145 | 1.into(), 146 | 0.0.into(), 147 | 2.0.into(), 148 | "汉语 / 漢語; Hàn\u{8} \tyǔ ".into() 149 | ]) 150 | ); 151 | 152 | assert_eq!( 153 | js.next().unwrap().unwrap(), 154 | Json::new::>(vec![ 155 | 0.2.into(), 156 | 0.02.into(), 157 | 0.0.into(), 158 | 0.2.into(), 159 | 0.2.into(), 160 | ]) 161 | ); 162 | 163 | assert_eq!( 164 | js.next().unwrap().unwrap(), 165 | Json::new::>(vec![ 166 | (-102).into(), 167 | (-100).into(), 168 | (-0.0).into(), 169 | (-20.0).into(), 170 | ]) 171 | ); 172 | 173 | assert_eq!(js.next().unwrap().unwrap(), Json::new::>(vec![])); 174 | assert_eq!( 175 | js.next().unwrap().unwrap(), 176 | Json::new(vec![Property::new("key1", "value1".into())]) 177 | ); 178 | 179 | assert_eq!( 180 | js.next().unwrap().unwrap(), 181 | Json::new(vec![ 182 | Property::new("key1", "value1".into()), 183 | Property::new("key2", "value2".into()), 184 | ]) 185 | ); 186 | 187 | assert_eq!( 188 | js.next().unwrap().unwrap(), 189 | Json::new(vec![ 190 | Property::new("z", 1.into()), 191 | Property::new("a", 1.into()), 192 | Property::new("c", 1.into()), 193 | Property::new("d", 1.into()), 194 | Property::new("f", 1.into()), 195 | Property::new("e", 1.into()), 196 | Property::new("b", 1.into()), 197 | Property::new("x", 1.into()), 198 | ]) 199 | ); 200 | 201 | let obj = Json::new(vec![Property::new("key3", 20.into())]); 202 | let obj = Json::new(vec![Property::new("key2", obj)]); 203 | let arr = Json::new::>(vec!["world".into(), obj]); 204 | let obj = Json::new(vec![Property::new("key1", arr)]); 205 | let arr = Json::new::>(vec!["hello".into(), obj]); 206 | assert_eq!(js.next().unwrap().unwrap(), arr); 207 | } 208 | -------------------------------------------------------------------------------- /src/lex.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2019 R Pratap Chakravarthy. All rights reserved. 2 | 3 | // This is local type, useful to pinpoint position of lex-failures. 4 | #[derive(Debug)] 5 | pub struct Lex { 6 | pub off: usize, 7 | pub row: usize, 8 | pub col: usize, 9 | } 10 | 11 | impl Lex { 12 | pub fn new(off: usize, row: usize, col: usize) -> Lex { 13 | Lex { off, row, col } 14 | } 15 | 16 | pub fn incr_col(&mut self, i: usize) { 17 | self.off += i; 18 | self.col += i; 19 | } 20 | 21 | pub fn format(&self, prefix: &str) -> String { 22 | format!("{} at offset:{} line:{} col:{}", prefix, self.off, self.row, self.col) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2019 R Pratap Chakravarthy. All rights reserved. 2 | 3 | //! Jsondata is yet another [JSON] implementation in Rust, but 4 | //! optimized for big-data and document-databases that store 5 | //! documents in [JSON] format. Following is the scope defined 6 | //! for this package: 7 | //! 8 | //! * Support for 128-bit signed integers. 9 | //! * Deferred conversion of numbers. 10 | //! * Serialization from Rust native type, [`Json`], to JSON text. 11 | //! * De-serialization from JSON text to Rust native [`Json`] type. 12 | //! * [CRUD] operation on JSON documents, using [JSON Pointer]. 13 | //! * Sorted keys in property object. 14 | //! * Streaming JSON parser, using [`Jsons`] type. 15 | //! * Support [JSON5](http://json5.org) standard. 16 | //! * Common arithmetic and logical ops implemented for [`Json`]. 17 | //! * [`Json`] objects can be compared and sorted. 18 | //! 19 | //! To parse JSON text, use [`str::parse`]: 20 | //! 21 | //! ``` 22 | //! let text = r#"[null,true,false,10,"true"]"#; 23 | //! let json = text.parse::().unwrap(); 24 | //! ``` 25 | //! 26 | //! To serialise [`Json`] type to JSON text: 27 | //! 28 | //! ``` 29 | //! let text = r#"[null,true,false,10,"true"]"#; 30 | //! let json = text.parse::().unwrap(); 31 | //! 32 | //! let text1 = json.to_string(); // one way to serialize to JSON 33 | //! let text2 = format!("{}", json); // another way to serialize to JSON 34 | //! assert_eq!(text1, text2); 35 | //! ``` 36 | //! 37 | //! When parsing a JSON text to [Json] instance, numbers are not parsed 38 | //! right away, hence calls to [integer] and [float] methods will have 39 | //! to compute the value every time, 40 | //! 41 | //! ``` 42 | //! let mut json = "1000".parse::().unwrap(); 43 | //! json.to_integer().unwrap(); // "1000" is parsed 44 | //! json.to_integer().unwrap(); // "1000" is parsed again 45 | //! 46 | //! match json.compute() { // pre-compute all numbers in the json document. 47 | //! Ok(_) => (), 48 | //! Err(s) => println!("{:?}", s), 49 | //! } 50 | //! ``` 51 | //! 52 | //! If JSON text is going to come from untrusted parties, 53 | //! 54 | //! ``` 55 | //! let mut json = r#"{"a": 1000}"#.parse::().unwrap(); 56 | //! match json.validate() { // validate 57 | //! Ok(_) => (), 58 | //! Err(s) => println!("{:?}", s), 59 | //! } 60 | //! ``` 61 | //! 62 | //! Boolean table for [`Json`] data: 63 | //! ================================ 64 | //! 65 | //! In boolean expression context, like BitAnd, BitOr, BitXor and Not, 66 | //! [`Json`] value will be automatically converted to boolean value 67 | //! with following rules. 68 | //! 69 | //! | data type | boolean value | 70 | //! |-------------------|---------------| 71 | //! | null | false | 72 | //! | boolean true | true | 73 | //! | boolean false | false | 74 | //! | integer-zero | false | 75 | //! | integer-non-zero | true | 76 | //! | float-zero | false | 77 | //! | float-non-zero | true | 78 | //! | string-empty | false | 79 | //! | string | true | 80 | //! | array-empty | false | 81 | //! | array-non-empty | true | 82 | //! | object-empty | false | 83 | //! | object-non-empty | true | 84 | //! 85 | //! 86 | //! JSON operations: 87 | //! ================ 88 | //! 89 | //! [`Json`] implements common arithmetic and logical operations like 90 | //! [Add], [Sub], [Mul], [Div], [Rem], [Neg], [Shl], [Shr], [BitAnd], 91 | //! [BitOr], [BitXor], [Not], [Index]. 92 | //! 93 | //! *Addition:* 94 | //! 95 | //! * Adding with Null, shall return the same value. 96 | //! * Integer addition and Float addition respectively follow 97 | //! i128 and f64 rules. When adding mixed numbers, integers 98 | //! are type casted to floats. 99 | //! * Adding two string, shall concatenate and return a new string. 100 | //! * Adding two array, shall return a new array with first array's 101 | //! element and later second array's element. 102 | //! * Adding two object, is similar to adding two array except that if 103 | //! both object have same property, then property from first object 104 | //! is overwritten by property from second object. 105 | //! 106 | //! All other combination shall return [`Error::AddFail`]. 107 | //! 108 | //! *Subraction:* 109 | //! 110 | //! * Subracting with Null, shall return the same value. 111 | //! * Integer subraction and Float subraction respectively follow 112 | //! i128 and f64 rules. When subracting mixed numbers, integers 113 | //! are type casted to floats. 114 | //! * Subracting an array from another array, shall return a new array 115 | //! with remaining items after removing second array's item from the 116 | //! the first array. 117 | //! * Subracting two object, is similar to subracting two array. 118 | //! 119 | //! All other combination shall return [`Error::SubFail`]. 120 | //! 121 | //! *Multiplication:* 122 | //! 123 | //! * Multiplying with Null, shall return Null. 124 | //! * Integer multiplication and Float multiplication respectively 125 | //! follow i128 and f64 rules. When multiplying mixed numbers, 126 | //! integers are type casted to floats. 127 | //! * Multiplying integer with string or vice-versa, shall repeat 128 | //! the string operand as many times specified by the integer value 129 | //! and return a new string. 130 | //! 131 | //! All other combination shall return [`Error::MulFail`]. 132 | //! 133 | //! *Division:* 134 | //! 135 | //! * Dividing with Null, shall return Null. 136 | //! * Integer division and Float division respectively follow 137 | //! i128 and f64 rules. When dividing with mixed numbers, 138 | //! integers are type casted to floats. 139 | //! 140 | //! All other combination shall return [`Error::DivFail`]. 141 | //! 142 | //! *Reminder:* 143 | //! 144 | //! * Finding reminder with Null, shall return Null. 145 | //! * Integer reminder and Float reminder respectively follow 146 | //! i128 and f64 rules. When dividing with mixed numbers, 147 | //! integers are type casted to floats. 148 | //! 149 | //! All other combination shall return [`Error::RemFail`]. 150 | //! 151 | //! *Negation:* 152 | //! 153 | //! * Negating Null, shall return Null. 154 | //! * Negating Integer and Float shall respectively follow 155 | //! i128 and f64 rules. 156 | //! 157 | //! All other combination shall return [`Error::NegFail`]. 158 | //! 159 | //! *Shift-right / Shift-left:* 160 | //! 161 | //! Applicable only for integers and follow the same behaviour as 162 | //! that of [`i128`]. 163 | //! 164 | //! All other combination shall return [`Error::ShrFail`] / 165 | //! [`Error::ShlFail`]. 166 | //! 167 | //! *BitAnd:* 168 | //! 169 | //! * For integer operands, BitAnd follows normal integer bitwise-and 170 | //! rules. 171 | //! * For other Json variant, operand is converted to boolean 172 | //! version and performs logical AND. 173 | //! 174 | //! *BitOr:* 175 | //! 176 | //! * For integer operands, BitOr follows normal integer bitwise-or 177 | //! rules. 178 | //! * For other Json variant, operand is converted to boolean 179 | //! version and performs logical OR. 180 | //! 181 | //! *BitXor:* 182 | //! 183 | //! * For integer operands, BitXor follows normal integer bitwise-xor 184 | //! rules. 185 | //! * For other Json variant, operand is converted to boolean 186 | //! version and performs logical XOR. 187 | //! 188 | //! [JSON]: https://tools.ietf.org/html/rfc8259 189 | //! [CRUD]: https://en.wikipedia.org/wiki/Create,_read,_update_and_delete 190 | //! [JSON Pointer]: https://tools.ietf.org/html/rfc6901 191 | //! [integer]: enum.Json.html#method.integer 192 | //! [float]: enum.Json.html#method.float 193 | 194 | #![doc( 195 | html_favicon_url = "https://cdn4.iconfinder.com/data/icons/fugue/icon_shadowless/json.png" 196 | )] 197 | #![doc( 198 | html_logo_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c9/JSON_vector_logo.svg/1024px-JSON_vector_logo.svg.png" 199 | )] 200 | 201 | #[allow(unused_imports)] 202 | use std::ops::{Add, BitAnd, BitOr, BitXor, Div}; 203 | #[allow(unused_imports)] 204 | use std::ops::{Index, Mul, Neg, Not, Rem, Shl, Shr, Sub}; 205 | 206 | #[doc(hidden)] 207 | pub use jsondata_derive::*; 208 | 209 | /// Result type, for jsondata functions and methods, that require a 210 | /// success or failure variant. 211 | pub type Result = std::result::Result; 212 | 213 | #[macro_use] 214 | mod error; 215 | mod json; 216 | mod jsons; 217 | mod lex; 218 | mod num; 219 | mod ops; 220 | mod parse; 221 | mod property; 222 | 223 | pub mod jptr; 224 | 225 | // Re-exports for API documentation. 226 | pub use error::Error; 227 | pub use json::Json; 228 | pub use jsons::Jsons; 229 | pub use property::Property; 230 | -------------------------------------------------------------------------------- /src/num.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2019 R Pratap Chakravarthy. All rights reserved. 2 | 3 | use std::cmp::Ordering; 4 | 5 | use crate::{Error, Result}; 6 | 7 | #[inline] 8 | fn parse_integer(text: &[u8]) -> Result { 9 | use std::str::from_utf8_unchecked; 10 | 11 | let res = unsafe { 12 | if text.len() > 2 && text[0] == 48 && text[1] == 120 13 | // "0x" 14 | { 15 | i128::from_str_radix(from_utf8_unchecked(&text[2..]), 16) 16 | } else if text.len() > 3 && text[0] == 45 && text[1] == 48 && text[2] == 120 17 | // "-0x" 18 | { 19 | i128::from_str_radix(from_utf8_unchecked(&text[3..]), 16).map(|x| -x) 20 | } else { 21 | from_utf8_unchecked(text).parse::() 22 | } 23 | }; 24 | err_at!(InvalidNumber, res) 25 | } 26 | 27 | #[inline] 28 | fn parse_float(text: &[u8]) -> Result { 29 | use std::str::from_utf8_unchecked; 30 | 31 | err_at!(InvalidNumber, unsafe { from_utf8_unchecked(text).parse::() }) 32 | } 33 | 34 | #[derive(Clone, Debug)] 35 | pub enum Integral { 36 | Text { len: usize, bytes: [u8; 128] }, 37 | Data { value: i128 }, 38 | } 39 | 40 | macro_rules! convert_to_integral { 41 | ($($from:ty),*) => ( 42 | $( 43 | impl From<$from> for Integral { 44 | fn from(val: $from) -> Integral { 45 | Integral::Data { value: i128::try_from(val).unwrap() } 46 | } 47 | } 48 | )* 49 | ); 50 | } 51 | 52 | convert_to_integral! {u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize } 53 | 54 | impl<'a> TryFrom<&'a str> for Integral { 55 | type Error = Error; 56 | 57 | fn try_from(val: &str) -> Result { 58 | let src = val.as_bytes(); 59 | let val = match src.len() { 60 | n if n < 128 => { 61 | let mut bytes = [0_u8; 128]; 62 | bytes[..n].copy_from_slice(src); 63 | Integral::Text { len: n, bytes } 64 | } 65 | _ => { 66 | let value = parse_integer(src)?; 67 | Integral::Data { value } 68 | } 69 | }; 70 | 71 | Ok(val) 72 | } 73 | } 74 | 75 | impl Eq for Integral {} 76 | 77 | impl PartialEq for Integral { 78 | fn eq(&self, other: &Integral) -> bool { 79 | use Integral::{Data, Text}; 80 | 81 | match (self, other) { 82 | (Data { value: a }, Data { value: b }) => a.eq(b), 83 | (Text { len, bytes }, Data { value: b }) => { 84 | parse_integer(&bytes[..*len]).map(|a| a.eq(b)).unwrap() 85 | } 86 | (Data { value: a }, Text { len, bytes }) => { 87 | parse_integer(&bytes[..*len]).map(|b| a.eq(&b)).unwrap() 88 | } 89 | ( 90 | Text { len: a_len, bytes: a_bytes }, 91 | Text { len: b_len, bytes: b_bytes }, 92 | ) => { 93 | let a = parse_integer(&a_bytes[..*a_len]).unwrap(); 94 | let b = parse_integer(&b_bytes[..*b_len]).unwrap(); 95 | a.eq(&b) 96 | } 97 | } 98 | } 99 | } 100 | 101 | impl PartialOrd for Integral { 102 | fn partial_cmp(&self, other: &Integral) -> Option { 103 | use Integral::{Data, Text}; 104 | 105 | match (self, other) { 106 | (Data { value: a }, Data { value: b }) => a.partial_cmp(b), 107 | (Text { len, bytes }, Data { value: b }) => { 108 | match parse_integer(&bytes[..*len]) { 109 | Ok(a) => a.partial_cmp(b), 110 | _ => None, 111 | } 112 | } 113 | (Data { value: a }, Text { len, bytes }) => { 114 | match parse_integer(&bytes[..*len]) { 115 | Ok(b) => a.partial_cmp(&b), 116 | _ => None, 117 | } 118 | } 119 | ( 120 | Text { len: a_len, bytes: a_bytes }, 121 | Text { len: b_len, bytes: b_bytes }, 122 | ) => { 123 | let a = parse_integer(&a_bytes[..*a_len]).ok()?; 124 | let b = parse_integer(&b_bytes[..*b_len]).ok()?; 125 | a.partial_cmp(&b) 126 | } 127 | } 128 | } 129 | } 130 | 131 | impl Integral { 132 | pub fn integer(&self) -> Option { 133 | match self { 134 | Integral::Data { value } => Some(*value), 135 | Integral::Text { len, bytes } => parse_integer(&bytes[0..*len]).ok(), 136 | } 137 | } 138 | 139 | pub fn integer_result(&self) -> Result { 140 | match self { 141 | Integral::Data { value } => Ok(*value), 142 | Integral::Text { len, bytes } => parse_integer(&bytes[0..*len]), 143 | } 144 | } 145 | 146 | pub fn float(&self) -> Option { 147 | let val = self.integer()?; 148 | if (-9007199254740992..=9007199254740992).contains(&val) { 149 | Some(val as f64) 150 | } else { 151 | // TODO: strict accuracy or tolerant behaviour 152 | None 153 | } 154 | } 155 | 156 | pub fn compute(&mut self) -> Result<()> { 157 | if let Integral::Text { len, bytes } = self { 158 | let value = parse_integer(&bytes[0..*len])?; 159 | *self = Integral::Data { value }; 160 | } 161 | 162 | Ok(()) 163 | } 164 | } 165 | 166 | #[derive(Clone, Debug)] 167 | pub enum Floating { 168 | Text { len: usize, bytes: [u8; 128] }, 169 | Data { value: f64 }, 170 | } 171 | 172 | macro_rules! convert_to_float { 173 | ($($from:ty),*) => ( 174 | $( 175 | impl From<$from> for Floating { 176 | fn from(val: $from) -> Floating { 177 | Floating::Data { value: val.into() } 178 | } 179 | } 180 | )* 181 | ); 182 | } 183 | 184 | convert_to_float! {f32, f64} 185 | 186 | impl<'a> TryFrom<&'a str> for Floating { 187 | type Error = Error; 188 | 189 | fn try_from(val: &str) -> Result { 190 | let src = val.as_bytes(); 191 | let val = match src.len() { 192 | n if n < 128 => { 193 | let mut bytes = [0_u8; 128]; 194 | bytes[..src.len()].copy_from_slice(src); 195 | Floating::Text { len: val.len(), bytes } 196 | } 197 | _ => { 198 | let value = parse_float(src)?; 199 | Floating::Data { value } 200 | } 201 | }; 202 | 203 | Ok(val) 204 | } 205 | } 206 | 207 | impl Eq for Floating {} 208 | 209 | impl PartialEq for Floating { 210 | fn eq(&self, other: &Floating) -> bool { 211 | use Floating::{Data, Text}; 212 | 213 | match (self, other) { 214 | (Data { value: a }, Data { value: b }) => a.eq(b), 215 | (Text { len, bytes }, Data { value: b }) => { 216 | parse_float(&bytes[..*len]).map(|a| a.eq(b)).unwrap() 217 | } 218 | (Data { value: a }, Text { len, bytes }) => { 219 | parse_float(&bytes[..*len]).map(|b| a.eq(&b)).unwrap() 220 | } 221 | ( 222 | Text { len: a_len, bytes: a_bytes }, 223 | Text { len: b_len, bytes: b_bytes }, 224 | ) => { 225 | let a = parse_float(&a_bytes[..*a_len]).unwrap(); 226 | let b = parse_float(&b_bytes[..*b_len]).unwrap(); 227 | a.eq(&b) 228 | } 229 | } 230 | } 231 | } 232 | 233 | impl PartialOrd for Floating { 234 | fn partial_cmp(&self, other: &Floating) -> Option { 235 | use Floating::{Data, Text}; 236 | 237 | match (self, other) { 238 | (Data { value: a }, Data { value: b }) => Some(a.total_cmp(b)), 239 | (Text { len, bytes }, Data { value: b }) => { 240 | parse_float(&bytes[..*len]).map(|a| a.total_cmp(b)).ok() 241 | } 242 | (Data { value: a }, Text { len, bytes }) => { 243 | parse_float(&bytes[..*len]).map(|b| a.total_cmp(&b)).ok() 244 | } 245 | ( 246 | Text { len: a_len, bytes: a_bytes }, 247 | Text { len: b_len, bytes: b_bytes }, 248 | ) => { 249 | let a = parse_float(&a_bytes[..*a_len]).unwrap(); 250 | let b = parse_float(&b_bytes[..*b_len]).unwrap(); 251 | Some(a.total_cmp(&b)) 252 | } 253 | } 254 | } 255 | } 256 | 257 | impl Floating { 258 | pub fn float(&self) -> Option { 259 | match self { 260 | Floating::Data { value } => Some(*value), 261 | Floating::Text { len, bytes } => parse_float(&bytes[0..*len]).ok(), 262 | } 263 | } 264 | 265 | pub fn float_result(&self) -> Result { 266 | match self { 267 | Floating::Data { value } => Ok(*value), 268 | Floating::Text { len, bytes } => parse_float(&bytes[0..*len]), 269 | } 270 | } 271 | 272 | pub fn compute(&mut self) -> Result<()> { 273 | if let Floating::Text { len, bytes } = self { 274 | let value = parse_float(&bytes[..*len])?; 275 | *self = Floating::Data { value }; 276 | } 277 | 278 | Ok(()) 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /src/ops_test.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2019 R Pratap Chakravarthy. All rights reserved. 2 | 3 | use crate::{json::Json, property::Property}; 4 | 5 | #[test] 6 | fn test_ops_add() { 7 | // Null as lhs 8 | assert_eq!(Json::Null, Json::Null + Json::Null); 9 | let refval: Json = true.into(); 10 | assert_eq!(refval, Json::Null + true.into()); 11 | let refval: Json = false.into(); 12 | assert_eq!(refval, Json::Null + false.into()); 13 | let refval: Json = 10.into(); 14 | assert_eq!(refval, Json::Null + 10.into()); 15 | let refval: Json = 10.2.into(); 16 | assert_eq!(refval, Json::Null + 10.2.into()); 17 | let refval: Json = "hello".into(); 18 | assert_eq!(refval, Json::Null + "hello".into()); 19 | let rhs: Json = vec![Json::new(1), 2.into()].into(); 20 | let refval: Json = vec![Json::new(1), 2.into()].into(); 21 | assert_eq!(refval, Json::Null + rhs); 22 | let rhs: Json = vec![Property::new("a", 10.into())].into(); 23 | let refval: Json = vec![Property::new("a", 10.into())].into(); 24 | assert_eq!(refval, Json::Null + rhs); 25 | 26 | // Null as rhs 27 | assert_eq!(Json::Null, Json::Null + Json::Null); 28 | let refval: Json = true.into(); 29 | assert_eq!(refval, Json::new(true) + Json::Null); 30 | let refval: Json = false.into(); 31 | assert_eq!(refval, Json::new(false) + Json::Null); 32 | let refval: Json = 10.into(); 33 | assert_eq!(refval, Json::new(10) + Json::Null); 34 | let refval: Json = 10.2.into(); 35 | assert_eq!(refval, Json::new(10.2) + Json::Null); 36 | let refval: Json = "hello".into(); 37 | assert_eq!(refval, Json::new("hello") + Json::Null); 38 | let lhs: Json = vec![Json::new(1), 2.into()].into(); 39 | let refval: Json = vec![Json::new(1), 2.into()].into(); 40 | assert_eq!(refval, lhs + Json::Null); 41 | let lhs: Json = vec![Property::new("a", 10.into())].into(); 42 | let refval: Json = vec![Property::new("a", 10.into())].into(); 43 | assert_eq!(refval, lhs + Json::Null); 44 | 45 | // Integers and floats 46 | assert_eq!(Json::new(20), Json::new(10) + 10.into()); 47 | assert_eq!(Json::new(20.2), Json::new(10.1) + 10.1.into()); 48 | assert_eq!(Json::new(20.2), Json::new(10) + 10.2.into()); 49 | assert_eq!(Json::new(20.2), Json::new(10.2) + 10.into()); 50 | 51 | // String addition 52 | assert_eq!(Json::new("helloworld"), Json::new("hello") + "world".into()); 53 | 54 | // Array addition 55 | let lhs: Json = vec![Json::new(1), 2.into()].into(); 56 | let rhs: Json = vec![Json::new(2), 1.into()].into(); 57 | let refval: Json = vec![Json::new(1), 2.into(), 2.into(), 1.into()].into(); 58 | assert_eq!(refval, lhs + rhs); 59 | 60 | // Object addition 61 | let lhs: Json = 62 | vec![Property::new("a", 10.into()), Property::new("b", 11.into())].into(); 63 | let rhs: Json = vec![Property::new("b", 20.into())].into(); 64 | let refval: Json = 65 | vec![Property::new("a", 10.into()), Property::new("b", 20.into())].into(); 66 | assert_eq!(refval, lhs + rhs); 67 | } 68 | 69 | #[test] 70 | #[allow(clippy::eq_op)] 71 | fn test_ops_sub() { 72 | // Null as lhs 73 | assert_eq!(Json::Null, Json::Null - Json::Null); 74 | let refval: Json = true.into(); 75 | assert_eq!(refval, Json::Null - true.into()); 76 | let refval: Json = false.into(); 77 | assert_eq!(refval, Json::Null - false.into()); 78 | let refval: Json = 10.into(); 79 | assert_eq!(refval, Json::Null - 10.into()); 80 | let refval: Json = 10.2.into(); 81 | assert_eq!(refval, Json::Null - 10.2.into()); 82 | let refval: Json = "hello".into(); 83 | assert_eq!(refval, Json::Null - "hello".into()); 84 | let rhs: Json = vec![Json::new(1), 2.into()].into(); 85 | let refval: Json = vec![Json::new(1), 2.into()].into(); 86 | assert_eq!(refval, Json::Null - rhs); 87 | let rhs: Json = vec![Property::new("a", 10.into())].into(); 88 | let refval: Json = vec![Property::new("a", 10.into())].into(); 89 | assert_eq!(refval, Json::Null - rhs); 90 | 91 | // Null as rhs 92 | assert_eq!(Json::Null, Json::Null - Json::Null); 93 | let refval: Json = true.into(); 94 | assert_eq!(refval, Json::new(true) - Json::Null); 95 | let refval: Json = false.into(); 96 | assert_eq!(refval, Json::new(false) - Json::Null); 97 | let refval: Json = 10.into(); 98 | assert_eq!(refval, Json::new(10) - Json::Null); 99 | let refval: Json = 10.2.into(); 100 | assert_eq!(refval, Json::new(10.2) - Json::Null); 101 | let refval: Json = "hello".into(); 102 | assert_eq!(refval, Json::new("hello") - Json::Null); 103 | let lhs: Json = vec![Json::new(1), 2.into()].into(); 104 | let refval: Json = vec![Json::new(1), 2.into()].into(); 105 | assert_eq!(refval, lhs - Json::Null); 106 | let lhs: Json = vec![Property::new("a", 10.into())].into(); 107 | let refval: Json = vec![Property::new("a", 10.into())].into(); 108 | assert_eq!(refval, lhs - Json::Null); 109 | 110 | // Integers and floats 111 | assert_eq!(Json::new(10), Json::new(20) - 10.into()); 112 | assert_eq!(Json::new(20.1 - 10.1), Json::new(20.1) - 10.1.into()); 113 | assert_eq!(Json::new(9.8), Json::new(20) - 10.2.into()); 114 | assert_eq!(Json::new(10.2 - (f64::from(10))), Json::new(10.2) - 10.into()); 115 | 116 | // Array substraction 117 | let lhs: Json = vec![Json::new(1), 1.into(), 2.into(), 2.into(), 2.into()].into(); 118 | let rhs: Json = vec![Json::new(2), 2.into(), 1.into()].into(); 119 | let refval: Json = vec![Json::new(1), 2.into()].into(); 120 | assert_eq!(refval, lhs - rhs); 121 | 122 | // Object substraction 123 | let lhs: Json = 124 | vec![Property::new("a", 10.into()), Property::new("b", 20.into())].into(); 125 | let rhs: Json = vec![Property::new("b", 20.into())].into(); 126 | let refval: Json = vec![Property::new("a", 10.into())].into(); 127 | assert_eq!(refval, lhs - rhs); 128 | } 129 | 130 | #[test] 131 | fn test_ops_mul() { 132 | // Null as lhs 133 | assert_eq!(Json::Null, Json::Null * Json::Null); 134 | assert_eq!(Json::Null, Json::Null * true.into()); 135 | assert_eq!(Json::Null, Json::Null * false.into()); 136 | assert_eq!(Json::Null, Json::Null * 10.into()); 137 | assert_eq!(Json::Null, Json::Null * 10.2.into()); 138 | assert_eq!(Json::Null, Json::Null * "hello".into()); 139 | let rhs: Json = vec![Json::new(1), 2.into()].into(); 140 | assert_eq!(Json::Null, Json::Null * rhs); 141 | let rhs: Json = vec![Property::new("a", 10.into())].into(); 142 | assert_eq!(Json::Null, Json::Null * rhs); 143 | 144 | // Null as rhs 145 | assert_eq!(Json::Null, Json::Null * Json::Null); 146 | assert_eq!(Json::Null, Json::new(true) * Json::Null); 147 | assert_eq!(Json::Null, Json::new(false) * Json::Null); 148 | assert_eq!(Json::Null, Json::new(10) * Json::Null); 149 | assert_eq!(Json::Null, Json::new(10.2) * Json::Null); 150 | assert_eq!(Json::Null, Json::new("hello") * Json::Null); 151 | let lhs: Json = vec![Json::new(1), 2.into()].into(); 152 | assert_eq!(Json::Null, lhs * Json::Null); 153 | let lhs: Json = vec![Property::new("a", 10.into())].into(); 154 | assert_eq!(Json::Null, lhs * Json::Null); 155 | 156 | // Integers and floats 157 | assert_eq!(Json::new(200), Json::new(20) * 10.into()); 158 | assert_eq!(Json::new(20.1 * 10.1), Json::new(20.1) * 10.1.into()); 159 | assert_eq!(Json::new((f64::from(20)) * 10.2), Json::new(20) * 10.2.into()); 160 | assert_eq!(Json::new(10.2 * (f64::from(10))), Json::new(10.2) * 10.into()); 161 | 162 | // String multiplication 163 | assert_eq!(Json::new("okokok"), Json::new("ok") * 3.into()); 164 | assert_eq!(Json::Null, Json::new("ok") * 0.into()); 165 | assert_eq!(Json::new("okokok"), Json::new(3) * "ok".into()); 166 | assert_eq!(Json::Null, Json::new(0) * "ok".into()); 167 | } 168 | 169 | #[test] 170 | #[allow(clippy::eq_op)] 171 | fn test_ops_div() { 172 | // Null as lhs 173 | assert_eq!(Json::Null, Json::Null / Json::Null); 174 | assert_eq!(Json::Null, Json::Null / true.into()); 175 | assert_eq!(Json::Null, Json::Null / false.into()); 176 | assert_eq!(Json::Null, Json::Null / 10.into()); 177 | assert_eq!(Json::Null, Json::Null / 10.2.into()); 178 | assert_eq!(Json::Null, Json::Null / "hello".into()); 179 | let rhs: Json = vec![Json::new(1), 2.into()].into(); 180 | assert_eq!(Json::Null, Json::Null / rhs); 181 | let rhs: Json = vec![Property::new("a", 10.into())].into(); 182 | assert_eq!(Json::Null, Json::Null / rhs); 183 | 184 | // Null as rhs 185 | assert_eq!(Json::Null, Json::Null / Json::Null); 186 | assert_eq!(Json::Null, Json::new(true) / Json::Null); 187 | assert_eq!(Json::Null, Json::new(false) / Json::Null); 188 | assert_eq!(Json::Null, Json::new(10) / Json::Null); 189 | assert_eq!(Json::Null, Json::new(10.2) / Json::Null); 190 | assert_eq!(Json::Null, Json::new("hello") / Json::Null); 191 | let lhs: Json = vec![Json::new(1), 2.into()].into(); 192 | assert_eq!(Json::Null, lhs / Json::Null); 193 | let lhs: Json = vec![Property::new("a", 10.into())].into(); 194 | assert_eq!(Json::Null, lhs / Json::Null); 195 | 196 | // Integers and floats 197 | assert_eq!(Json::new(2), Json::new(20) / 10.into()); 198 | assert_eq!(Json::new(20.1 / 10.1), Json::new(20.1) / 10.1.into()); 199 | assert_eq!(Json::new((f64::from(20)) / 10.2), Json::new(20) / 10.2.into()); 200 | assert_eq!(Json::new(10.2 / (f64::from(10))), Json::new(10.2) / 10.into()); 201 | } 202 | 203 | #[test] 204 | fn test_ops_rem() { 205 | // Null as lhs 206 | assert_eq!(Json::Null, Json::Null % Json::Null); 207 | assert_eq!(Json::Null, Json::Null % true.into()); 208 | assert_eq!(Json::Null, Json::Null % false.into()); 209 | assert_eq!(Json::Null, Json::Null % 10.into()); 210 | assert_eq!(Json::Null, Json::Null % 10.2.into()); 211 | assert_eq!(Json::Null, Json::Null % "hello".into()); 212 | let rhs: Json = vec![Json::new(1), 2.into()].into(); 213 | assert_eq!(Json::Null, Json::Null % rhs); 214 | let rhs: Json = vec![Property::new("a", 10.into())].into(); 215 | assert_eq!(Json::Null, Json::Null % rhs); 216 | 217 | // Null as rhs 218 | assert_eq!(Json::Null, Json::Null % Json::Null); 219 | assert_eq!(Json::Null, Json::new(true) % Json::Null); 220 | assert_eq!(Json::Null, Json::new(false) % Json::Null); 221 | assert_eq!(Json::Null, Json::new(10) % Json::Null); 222 | assert_eq!(Json::Null, Json::new(10.2) % Json::Null); 223 | assert_eq!(Json::Null, Json::new("hello") % Json::Null); 224 | let lhs: Json = vec![Json::new(1), 2.into()].into(); 225 | assert_eq!(Json::Null, lhs % Json::Null); 226 | let lhs: Json = vec![Property::new("a", 10.into())].into(); 227 | assert_eq!(Json::Null, lhs % Json::Null); 228 | 229 | // Integers and floats 230 | assert_eq!(Json::new(2), Json::new(202) % 10.into()); 231 | assert_eq!(Json::new(20.1 % 10.1), Json::new(20.1) % 10.1.into()); 232 | assert_eq!(Json::new((f64::from(20)) % 10.2), Json::new(20) % 10.2.into()); 233 | assert_eq!(Json::new(10.2 % (f64::from(10))), Json::new(10.2) % 10.into()); 234 | } 235 | 236 | #[test] 237 | fn test_ops_neg() { 238 | // Null as lhs 239 | assert_eq!(Json::Null, -Json::Null); 240 | 241 | // Integers and floats 242 | assert_eq!(Json::new(-202), -Json::new(202)); 243 | assert_eq!(Json::new(-20.1), -Json::new(20.1)); 244 | } 245 | 246 | #[test] 247 | fn test_ops_shl() { 248 | assert_eq!(Json::new(2), Json::new(1) << 1.into()); 249 | let v = -170_141_183_460_469_231_731_687_303_715_884_105_728_i128; 250 | assert_eq!(Json::new(v), Json::new(1) << 127.into()); 251 | } 252 | 253 | #[test] 254 | fn test_ops_shr() { 255 | assert_eq!(Json::new(0), Json::new(1) >> 1.into()); 256 | assert_eq!(Json::new(-1), (Json::new(1) << 127.into()) >> 127.into()); 257 | } 258 | 259 | #[test] 260 | fn test_ops_bitand() { 261 | assert_eq!(Json::new(0xABCD), Json::new(0xABCD) & 0xFFFF.into()); 262 | assert_eq!(Json::new(0), Json::new(0xABCD) & 0.into()); 263 | } 264 | 265 | #[test] 266 | fn test_ops_bitor() { 267 | assert_eq!(Json::new(0xFFFF), Json::new(0xABCD) | 0xFFFF.into()); 268 | assert_eq!(Json::new(0xABCD), Json::new(0xABCD) | 0.into()); 269 | } 270 | 271 | #[test] 272 | fn test_ops_bitxor() { 273 | assert_eq!(Json::new(0x5432), Json::new(0xABCD) ^ 0xFFFF.into()); 274 | assert_eq!(Json::new(0xABCD), Json::new(0xABCD) ^ 0.into()); 275 | } 276 | 277 | #[test] 278 | fn test_ops_and() { 279 | assert_eq!(Json::new(true), Json::new(true) & true.into()); 280 | assert_eq!(Json::new(false), Json::new(false) & true.into()); 281 | assert_eq!(Json::new(false), Json::new(true) & false.into()); 282 | assert_eq!(Json::new(false), Json::new(false) & false.into()); 283 | } 284 | 285 | #[test] 286 | fn test_ops_or() { 287 | assert_eq!(Json::new(true), Json::new(true) | true.into()); 288 | assert_eq!(Json::new(true), Json::new(false) | true.into()); 289 | assert_eq!(Json::new(true), Json::new(true) | false.into()); 290 | assert_eq!(Json::new(false), Json::new(false) | false.into()); 291 | } 292 | 293 | #[test] 294 | fn test_ops_xor() { 295 | assert_eq!(Json::new(false), Json::new(true) ^ true.into()); 296 | assert_eq!(Json::new(true), Json::new(false) ^ true.into()); 297 | assert_eq!(Json::new(true), Json::new(true) ^ false.into()); 298 | assert_eq!(Json::new(false), Json::new(false) ^ false.into()); 299 | } 300 | 301 | #[test] 302 | fn test_index_arr() { 303 | let item: Json = vec![Json::new(1), 2.into()].into(); 304 | let value: Json = 305 | vec![Json::new(1), 2.into(), true.into(), Json::Null, 3.4.into(), item.clone()] 306 | .into(); 307 | 308 | assert_eq!(value[0], Json::new(1)); 309 | assert_eq!(value[1], Json::new(2)); 310 | assert_eq!(value[5], item); 311 | assert_eq!(value[-1], item); 312 | assert_eq!(value[-2], Json::new(3.4)); 313 | assert_eq!(value[-6], Json::new(1)); 314 | 315 | assert!(value[-7].is_error()); 316 | assert!(value[6].is_error()); 317 | } 318 | 319 | #[test] 320 | fn test_index_obj() { 321 | let value: Json = vec![ 322 | Property::new("a", 10.into()), 323 | Property::new("b", 10.into()), 324 | Property::new("c", 10.into()), 325 | ] 326 | .into(); 327 | assert_eq!(value["a"], Json::new(10)); 328 | assert!(value["z"].is_error()); 329 | } 330 | 331 | #[test] 332 | fn test_range_arr() { 333 | let arr: Vec = 334 | vec![Json::new(1), 2.into(), true.into(), Json::Null, 3.4.into(), 6.into()]; 335 | let value: Json = arr.clone().into(); 336 | 337 | assert_eq!(value.range(1..), Json::new(arr[1..].to_vec())); 338 | assert_eq!(value.range(1..3), Json::new(arr[1..3].to_vec())); 339 | assert_eq!(value.range(..3), Json::new(arr[..3].to_vec())); 340 | assert_eq!(value.range(..), Json::new(arr[..].to_vec())); 341 | assert_eq!(value.range(1..=3), Json::new(arr[1..=3].to_vec())); 342 | assert_eq!(value.range(..=3), Json::new(arr[..=3].to_vec())); 343 | 344 | assert_eq!(value.range(-5..), Json::new(arr[1..].to_vec())); 345 | assert_eq!(value.range(-5..-3), Json::new(arr[1..3].to_vec())); 346 | assert_eq!(value.range(..-3), Json::new(arr[..3].to_vec())); 347 | assert_eq!(value.range(..), Json::new(arr[..].to_vec())); 348 | assert_eq!(value.range(-5..=-3), Json::new(arr[1..=3].to_vec())); 349 | assert_eq!(value.range(..=-3), Json::new(arr[..=3].to_vec())); 350 | } 351 | -------------------------------------------------------------------------------- /src/parse.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2019 R Pratap Chakravarthy. All rights reserved. 2 | 3 | use std::str::CharIndices; 4 | use std::{char, f64}; 5 | 6 | use lazy_static::lazy_static; 7 | 8 | use crate::property::{self, Property}; 9 | use crate::{json::Json, lex::Lex, num, Error, Result}; 10 | 11 | pub fn parse_value(text: &str, lex: &mut Lex) -> Result { 12 | parse_whitespace(text, lex); 13 | 14 | not_eof(text, lex)?; 15 | 16 | //println!("text -- {:?}", &text[lex.off..].as_bytes()); 17 | let bs = text[lex.off..].as_bytes(); 18 | match bs[0] { 19 | b'n' => parse_null(text, lex), 20 | b't' => parse_true(text, lex), 21 | b'f' => parse_false(text, lex), 22 | b'-' if bs.len() > 1 && bs[1] == b'I' => parse_json5_float(text, lex, 1), 23 | b'0'..=b'9' | b'+' | b'-' | b'.' | b'e' | b'E' => parse_num(text, lex), 24 | b'"' => parse_string(text, lex), 25 | b'[' => parse_array(text, lex), 26 | b'{' => parse_object(text, lex), 27 | b'I' => parse_json5_float(text, lex, 2), 28 | b'N' => parse_json5_float(text, lex, 3), 29 | ch => { 30 | err_at!(ParseFail, msg: "{}", lex.format(&format!("invalid token {}", ch))) 31 | } 32 | } 33 | //println!("valu -- {:?}", v); 34 | } 35 | 36 | #[inline] 37 | fn parse_null(text: &str, lex: &mut Lex) -> Result { 38 | let text = &text[lex.off..]; 39 | if text.len() >= 4 && &text[..4] == "null" { 40 | lex.incr_col(4); 41 | Ok(Json::Null) 42 | } else { 43 | err_at!(ParseFail, msg: "{}", lex.format("expected null")) 44 | } 45 | } 46 | 47 | #[inline] 48 | fn parse_true(text: &str, lex: &mut Lex) -> Result { 49 | let text = &text[lex.off..]; 50 | if text.len() >= 4 && &text[..4] == "true" { 51 | lex.incr_col(4); 52 | Ok(Json::Bool(true)) 53 | } else { 54 | err_at!(ParseFail, msg: "{}", lex.format("expected true")) 55 | } 56 | } 57 | 58 | #[inline] 59 | fn parse_false(text: &str, lex: &mut Lex) -> Result { 60 | let text = &text[lex.off..]; 61 | if text.len() >= 5 && &text[..5] == "false" { 62 | lex.incr_col(5); 63 | Ok(Json::Bool(false)) 64 | } else { 65 | err_at!(ParseFail, msg: "{}", lex.format("expected false")) 66 | } 67 | } 68 | 69 | fn parse_num(text: &str, lex: &mut Lex) -> Result { 70 | let text = &text[lex.off..]; 71 | 72 | let mut dofn = |t: &str, i: usize, is_float: bool, is_hex: bool| -> Result { 73 | lex.incr_col(i); 74 | //println!("parse_num -- {}", t); 75 | if is_float && !is_hex { 76 | Ok(Json::Float(num::Floating::try_from(t)?)) 77 | } else { 78 | Ok(Json::Integer(num::Integral::try_from(t)?)) 79 | } 80 | }; 81 | 82 | let (mut is_float, mut is_hex) = (false, false); 83 | for (i, ch) in text.char_indices() { 84 | let mut ok = (ch as u32) > (ISNUMBER.len() as u32); 85 | ok = ok || ISNUMBER[ch as usize] == 0; 86 | if ok { 87 | return dofn(&text[..i], i, is_float, is_hex); 88 | } else if !is_float && ISNUMBER[ch as usize] == 2 { 89 | is_float = true 90 | } else if ISNUMBER[ch as usize] == 3 { 91 | is_hex = true 92 | } 93 | } 94 | dofn(text, text.len(), is_float, is_hex) 95 | } 96 | 97 | fn parse_json5_float(txt: &str, lex: &mut Lex, w: usize) -> Result { 98 | lazy_static! { 99 | static ref JSON5_FLOAT_LOOKUP: Vec<(String, usize, Json)> = vec![ 100 | ("".to_string(), 0_usize, Json::Null), 101 | ("-Infinity".to_string(), 9, Json::new(f64::NEG_INFINITY)), 102 | ("Infinity".to_string(), 8, Json::new(f64::INFINITY)), 103 | ("NaN".to_string(), 3, Json::new(f64::NAN)), 104 | ]; 105 | } 106 | let (token, l, res) = &JSON5_FLOAT_LOOKUP[w]; 107 | let txt = &txt[lex.off..]; 108 | if txt.len() >= *l && token == &txt[..*l] { 109 | lex.col += l; 110 | lex.off += l; 111 | Ok(res.clone()) 112 | } else { 113 | err_at!(ParseFail, msg: "{}", lex.format("expected json5 float")) 114 | } 115 | } 116 | 117 | fn parse_string(text: &str, lex: &mut Lex) -> Result { 118 | use self::Json::String as S; 119 | 120 | let mut escape = false; 121 | let mut res = String::new(); 122 | let mut chars = text[lex.off..].char_indices(); 123 | 124 | let (i, ch) = chars.next().unwrap(); // skip the opening quote 125 | if ch != '"' { 126 | err_at!(ParseFail, msg: "{}", lex.format("invalid string"))?; 127 | } 128 | 129 | while let Some((i, ch)) = chars.next() { 130 | if !escape { 131 | if ch == '\\' { 132 | escape = true; 133 | continue; 134 | } 135 | match ch { 136 | '"' => { 137 | lex.incr_col(i + 1); 138 | return Ok(S(res)); 139 | } 140 | _ => res.push(ch), 141 | } 142 | continue; 143 | } 144 | 145 | // previous char was escape 146 | match ch { 147 | '"' => res.push('"'), 148 | '\\' => res.push('\\'), 149 | '/' => res.push('/'), 150 | 'b' => res.push('\x08'), 151 | 'f' => res.push('\x0c'), 152 | 'n' => res.push('\n'), 153 | 'r' => res.push('\r'), 154 | 't' => res.push('\t'), 155 | 'u' => match decode_json_hex_code(&mut chars, lex)? { 156 | code1 @ 0xDC00..=0xDFFF => { 157 | lex.incr_col(i); 158 | err_at!( 159 | ParseFail, 160 | msg: "{}", lex.format(&format!("invalid codepoint {:x}", code1)) 161 | )?; 162 | } 163 | // Non-BMP characters are encoded as a sequence of 164 | // two hex escapes, representing UTF-16 surrogates. 165 | code1 @ 0xD800..=0xDBFF => { 166 | let code2 = decode_json_hex_code2(&mut chars, lex)?; 167 | if !(0xDC00..=0xDFFF).contains(&code2) { 168 | lex.incr_col(i); 169 | err_at!( 170 | ParseFail, 171 | msg: "{}", 172 | lex.format( 173 | &format!("invalid codepoint surrogate {:x}", code2) 174 | ) 175 | )?; 176 | } 177 | let code = ((code1 - 0xD800) << 10) | ((code2 - 0xDC00) + 0x1_0000); 178 | res.push(char::from_u32(code).unwrap()); 179 | } 180 | 181 | n => match char::from_u32(n) { 182 | Some(ch) => res.push(ch), 183 | None => { 184 | lex.incr_col(i); 185 | err_at!( 186 | ParseFail, 187 | msg: "{}", 188 | lex.format(&format!("invalid unicode escape u{:x}", n)) 189 | )?; 190 | } 191 | }, 192 | }, 193 | _ => { 194 | lex.incr_col(i); 195 | err_at!(ParseFail, msg: "{}", lex.format("invalid string escape type"))? 196 | } 197 | } 198 | escape = false; 199 | } 200 | lex.incr_col(i); 201 | err_at!(ParseFail, msg: "{}", lex.format("incomplete string")) 202 | } 203 | 204 | fn decode_json_hex_code2(chars: &mut CharIndices, lex: &mut Lex) -> Result { 205 | if let Some((_, ch1)) = chars.next() { 206 | if let Some((_, ch2)) = chars.next() { 207 | if ch1 == '\\' && ch2 == 'u' { 208 | return decode_json_hex_code(chars, lex); 209 | } 210 | } 211 | } 212 | err_at!(ParseFail, msg: "{}", lex.format("invalid string escape type")) 213 | } 214 | 215 | fn decode_json_hex_code(chars: &mut CharIndices, lex: &mut Lex) -> Result { 216 | let mut n = 0; 217 | let mut code = 0_u32; 218 | for (_, ch) in chars { 219 | if (ch as u32) > 128 || HEXNUM[ch as usize] == 20 { 220 | let msg = format!("invalid string escape code {:?}", ch); 221 | err_at!(ParseFail, msg: "{}", lex.format(&msg))?; 222 | } 223 | code = code * 16 + u32::from(HEXNUM[ch as usize]); 224 | n += 1; 225 | if n == 4 { 226 | break; 227 | } 228 | } 229 | if n != 4 { 230 | let msg = format!("incomplete string escape code {:x}", code); 231 | err_at!(ParseFail, msg: "{}", lex.format(&msg))?; 232 | } 233 | Ok(code) 234 | } 235 | 236 | fn parse_array(text: &str, lex: &mut Lex) -> Result { 237 | lex.incr_col(1); // skip '[' 238 | 239 | let mut array: Vec = Vec::new(); 240 | parse_whitespace(text, lex); 241 | if text[lex.off..].as_bytes()[0] == b',' { 242 | err_at!(ParseFail, msg: "{}", lex.format("expected ','"))?; 243 | } 244 | loop { 245 | if text[lex.off..].as_bytes()[0] == b']' { 246 | // end of array. 247 | lex.incr_col(1); 248 | break Ok(Json::Array(array)); 249 | } 250 | 251 | array.push(parse_value(text, lex)?); 252 | 253 | parse_whitespace(text, lex); 254 | if text[lex.off..].as_bytes()[0] == b',' { 255 | // skip comma 256 | lex.incr_col(1); 257 | parse_whitespace(text, lex); 258 | } 259 | } 260 | } 261 | 262 | fn parse_object(text: &str, lex: &mut Lex) -> Result { 263 | lex.incr_col(1); // skip '{' 264 | 265 | parse_whitespace(text, lex); 266 | 267 | let mut m: Vec = Vec::new(); 268 | 269 | if text[lex.off..].as_bytes()[0] == b'}' { 270 | lex.incr_col(1); 271 | return Ok(Json::Object(m)); 272 | } 273 | 274 | loop { 275 | // key 276 | parse_whitespace(text, lex); 277 | let key: String = match text[lex.off..].chars().next() { 278 | Some('}') => { 279 | lex.incr_col(1); 280 | break Ok(Json::Object(m)); 281 | } 282 | Some('"') => parse_string(text, lex)?.as_str().unwrap().to_string(), 283 | Some(ch) if ch.is_alphabetic() => parse_identifier(text, lex), 284 | _ => err_at!(ParseFail, msg:"{}", lex.format("invalid property key"))?, 285 | }; 286 | // colon 287 | parse_whitespace(text, lex); 288 | check_next_byte(text, lex, b':')?; 289 | 290 | // value 291 | parse_whitespace(text, lex); 292 | let value = parse_value(text, lex)?; 293 | 294 | property::upsert_object_key(&mut m, Property::new(key, value)); 295 | //println!("parse {} {} {:?}", key, i, m); 296 | 297 | // is exit 298 | parse_whitespace(text, lex); 299 | let mut chars = text[lex.off..].chars(); 300 | match chars.next() { 301 | None => err_at!(ParseFail, msg: "{}", lex.format("unexpected eof"))?, 302 | Some(',') => { 303 | lex.incr_col(1); 304 | } 305 | _ => (), 306 | } 307 | } 308 | } 309 | 310 | #[inline] 311 | fn parse_identifier(text: &str, lex: &mut Lex) -> String { 312 | let (off, mut n) = (lex.off, 0); 313 | for (i, ch) in text[lex.off..].char_indices() { 314 | if ch.is_alphanumeric() { 315 | continue; 316 | } 317 | n = i; 318 | break; 319 | } 320 | lex.off += n; 321 | text[off..off + n].to_string() 322 | } 323 | 324 | #[inline] 325 | fn parse_whitespace(text: &str, lex: &mut Lex) { 326 | for (i, ch) in text[lex.off..].char_indices() { 327 | //println!("{} {}", u32::from(ch), ch.is_whitespace()); 328 | if ch.is_whitespace() { 329 | if (u32::from(ch)) < 256 && ch == '\n' { 330 | lex.row += 1; 331 | lex.col = 0; 332 | } else { 333 | lex.col += 1; 334 | } 335 | continue; 336 | } 337 | lex.off += i; 338 | break; 339 | } 340 | } 341 | 342 | #[inline] 343 | fn check_next_byte(text: &str, lex: &mut Lex, b: u8) -> Result<()> { 344 | let progbytes = text[lex.off..].as_bytes(); 345 | 346 | if progbytes.is_empty() { 347 | err_at!(ParseFail, msg: "{}", lex.format(&format!("missing token {}", b)))?; 348 | } 349 | 350 | if progbytes[0] != b { 351 | err_at!( 352 | ParseFail, 353 | msg: "{}", lex.format(&format!("invalid byte {}, {}", b, progbytes[0])) 354 | )?; 355 | } 356 | 357 | lex.incr_col(1); 358 | 359 | Ok(()) 360 | } 361 | 362 | #[inline] 363 | fn not_eof(text: &str, lex: &mut Lex) -> Result<()> { 364 | if text[lex.off..].is_empty() { 365 | err_at!(ParseFail, msg: "{}", lex.format("unexpected eof")) 366 | } else { 367 | Ok(()) 368 | } 369 | } 370 | 371 | static HEXNUM: [u8; 256] = [ 372 | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 373 | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 374 | 20, 20, 20, 20, 20, 20, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 20, 10, 11, 375 | 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 376 | 0, 0, 0, 20, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20, 20, 20, 20, 377 | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 378 | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 379 | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 380 | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 381 | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 382 | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 383 | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 384 | ]; 385 | 386 | // These days, with unicode, white-spaces have become more complicated :/. 387 | static _WS_LOOKUP: [u8; 256] = [ 388 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 389 | 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 390 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 391 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 392 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 393 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 394 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 395 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 396 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 397 | 0, 0, 0, 0, 398 | ]; 399 | 400 | static ISNUMBER: [u8; 256] = [ 401 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 402 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 403 | 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 404 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 405 | 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 406 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 407 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 408 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 409 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 410 | 0, 0, 0, 0, 411 | ]; 412 | -------------------------------------------------------------------------------- /src/property.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2019 R Pratap Chakravarthy. All rights reserved. 2 | 3 | use crate::json::Json; 4 | 5 | /// Property type captures a single (key,value) pair in a JSON object. 6 | /// 7 | /// Where, 8 | /// * _key_ is [String] type, defined by JSON specification. 9 | /// * _value_ is JSON value. 10 | /// 11 | /// Implements [PartialEq] and [PartialOrd] for equality and ordering. 12 | /// 13 | /// [string]: std::string::String 14 | /// [PartialEq]: std::cmp::PartialEq 15 | /// [PartialOrd]: std::cmp::PartialOrd 16 | #[derive(Debug, Clone, Eq, PartialEq, PartialOrd)] 17 | pub struct Property(String, Json); 18 | 19 | /// Following inherent methods are self explanatory, typically 20 | /// used to move, or obtain a reference for key or value 21 | /// component of a property. 22 | impl Property { 23 | #[inline] 24 | pub fn new(key: T, value: Json) -> Property 25 | where 26 | T: ToString, 27 | { 28 | Property(key.to_string(), value) 29 | } 30 | 31 | #[inline] 32 | pub fn into_key(self) -> String { 33 | self.0 34 | } 35 | 36 | #[inline] 37 | pub fn as_key(&self) -> &str { 38 | &self.0 39 | } 40 | 41 | #[inline] 42 | pub fn into_value(self) -> Json { 43 | self.1 44 | } 45 | 46 | #[inline] 47 | pub fn as_value(&self) -> &Json { 48 | &self.1 49 | } 50 | 51 | #[inline] 52 | pub fn as_mut_value(&mut self) -> &mut Json { 53 | &mut self.1 54 | } 55 | 56 | #[inline] 57 | pub fn set_key(&mut self, key: String) { 58 | self.0 = key; 59 | } 60 | 61 | #[inline] 62 | pub fn set_value(&mut self, value: Json) { 63 | self.1 = value; 64 | } 65 | } 66 | 67 | pub fn upsert_object_key(obj: &mut Vec, prop: Property) { 68 | match obj.binary_search_by(|p| p.as_key().cmp(prop.as_key())) { 69 | Ok(off) => obj[off] = prop, 70 | Err(off) => obj.insert(off, prop), 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /template/css/PITCHME.css: -------------------------------------------------------------------------------- 1 | /* Intro Template Styles */ 2 | 3 | .byline { 4 | letter-spacing: 0.1em; 5 | } 6 | 7 | /* Split-Screen Template Styles */ 8 | 9 | .split-screen-img { 10 | border-radius: 8% !important; 11 | } 12 | 13 | .split-screen-list li { 14 | margin:0 0 40px 0; 15 | } 16 | 17 | .split-screen-list li::before { 18 | color: #FF5600 !important; 19 | background-color: white !important; 20 | } 21 | 22 | /* List-Content Template Styles */ 23 | 24 | .list-content-concise li { 25 | margin:0 0 10px 0; 26 | } 27 | 28 | .list-content-verbose ul { 29 | list-style-type: square; 30 | } 31 | 32 | .list-content-verbose li { 33 | font-size: 0.8em; 34 | margin:0 0 40px 0; 35 | } 36 | 37 | .list-bullets-black li::before { 38 | background-color: black !important; 39 | } 40 | 41 | .list-bullets-circles { 42 | list-style-type: circle !important; 43 | } 44 | 45 | /** Boxed-Text Template Styles ***/ 46 | 47 | .demo-box-text-padding #boxed-text { 48 | padding-top: 1.95em; 49 | padding-bottom: 1.95em; 50 | } 51 | 52 | .demo-box-step-padding #boxed-text { 53 | padding-top: 1.8em; 54 | padding-bottom: 2.3em; 55 | } 56 | 57 | /** Image Template Styles ***/ 58 | 59 | /** Sidebox Template Styles ***/ 60 | 61 | /** Code-Presenting Template Styles ***/ 62 | 63 | .hljs { 64 | background: none; 65 | } 66 | 67 | .reveal pre { 68 | width: 98%; 69 | margin: 0px; 70 | box-shadow: none; 71 | } 72 | 73 | .reveal pre code { 74 | font-size: 1.2em; 75 | line-height: 1.2; 76 | border-radius: 10px; 77 | max-height: 60vh !important; 78 | overflow: hidden !important; 79 | } 80 | 81 | @media (device-width: 100vw) and (device-height: 100vh) { 82 | .reveal pre code { 83 | max-height: 50vh !important; 84 | } 85 | } 86 | 87 | /** Header-Footer Template Styles ***/ 88 | 89 | .header-footer-list-shrink li { 90 | color: white; 91 | font-size: 0.7em; 92 | margin:0 0 20px 0; 93 | } 94 | 95 | /** Quotation Template Styles ***/ 96 | 97 | /** Announcements Template Styles ***/ 98 | 99 | .announce-big-news { 100 | font-size: 7em !important; 101 | } 102 | 103 | .announce-coming-soon { 104 | font-size: 6.5em !important; 105 | } 106 | 107 | /** About Template Styles **/ 108 | 109 | .about-team-pic img { 110 | width: 60%; 111 | border: none !important; 112 | box-shadow: none !important; 113 | -webkit-clip-path: circle(35% at 50% 50%); 114 | clip-path: circle(35% at 50% 50%); 115 | } 116 | 117 | .about-team-pic-center img { 118 | width: 70%; 119 | } 120 | 121 | /** Wrap-Up Template Styles **/ 122 | 123 | .contact-name { 124 | font-size: 0.6em !important; 125 | font-weight: 800 !important; 126 | text-transform: uppercase !important; 127 | font-family: Arial, Helvetica, sans-serif !important; 128 | } 129 | 130 | .contact-email { 131 | font-size: 0.5em !important; 132 | font-weight: 600 !important; 133 | } 134 | 135 | .contact-links { 136 | color: #00008B !important; 137 | } 138 | 139 | .twitter-handle { 140 | font-size: 0.5em !important; 141 | font-weight: bolder !important; 142 | vertical-align: middle !important; 143 | } 144 | 145 | .git-handle { 146 | font-size: 0.5em !important; 147 | font-weight: bolder !important; 148 | vertical-align: middle !important; 149 | } 150 | 151 | .pad-right-icon { 152 | padding-right: 0.4em; 153 | } 154 | 155 | .pad-left-icon { 156 | padding-left: 0.4em; 157 | } 158 | 159 | .sign-off h3, .sign-off h4 { 160 | color: white; 161 | } 162 | 163 | /** Final Slide Styles */ 164 | 165 | .docslink { 166 | font-size: 0.5em !important; 167 | font-variant: small-caps !important; 168 | letter-spacing: 0.2em; 169 | } 170 | 171 | div.docslink a { 172 | color: gray !important; 173 | } 174 | 175 | div.docslink a:hover { 176 | color: #e58537 !important; 177 | font-weight: bold; 178 | } 179 | 180 | /** General Template Styles **/ 181 | 182 | .template-note { 183 | letter-spacing: 0.1em; 184 | font-size: 0.4em !important; 185 | } 186 | 187 | #gp-logo img { 188 | max-height: 3.5em; 189 | max-width: none; 190 | } 191 | 192 | #title-footer a { 193 | color: black; 194 | } 195 | 196 | #title-footer p.footer-hard { 197 | color: black; 198 | } 199 | 200 | /** Reveal.js Core Override Styles **/ 201 | 202 | .reveal ol li { 203 | margin-left: 0; 204 | padding-right: 0; 205 | list-style-type: none; 206 | } 207 | 208 | .reveal ol li { 209 | position: relative; 210 | counter-increment: step-counter; 211 | } 212 | 213 | .reveal ol li::before { 214 | color: white; 215 | font-size: 80%; 216 | font-weight: bold; 217 | padding: 3px 10px; 218 | margin-right: 15px; 219 | border-radius: 3px; 220 | background-color: #F26225; 221 | content: counter(step-counter); 222 | } 223 | 224 | .reveal section img { 225 | border: none; 226 | box-shadow: none; 227 | background: none; 228 | } 229 | 230 | .reveal .slide-number { 231 | color: black !important; 232 | } 233 | 234 | .reveal blockquote { 235 | width: 100%; 236 | padding: 17px; 237 | border-radius: 17px; 238 | background: rgba(255,255,255,0.2); 239 | } 240 | 241 | .reveal p span.slide-title { 242 | display: none; 243 | } 244 | 245 | .reveal .slides section .code-presenting-annotation { 246 | color: white; 247 | background: black; 248 | padding: 0px 15px; 249 | border-radius: 15px; 250 | opacity: 0.75 !important; 251 | font-size: 50% !important; 252 | } 253 | 254 | /* pitch for json */ 255 | 256 | .reveal .slides { 257 | text-align: left !important; 258 | } 259 | .reveal h1 { 260 | font-size: 1.7em !important; 261 | text-align: center !important; 262 | color: crimson !important; 263 | } 264 | .reveal p { 265 | text-align: left !important; 266 | font-size: 0.75em !important; 267 | } 268 | section div { 269 | font-size: 0.75em !important; 270 | } 271 | .reveal ul { 272 | text-align: left !important; 273 | font-size: 0.75em !important; 274 | } 275 | .text-crimson { 276 | color: crimson !important; 277 | } 278 | .author-box { 279 | font-size: 50% !important; 280 | text-align: left; 281 | } 282 | .reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { 283 | text-transform: none !important; 284 | } 285 | code.hljs { 286 | font-size: 100% !important; 287 | background: #F0F0F0 !important; 288 | } 289 | .reveal .slides section .code-presenting-annotation { 290 | background: #303030 !important; 291 | padding: 10px !important; 292 | margin: 100px 0px !important; 293 | font-size: 70% !important; 294 | } 295 | .mt20 { 296 | margin-top: 20px !important; 297 | } 298 | .mt30 { 299 | margin-top: 30px !important; 300 | } 301 | .ml5 { 302 | margin-left: 5em !important; 303 | } 304 | .ml20 { 305 | margin-left: 5em !important; 306 | } 307 | .mr5 { 308 | margin-right: 5em !important; 309 | } 310 | .mr20 { 311 | margin-right: 5em !important; 312 | } 313 | div.slide1 h1 { 314 | text-align: center !important; 315 | } 316 | ul.number-eg { 317 | list-style: none !important; 318 | } 319 | div.arraytype { 320 | font-size: 2em; 321 | margin-left:2em; 322 | } 323 | div.objecttype { 324 | font-size: 2em; 325 | margin-right:2em; 326 | } 327 | section.jptr-result { 328 | font-size: .6em !important; 329 | } 330 | section.chresc { 331 | font-size: .8em !important; 332 | } 333 | section.chresc table { 334 | font-size: .6em !important; 335 | } 336 | section.whitespace table { 337 | font-size: .5em !important; 338 | } 339 | -------------------------------------------------------------------------------- /template/img/batman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/batman.png -------------------------------------------------------------------------------- /template/img/bg/black.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/bg/black.jpg -------------------------------------------------------------------------------- /template/img/bg/blue.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/bg/blue.jpg -------------------------------------------------------------------------------- /template/img/bg/gray.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/bg/gray.jpg -------------------------------------------------------------------------------- /template/img/bg/green.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/bg/green.jpg -------------------------------------------------------------------------------- /template/img/bg/orange.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/bg/orange.jpg -------------------------------------------------------------------------------- /template/img/bg/pink.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/bg/pink.jpg -------------------------------------------------------------------------------- /template/img/bg/purple.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/bg/purple.jpg -------------------------------------------------------------------------------- /template/img/bg/white.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/bg/white.jpg -------------------------------------------------------------------------------- /template/img/bg/yellow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/bg/yellow.jpg -------------------------------------------------------------------------------- /template/img/cityscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/cityscape.png -------------------------------------------------------------------------------- /template/img/contact-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/contact-1.png -------------------------------------------------------------------------------- /template/img/contact-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/contact-2.png -------------------------------------------------------------------------------- /template/img/dataflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/dataflow.png -------------------------------------------------------------------------------- /template/img/design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/design.png -------------------------------------------------------------------------------- /template/img/developer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/developer.jpg -------------------------------------------------------------------------------- /template/img/einstein.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/einstein.png -------------------------------------------------------------------------------- /template/img/friday.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/friday.gif -------------------------------------------------------------------------------- /template/img/geek.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/geek.gif -------------------------------------------------------------------------------- /template/img/geek.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/geek.png -------------------------------------------------------------------------------- /template/img/grass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/grass.png -------------------------------------------------------------------------------- /template/img/headphones.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/headphones.jpg -------------------------------------------------------------------------------- /template/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/logo.png -------------------------------------------------------------------------------- /template/img/lovelace.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/lovelace.jpg -------------------------------------------------------------------------------- /template/img/moon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/moon.jpg -------------------------------------------------------------------------------- /template/img/olaf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/olaf.png -------------------------------------------------------------------------------- /template/img/pencils.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/pencils.jpg -------------------------------------------------------------------------------- /template/img/phone.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/phone.jpg -------------------------------------------------------------------------------- /template/img/presenter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/presenter.jpg -------------------------------------------------------------------------------- /template/img/profile/abby.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/profile/abby.jpg -------------------------------------------------------------------------------- /template/img/profile/berry.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/profile/berry.jpg -------------------------------------------------------------------------------- /template/img/profile/ronny.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/profile/ronny.jpg -------------------------------------------------------------------------------- /template/img/profile/wendy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/profile/wendy.jpg -------------------------------------------------------------------------------- /template/img/questions-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/questions-1.png -------------------------------------------------------------------------------- /template/img/questions-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/questions-2.png -------------------------------------------------------------------------------- /template/img/questions-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/questions-3.png -------------------------------------------------------------------------------- /template/img/questions-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/questions-4.png -------------------------------------------------------------------------------- /template/img/questions2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/questions2.png -------------------------------------------------------------------------------- /template/img/quotes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/quotes.jpg -------------------------------------------------------------------------------- /template/img/ribbon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/ribbon.png -------------------------------------------------------------------------------- /template/img/snowman.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/snowman.gif -------------------------------------------------------------------------------- /template/img/spotlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/spotlight.png -------------------------------------------------------------------------------- /template/img/thanks.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/thanks.jpg -------------------------------------------------------------------------------- /template/img/tip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/img/tip.png -------------------------------------------------------------------------------- /template/jpt-eg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prataprc/jsondata/f8d45ce0f407294ec47807913cd21812e921365a/template/jpt-eg.png -------------------------------------------------------------------------------- /template/md/about/PITCHME.md: -------------------------------------------------------------------------------- 1 | ---?image=template/img/pencils.jpg 2 | @title[About Templates] 3 | 4 | ## @color[black](About
Slide Templates) 5 | 6 | @fa[arrow-down text-black] 7 | 8 | @snap[south docslink span-50] 9 | [The Template Docs](https://gitpitch.com/docs/the-template) 10 | @snapend 11 | 12 | 13 | +++?image=template/img/bg/blue.jpg&color=white&position=top&size=100% 50% 14 | @title[Meet The Team] 15 | 16 | @snap[north text-white span-100] 17 | @size[1.5em](Meet The Team) 18 | @snapend 19 | 20 | @snap[west about-team-pic] 21 | ![WENDY](template/img/profile/wendy.jpg) 22 | @snapend 23 | 24 | @snap[south-west text-06] 25 | @color[#4487F2](Wendy Sesay) 26 |

27 | @fa[twitter](wendy) 28 |
29 | Graphic Designer 30 | @snapend 31 | 32 | @snap[midpoint about-team-pic about-team-pic-center] 33 | ![ABBY](template/img/profile/abby.jpg) 34 | @snapend 35 | 36 | @snap[south text-06] 37 | @color[#4487F2](Abby Bauer) 38 |

39 | @fa[github](abbycode) 40 |
41 | Lead Developer 42 | @snapend 43 | 44 | @snap[east about-team-pic] 45 | ![BERRY](template/img/profile/berry.jpg) 46 | @snapend 47 | 48 | @snap[south-east text-06] 49 | @color[#4487F2](Berry Nguyen) 50 |

51 | @fa[linkedin](berryngu) 52 |
53 | Channel Marketing 54 | @snapend 55 | 56 | @snap[north-east template-note text-white] 57 | Team intro template. 58 | @snapend 59 | 60 | +++?image=template/img/lovelace.jpg&position=right&size=42% 65% 61 | @title[Personal Biography] 62 | 63 | @snap[north-west bio-name] 64 | Ada Lovelace 65 | @snapend 66 | 67 | @snap[west text-08 span-60] 68 | A gifted mathematician. Born 1815. 69 |

70 | Inspired by Babbage’s Analytical Engine she imagined the modern-day, general-purpose computer back in 1843.

Ada is now recognized as the first computer programmer. 71 | @snapend 72 | 73 | @snap[south-west template-note text-gray] 74 | Simple biography template. 75 | @snapend 76 | -------------------------------------------------------------------------------- /template/md/announcement/PITCHME.md: -------------------------------------------------------------------------------- 1 | ---?image=template/img/pencils.jpg 2 | @title[Announcement Templates] 3 | 4 | ## @color[black](Announcement
Slide Templates) 5 | 6 | @fa[arrow-down text-black] 7 | 8 | @snap[south docslink span-50] 9 | [The Template Docs](https://gitpitch.com/docs/the-template) 10 | @snapend 11 | 12 | 13 | +++ 14 | @title[Big News Teaser] 15 | 16 | @snap[north announce-big-news] 17 | BIG 18 | @snapend 19 | 20 | @snap[south announce-big-news text-orange] 21 | NEWS 22 | @snapend 23 | 24 | @snap[south-west template-note text-gray] 25 | Big-news teaser template. 26 | @snapend 27 | 28 | 29 | +++ 30 | @title[New Release Teaser] 31 | 32 | ## New Release 33 | 34 | @css[text-pink](@fa[calendar] January, 2019.) 35 | 36 | @snap[south-west template-note text-gray] 37 | Upcoming-release teaser template. 38 | @snapend 39 | 40 | 41 | +++?color=linear-gradient(to top, #ffb347, #ffcc33) 42 | @title[Coming Soon Teaser] 43 | 44 | @snap[midpoint announce-coming-soon text-white] 45 | COMING 46 | @snapend 47 | 48 | @snap[south text-white] 49 | SOON ;) 50 | @snapend 51 | 52 | @snap[south-west template-note text-white] 53 | Coming-soon teaser template. 54 | @snapend 55 | -------------------------------------------------------------------------------- /template/md/boxed-text/PITCHME.md: -------------------------------------------------------------------------------- 1 | ---?image=template/img/pencils.jpg 2 | @title[Boxed Text Templates] 3 | 4 | ## @color[black](Boxed Text
Slide Templates) 5 | 6 | @fa[arrow-down text-black] 7 | 8 | @snap[south docslink span-50] 9 | [The Template Docs](https://gitpitch.com/docs/the-template) 10 | @snapend 11 | 12 | +++?image=template/img/cityscape.png&position=bottom&size=100% 60% 13 | 14 | @snap[north span-65] 15 | @box[bg-blue text-white rounded](Tech Meetup News#Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.

Ut enim ad minim veniam quis.) 16 | @snapend 17 | 18 | @snap[midpoint template-note text-gray] 19 |
Boxed text with title template. 20 | @snapend 21 | 22 | +++?color=black 23 | 24 | @snap[west span-45] 25 | # Step by Step 26 | @snapend 27 | 28 | @snap[north-east span-60 fragment] 29 | @box[bg-purple text-white](Step 1.#Lorem ipsum dolor sit amet, consectetur adipiscing elit.) 30 | @snapend 31 | 32 | @snap[east span-60 fragment] 33 | @box[bg-orange text-white](Step 2.#Sed do eiusmod tempor incididunt ut labore ut enim ad.) 34 | @snapend 35 | 36 | @snap[south-east span-60 fragment] 37 | @box[bg-pink text-white](Step 3.#Cupidatat non proident sunt in culpa officia veniam quis.) 38 | @snapend 39 | 40 | @snap[south-west template-note text-white] 41 | Boxed text fragments template. 42 | @snapend 43 | 44 | +++?color=linear-gradient(120deg, #f6d365 0%, #fda085 100%); 45 | 46 | @box[bg-orange text-white demo-box-text-padding rounded](Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.) 47 | 48 | @snap[south-west template-note text-white] 49 |
Boxed text without title template. 50 | @snapend 51 | 52 | +++?color=black 53 | 54 | @snap[north-west span-40] 55 | @box[bg-green text-white demo-box-step-padding](1. Plan#Lorem ipsum dolor sit amet eiusmod) 56 | @snapend 57 | 58 | @snap[north-east span-40] 59 | @box[bg-orange text-white demo-box-step-padding rounded](2. Build#Sed do eiusmod tempor labore) 60 | @snapend 61 | 62 | @snap[south-east span-40] 63 | @box[bg-pink text-white demo-box-step-padding](3. Measure#Cupidatat non proident sunt in) 64 | @snapend 65 | 66 | @snap[south-west span-40] 67 | @box[bg-blue text-white demo-box-step-padding waved](4. Repeat#Ut enim ad minim veniam prodient) 68 | @snapend 69 | 70 | @snap[midpoint] 71 | @fa[refresh fa-3x] 72 | @snapend 73 | 74 | @snap[south template-note text-white] 75 | Mixed styles
boxed template. 76 | @snapend 77 | -------------------------------------------------------------------------------- /template/md/code-presenting/PITCHME.md: -------------------------------------------------------------------------------- 1 | ---?image=template/img/pencils.jpg 2 | @title[Code Presenting Templates] 3 | 4 | ## @color[black](Code Presenting
Slide Templates) 5 | 6 | @fa[arrow-down text-black] 7 | 8 | @snap[south docslink span-50] 9 | [The Template Docs](https://gitpitch.com/docs/the-template) 10 | @snapend 11 | 12 | 13 | +++?code=template/src/go/server.go&lang=golang 14 | @title[Repo Source File] 15 | 16 | @[1,3-6](Present code found within any repository source file.) 17 | @[8-18](Without ever leaving your slideshow.) 18 | @[19-28](Using GitPitch code-presenting with (optional) annotations.) 19 | 20 | @snap[north-east template-note text-gray] 21 | Code presenting repository source file template. 22 | @snapend 23 | 24 | 25 | +++?color=lavender 26 | @title[Fenced Code Block] 27 | 28 | ```javascript 29 | // Include http module. 30 | var http = require("http"); 31 | 32 | // Create the server. Function passed as parameter 33 | // is called on every request made. 34 | http.createServer(function (request, response) { 35 | // Attach listener on end event. This event is 36 | // called when client sent, awaiting response. 37 | request.on("end", function () { 38 | // Write headers to the response. 39 | // HTTP 200 status, Content-Type text/plain. 40 | response.writeHead(200, { 41 | 'Content-Type': 'text/plain' 42 | }); 43 | // Send data and end response. 44 | response.end('Hello HTTP!'); 45 | }); 46 | 47 | // Listen on the 8080 port. 48 | }).listen(8080); 49 | ``` 50 | 51 | @[1,2](You can present code inlined within your slide markdown too.) 52 | @[9-17](Your code is displayed using code-syntax highlighting just like your IDE.) 53 | @[19-20](Again, all of this without ever leaving your slideshow.) 54 | 55 | @snap[north-east template-note text-gray] 56 | Code presenting fenced code block template. 57 | @snapend 58 | 59 | 60 | +++?gist=onetapbeyond/494e0fecaf0d6a2aa2acadfb8eb9d6e8&lang=scala&color=black 61 | @title[GitHub GIST] 62 | 63 | @[1-6](You can even present code found within any GitHub GIST.) 64 | @[41-53](GIST source code is beautifully rendered on any slide.) 65 | @[57-62](Code-presenting works seamlessly both online and offline.) 66 | 67 | @snap[north-east template-note text-white] 68 | Code presenting GitHub GIST template. 69 | @snapend 70 | 71 | 72 | +++?color=#36454F 73 | @title[Fenced Text Block] 74 | 75 | ```text 76 | . 77 | ├── PITCHME.md 78 | ├── PITCHME.yaml 79 | └── template 80 | ├── css 81 | │   └── PITCHME.css 82 | ├── img 83 | │   ├── batman.png 84 | │   ├── dataflow.png 85 | │   ├── developer.jpg 86 | │   └── .... 87 | └── md 88 |    ├── about/PITCHME.md 89 |    ├── announcement/PITCHME.md 90 |    ├── code-presenting/PITCHME.md 91 |    ├── header-footer/PITCHME.md 92 |    ├── image/PITCHME.md 93 |    ├── list-content/PITCHME.md 94 |    ├── quotation/PITCHME.md 95 |    ├── sidebar/PITCHME.md 96 |    ├── sidebox/PITCHME.md 97 |    ├── split-screen/PITCHME.md 98 |    └── wrap-up/PITCHME.md 99 | ``` 100 | 101 | @[1-3, 6](Code presenting can also be used to step through any text-based content.) 102 | @[4,5,7,12](Here for example we can navigate through the directory structure for this template.) 103 | @[12-23](We can see that this template uses GitPitch's cool modular markdown support @fa[smile-o fa-spin]) 104 | 105 | @snap[north-east template-note text-white] 106 | Code presenting fenced text block template. 107 | @snapend 108 | -------------------------------------------------------------------------------- /template/md/header-footer/PITCHME.md: -------------------------------------------------------------------------------- 1 | ---?image=template/img/pencils.jpg 2 | @title[Header + Footer Templates] 3 | 4 | ## @color[black](Header & Footer
Slide Templates) 5 | 6 | @fa[arrow-down text-black] 7 | 8 | @snap[south docslink span-50] 9 | [The Template Docs](https://gitpitch.com/docs/the-template) 10 | @snapend 11 | 12 | 13 | +++?image=template/img/bg/orange.jpg&position=top&size=100% 20% 14 | @title[Header Bar + Image Body] 15 | 16 | @snap[north text-white span-100] 17 | @size[1.5em](Lorem Ipsum Dolor Sit Amet) 18 | @snapend 19 | 20 | @snap[south span-100] 21 | ![DATAFLOW](template/img/dataflow.png) 22 |

23 | @snapend 24 | 25 | @snap[south-west template-note text-gray] 26 | Header bar with image body template. 27 | @snapend 28 | 29 | 30 | +++?image=template/img/bg/blue.jpg&position=bottom&size=100% 20% 31 | @title[Footer Bar + Image Body] 32 | 33 | @snap[south text-white span-100] 34 | @size[1.5em](Lorem Ipsum Dolor Sit Amet) 35 | @snapend 36 | 37 | @snap[north span-100] 38 |
39 | ![DATAFLOW](template/img/dataflow.png) 40 | @snapend 41 | 42 | @snap[north-east template-note text-gray span-40] 43 | Footer bar with image body template. 44 | @snapend 45 | 46 | 47 | +++?image=template/img/bg/black.jpg&position=center&size=100% 65% 48 | @title[Center Bar + Image Body] 49 | 50 | @snap[north span-100] 51 | @size[1.5em](Lorem Ipsum Dolor Sit Amet) 52 | @snapend 53 | 54 | @snap[midpoint span-80] 55 | ![DATAFLOW](template/img/dataflow.png) 56 | @snapend 57 | 58 | @snap[south-west template-note text-gray] 59 | Center bar with image body template. 60 | @snapend 61 | 62 | 63 | +++?image=template/img/bg/purple.jpg&position=top&size=100% 20% 64 | @title[Header Bar + List Body] 65 | 66 | @snap[north text-white span-100] 67 | @size[1.5em](Lorem Ipsum Dolor Sit Amet) 68 | @snapend 69 | 70 | @snap[south span-100] 71 | @ol[bullet-green](false) 72 | - Consectetur adipiscing elit 73 | - Sed do eiusmod tempor 74 | - Ut enim ad minim veniam 75 | - Duis aute irure dolor in 76 | - Excepteur sint occaecat 77 | - Cupidatat non proident 78 | - Sunt in culpa qui officia 79 | @olend 80 |

81 | @snapend 82 | 83 | @snap[south-west template-note text-gray] 84 | Header bar with list body template. 85 | @snapend 86 | 87 | 88 | +++?image=template/img/bg/green.jpg&position=center&size=100% 65% 89 | @title[Center Bar + List Body] 90 | 91 | @snap[north span-100] 92 | @size[1.5em](Lorem Ipsum Dolor Sit Amet) 93 | @snapend 94 | 95 | @snap[midpoint text-white span-100] 96 | @ul[header-footer-list-shrink](false) 97 | - Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 98 | - Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 99 | - Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 100 | - Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 101 | @snapend 102 | 103 | @snap[south-west template-note text-gray] 104 | Center bar with list body template. 105 | @snapend 106 | -------------------------------------------------------------------------------- /template/md/image/PITCHME.md: -------------------------------------------------------------------------------- 1 | ---?image=template/img/pencils.jpg 2 | @title[Image Templates] 3 | 4 | ## @color[black](Image
Slide Templates) 5 | 6 | @fa[arrow-down text-black] 7 | 8 | @snap[south docslink span-50] 9 | [The Template Docs](https://gitpitch.com/docs/the-template) 10 | @snapend 11 | 12 | 13 | +++?image=template/img/headphones.jpg 14 | @title[Covered Background] 15 | 16 | @snap[west text-black span-15] 17 | **@size[1.2em](Where words fail, music speaks.)** 18 | @snapend 19 | 20 | @snap[south-west template-note text-white] 21 | Covered background image template. 22 | @snapend 23 | 24 | 25 | +++?image=template/img/dataflow.png&size=contain 26 | @title[Contained Background] 27 | 28 | @snap[south-west template-note text-gray] 29 | Contained background image template. 30 | @snapend 31 | 32 | 33 | +++?image=template/img/batman.png&size=contain&color=linear-gradient(to right, #009fff, #ec2f4b) 34 | @title[Transparent Background] 35 | 36 | @snap[north-east text-white span-25] 37 | @quote[It's what I do that defines me.](Bruce Wayne) 38 | @snapend 39 | 40 | @snap[south-west template-note text-white] 41 | Transparent background with gradient template. 42 | @snapend 43 | 44 | @snap[south-east text-white] 45 | @size[0.3em](Lego Batman Clipart by Clipart.info is licensed under CC BY 4.0) 46 | @snapend 47 | 48 | 49 | +++?image=template/img/geek.png&repeat=repeat-x&color=#F5DB2E&size=25% auto 50 | @title[Repeat Background] 51 | 52 | @snap[north-east text-black span-70] 53 | @quote[Beware of geeks bearing formulas.] 54 | @snapend 55 | 56 | @snap[south-west template-note text-black] 57 | Transparent background image-repeat template. 58 | @snapend 59 | 60 | 61 | +++ 62 | @title[Side-by-Side Images] 63 | 64 | @snap[west span-50] 65 | ![SNOWMAN](template/img/snowman.gif) 66 | @snapend 67 | 68 | @snap[east span-50] 69 | ![OLAF](template/img/olaf.png) 70 | @snapend 71 | 72 | @snap[south-west template-note text-gray] 73 | Side-by-side inline images template. 74 | @snapend 75 | 76 | 77 | +++?image=template/img/geek.gif 78 | @title[GIF Background] 79 | 80 | @snap[north-east text-white] 81 | The Suave Geek 82 | @snapend 83 | 84 | @snap[south-east template-note text-white] 85 | Covered background animated GIF template. 86 | @snapend 87 | 88 | 89 | +++?image=template/img/grass.png&position=bottom&size=100% 30% 90 | @title[Positioned Background] 91 | 92 | ## Is the @size[2.2em](grass) @color[green](always) greener on the other side? 93 | 94 | @snap[north-east template-note text-gray] 95 | Fixed position background image template. 96 | @snapend 97 | -------------------------------------------------------------------------------- /template/md/list-content/PITCHME.md: -------------------------------------------------------------------------------- 1 | ---?image=template/img/pencils.jpg 2 | @title[List Content Templates] 3 | 4 | ## @color[black](List Content
Slide Templates) 5 | 6 | @fa[arrow-down text-black] 7 | 8 | @snap[south docslink span-50] 9 | [The Template Docs](https://gitpitch.com/docs/the-template) 10 | @snapend 11 | 12 | 13 | +++?image=template/img/spotlight.png&position=top right&size=20% auto 14 | @title[Title + Concise List] 15 | 16 | @snap[north-west] 17 | The Agenda 18 | @snapend 19 | 20 | @snap[south-west list-content-concise span-100] 21 | @ol[list-bullets-black](false) 22 | - Lorem ipsum dolor sit amet 23 | - Consectetur adipiscing elit 24 | - Sed do eiusmod tempor 25 | - Ut enim ad minim veniam 26 | - Duis aute irure dolor in 27 | - Excepteur sint occaecat 28 | - Cupidatat non proident 29 | - Sunt in culpa qui officia 30 | @olend 31 |

32 | @snapend 33 | 34 | @snap[south-west template-note text-gray] 35 | Concise ordered list-items template. 36 | @snapend 37 | 38 | 39 | +++?image=template/img/spotlight.png&position=top right&size=20% auto 40 | @title[Title + List Fragments] 41 | 42 | @snap[north-west] 43 | The Agenda [ Step-by-Step ] 44 | @snapend 45 | 46 | @snap[south-west list-content-concise span-100] 47 | @ol 48 | - Lorem ipsum dolor sit amet 49 | - Consectetur adipiscing elit 50 | - Sed do eiusmod tempor 51 | - Ut enim ad minim veniam 52 | - Duis aute irure dolor in 53 | - Excepteur sint occaecat 54 | - Cupidatat non proident 55 | - Sunt in culpa qui officia 56 | @olend 57 |

58 | @snapend 59 | 60 | @snap[south-west template-note text-gray] 61 | Concise list-item fragments template. 62 | @snapend 63 | 64 | 65 | +++?image=template/img/spotlight.png&position=top right&size=20% auto 66 | @title[Title + Verbose List] 67 | 68 | @snap[north-west] 69 | The Key Concepts 70 | @snapend 71 | 72 | @snap[west list-content-verbose span-100] 73 |
74 | @ul[](false) 75 | - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 76 | - Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 77 | - Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 78 | - Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 79 | @ulend 80 | @snapend 81 | 82 | @snap[south-west template-note text-gray] 83 | Verbose unordered list-items template. 84 | @snapend 85 | 86 | 87 | +++?image=template/img/spotlight.png&position=top right&size=20% auto 88 | @title[Title + List Fragments] 89 | 90 | @snap[north-west] 91 | The Key Concepts [ Step-by-Step ] 92 | @snapend 93 | 94 | @snap[west list-content-verbose span-100] 95 |
96 | @ul[list-bullets-circles] 97 | - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 98 | - Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 99 | - Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 100 | - Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 101 | @ulend 102 | @snapend 103 | 104 | @snap[south-west template-note text-gray] 105 | Verbose list-item fragments template. 106 | @snapend 107 | -------------------------------------------------------------------------------- /template/md/quotation/PITCHME.md: -------------------------------------------------------------------------------- 1 | ---?image=template/img/pencils.jpg 2 | @title[Quotation Templates] 3 | 4 | ## @color[black](Quotation
Slide Templates) 5 | 6 | @fa[arrow-down text-black] 7 | 8 | @snap[south docslink span-50] 9 | [The Template Docs](https://gitpitch.com/docs/the-template) 10 | @snapend 11 | 12 | 13 | +++?image=template/img/einstein.png&position=left&size=60% auto 14 | @title[Quote + Image] 15 | 16 | @snap[north-east span-60] 17 | @quote[We cannot solve our problems with the same thinking we used when we created them.] 18 | @snapend 19 | 20 | @snap[south-east template-note text-gray] 21 | Simple quotation with image template. 22 | @snapend 23 | 24 | +++?image=template/img/moon.jpg&size=cover 25 | @title[Quote Attributed] 26 | 27 | @snap[east text-white span-55] 28 | @quote[Houston, Tranquillity Base here. The Eagle has landed.](Neil Armstrong) 29 | @snapend 30 | 31 | @snap[north-east template-note text-white] 32 | Quotation with attribution template. 33 | @snapend 34 | 35 | +++?image=template/img/quotes.jpg 36 | @title[Quote Cloud] 37 | 38 | @snap[south-east span-50] 39 | @quote[GitPitch Desktop with speaker notes is AMAZING!](Dave T.) 40 |
41 | @snapend 42 | 43 | @snap[north-west] 44 |
45 | @quote[GitPitch is just WONDERFUL!](Mohammed A.) 46 | @snapend 47 | 48 | @snap[south-west span-20] 49 | @quote[Just discovered GitPitch. And WOW!](Adrian K.) 50 | @snapend 51 | 52 | @snap[north-west template-note text-black] 53 | Quotation cloud template. 54 | @snapend 55 | -------------------------------------------------------------------------------- /template/md/sidebar/PITCHME.md: -------------------------------------------------------------------------------- 1 | ---?image=template/img/pencils.jpg 2 | @title[Sidebar Templates] 3 | 4 | ## @color[black](Sidebar
Slide Templates) 5 | 6 | @fa[arrow-down text-black] 7 | 8 | @snap[south docslink span-50] 9 | [The Template Docs](https://gitpitch.com/docs/the-template) 10 | @snapend 11 | 12 | 13 | +++?image=template/img/bg/blue.jpg&position=left&size=30% 100% 14 | @title[Sidebar + Heading] 15 | 16 | @snap[west text-white] 17 | @size[3em](1.) 18 | @snapend 19 | 20 | @snap[east span-70] 21 |

Lorem ipsum @css[text-blue](dolor)

22 | @snapend 23 | 24 | @snap[north-east template-note text-gray] 25 | Sidebar with heading body template. 26 | @snapend 27 | 28 | 29 | +++?image=template/img/bg/orange.jpg&position=left&size=30% 100% 30 | @title[Sidebar + Image Body] 31 | 32 | @snap[west text-white] 33 | @size[3em](2.) 34 | @snapend 35 | 36 | @snap[east span-70] 37 | ![SNOWMAN](template/img/snowman.gif) 38 | @snapend 39 | 40 | @snap[north-east template-note text-gray] 41 | Sidebar with image body template. 42 | @snapend 43 | 44 | 45 | +++?image=template/img/bg/green.jpg&position=left&size=30% 100% 46 | @title[Sidebar + Mixed Body] 47 | 48 | @snap[west text-white] 49 | @size[3em](3.) 50 | @snapend 51 | 52 | @snap[east span-70] 53 | @fa[rocket fa-5x text-orange] 54 |

55 | We Have Lift Off 56 | @snapend 57 | 58 | @snap[north-east template-note text-gray] 59 | Sidebar with mixed body template. 60 | @snapend 61 | 62 | 63 | +++?image=template/img/bg/pink.jpg&position=left&size=30% 100% 64 | @title[Sidebar + Text Body] 65 | 66 | @snap[west text-white] 67 | @size[3em](4.) 68 | @snapend 69 | 70 | @snap[east span-70] 71 | Duis aute irure dolor in reprehenderit in voluptate velit @size[1.25em](esse cillum) dolore eu fugiat nulla pariatur. 72 |

73 | Excepteur sint occaecat cupidatat non proident, @css[text-pink](sunt in culpa) qui officia deserunt mollit anim id est laborum. 74 | @snapend 75 | 76 | @snap[north-east template-note text-gray] 77 | Sidebar with text body template. 78 | @snapend 79 | -------------------------------------------------------------------------------- /template/md/sidebox/PITCHME.md: -------------------------------------------------------------------------------- 1 | ---?image=template/img/pencils.jpg 2 | @title[Sidebox Templates] 3 | 4 | ## @color[black](Sidebox
Slide Templates) 5 | 6 | @fa[arrow-down text-black] 7 | 8 | @snap[south docslink span-50] 9 | [The Template Docs](https://gitpitch.com/docs/the-template) 10 | @snapend 11 | 12 | 13 | +++?image=template/img/bg/blue.jpg&position=left&size=30% 50% 14 | @title[Sidebox + Heading] 15 | 16 | @snap[west text-white] 17 | @size[3em](A.) 18 | @snapend 19 | 20 | @snap[east span-75] 21 |

Sunt in @css[text-pink](culpa) officia

22 | @snapend 23 | 24 | @snap[north-east template-note text-gray] 25 | Sidebox with heading body template. 26 | @snapend 27 | 28 | 29 | +++?image=template/img/bg/orange.jpg&position=left&size=30% 50% 30 | @title[Sidebox + Image Body] 31 | 32 | @snap[west text-white] 33 | @size[3em](B.) 34 | @snapend 35 | 36 | @snap[east span-70] 37 | ![FRIDAY](template/img/friday.gif) 38 | @snapend 39 | 40 | @snap[north-east template-note text-gray] 41 | Sidebox with image body template. 42 | @snapend 43 | 44 | 45 | +++?image=template/img/bg/green.jpg&position=left&size=30% 50% 46 | @title[Sidebox + Mixed Body] 47 | 48 | @snap[west text-white] 49 | @size[3em](C.) 50 | @snapend 51 | 52 | @snap[east span-70] 53 | @fa[bath fa-5x text-blue] 54 |

55 | How to write clean code. 56 | @snapend 57 | 58 | @snap[north-east template-note text-gray] 59 | Sidebox with mixed body template. 60 | @snapend 61 | 62 | 63 | +++?image=template/img/bg/pink.jpg&position=left&size=30% 50% 64 | @title[Sidebox + Text Body] 65 | 66 | @snap[west text-white] 67 | @size[3em](D.) 68 | @snapend 69 | 70 | @snap[east span-70] 71 | Ut enim ad minim veniam, quis @css[text-pink](nostrud exercitation ullamco laboris) nisi ut aliquip ex ea commodo. 72 |

73 | Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 74 | @snapend 75 | 76 | @snap[north-east template-note text-gray] 77 | Sidebox with text body template. 78 | @snapend 79 | -------------------------------------------------------------------------------- /template/md/split-screen/PITCHME.md: -------------------------------------------------------------------------------- 1 | ---?image=template/img/pencils.jpg 2 | @title[Split-Screen Templates] 3 | 4 | ## @color[black](Split-Screen
Slide Templates) 5 | 6 | @fa[arrow-down text-black] 7 | 8 | @snap[south docslink span-50] 9 | [The Template Docs](https://gitpitch.com/docs/the-template) 10 | @snapend 11 | 12 | +++?image=template/img/bg/orange.jpg&position=right&size=50% 100% 13 | @title[Heading + List Body] 14 | 15 | @snap[west text-16 text-bold text-italic text-orange span-50] 16 | Topics to be covered today 17 | @snapend 18 | 19 | @snap[east text-white span-45] 20 | @ol[split-screen-list text-08](false) 21 | - Lorem ipsum dolor sit amet, consectetur elit 22 | - Ut enim ad minim veniam, quis exercitation 23 | - Duis aute irure dolor in reprehenderit in voluptate 24 | @olend 25 | @snapend 26 | 27 | @snap[south-west template-note text-gray] 28 | Split-screen heading and list body template. 29 | @snapend 30 | 31 | 32 | +++?image=template/img/bg/pink.jpg&position=left&size=70% 100% 33 | @title[Heading + List Body] 34 | 35 | @snap[east text-17 text-bold text-pink span-50] 36 | Top
Tips! 37 | @snapend 38 | 39 | @snap[west text-white span-65] 40 | @ul[split-screen-list text-08](false) 41 | - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore 42 | - Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 43 | - Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore 44 | - Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id 45 | @ulend 46 | @snapend 47 | 48 | @snap[south-west template-note text-white] 49 | Split-screen heading and list body template. 50 | @snapend 51 | 52 | 53 | +++?image=template/img/bg/black.jpg&position=right&size=50% 100% 54 | @title[Text + Image] 55 | 56 | @snap[east span-40 text-11 text-white] 57 | Lorem ipsum sit dolor amet, consectetur elit. 58 | @snapend 59 | 60 | @snap[west] 61 | @img[split-screen-img span-55](template/img/developer.jpg) 62 | @snapend 63 | 64 | @snap[south-west template-note text-gray] 65 | Split-screen text and image template. 66 | @snapend 67 | 68 | 69 | +++?image=template/img/bg/green.jpg&position=left&size=50% 100% 70 | @title[Text + Image Fragment] 71 | 72 | @snap[west span-40 text-11 text-white] 73 | Lorem ipsum sit dolor amet, consectetur elit. 74 | @snapend 75 | 76 | @snap[east fragment] 77 | @img[split-screen-img span-55](template/img/developer.jpg) 78 | @snapend 79 | 80 | @snap[south-west template-note text-white] 81 | Split-screen text and image-fragment template. 82 | @snapend 83 | 84 | 85 | +++?image=template/img/bg/black.jpg&position=left&size=50% 100% 86 | @title[Text + Image Centered] 87 | 88 | @snap[west span-40 text-white] 89 | Lorem ipsum
sit dolor amet, consectetur elit. 90 | @snapend 91 | 92 | @snap[midpoint] 93 | @img[split-screen-img span-55](template/img/developer.jpg) 94 | @snapend 95 | 96 | @snap[east span-30 text-08 text-center text-black] 97 | Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 98 | @snapend 99 | 100 | @snap[south-west template-note text-white] 101 | Split-screen text and centered image template. 102 | @snapend 103 | 104 | 105 | +++?image=template/img/bg/pink.jpg&position=right&size=50% 100% 106 | @title[Text + Image Centered] 107 | 108 | @snap[east span-30 text-08 text-center text-white] 109 | Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 110 | @snapend 111 | 112 | @snap[midpoint] 113 | @img[split-screen-img span-55](template/img/developer.jpg) 114 | @snapend 115 | 116 | @snap[west span-35] 117 | Lorem ipsum
sit dolor amet, consectetur elit. 118 | @snapend 119 | 120 | @snap[south-west template-note text-gray] 121 | Split-screen text and centered image template. 122 | @snapend 123 | -------------------------------------------------------------------------------- /template/md/wrap-up/PITCHME.md: -------------------------------------------------------------------------------- 1 | ---?image=template/img/pencils.jpg 2 | @title[Wrap-Up Templates] 3 | 4 | ## @color[black](Wrap-Up
Slide Templates) 5 | 6 | @fa[arrow-down text-black] 7 | 8 | @snap[south docslink span-50] 9 | [The Template Docs](https://gitpitch.com/docs/the-template) 10 | @snapend 11 | 12 | 13 | +++?image=template/img/questions-1.png&size=80% auto 14 | @title[Questions #1] 15 | 16 | @snap[south-west template-note text-gray] 17 | Audience questions template. 18 | @snapend 19 | 20 | 21 | +++?image=template/img/questions-2.png&size=80% auto 22 | @title[Questions #2] 23 | 24 | @snap[south-west template-note text-gray] 25 | Audience questions template. 26 | @snapend 27 | 28 | 29 | +++?image=template/img/questions-3.png&size=auto 60% 30 | @title[Questions #3] 31 | 32 | @snap[south-west template-note text-gray] 33 | Audience questions template. 34 | @snapend 35 | 36 | 37 | +++ 38 | @title[Questions #4] 39 | 40 | @snap[east span-50] 41 | ![QUESTIONS-4](template/img/questions-4.png) 42 | @snapend 43 | 44 | @snap[south-west template-note text-gray] 45 | Audience questions template. 46 | @snapend 47 | 48 | 49 | +++?color=white 50 | @title[Get In Touch #1] 51 | 52 | @snap[west] 53 | @css[contact-name](Wendy Sesay)
54 | @fa[twitter-square text-blue pad-right-icon]@css[twitter-handle text-blue](@wendy)
55 | @fa[envelope-o text-pink pad-right-icon]@css[contact-email text-pink](wendy@gmail.com) 56 |
57 |
58 | @css[contact-name](Abby Bauer)
59 | @fa[twitter-square text-blue pad-right-icon]@css[twitter-handle text-blue](@abbycode)
60 | @fa[github-square pad-right-icon]@css[git-handle](abbycode)
61 | @fa[envelope-o text-pink pad-right-icon]@css[contact-email text-pink](abcode@hotmail.com) 62 |
63 |
64 | @css[contact-name](Berry Nguyen)
65 | @fa[twitter-square text-blue pad-right-icon]@css[twitter-handle text-blue](@BerryNgu)
66 | @fa[envelope-o text-pink pad-right-icon]@css[contact-email text-pink](B.Nguyen@gmail.com) 67 | @snapend 68 | 69 | @snap[east] 70 |

Contact Us

71 | @snapend 72 | 73 | @snap[north-east template-note text-gray] 74 | Contact info template. 75 | @snapend 76 | 77 | 78 | +++?color=#ED7D31 79 | @title[Get In Touch #2] 80 | 81 | @snap[east] 82 | @css[contact-name](Wendy Sesay)
83 | @css[twitter-handle text-white](@wendy) 84 | @fa[twitter-square text-white pad-left-icon]
85 | @css[contact-email text-white](wendy@gmail.com) 86 | @fa[envelope-o text-white pad-left-icon]
87 |
88 | @css[contact-name](Abby Bauer)
89 | @css[twitter-handle text-white](@abbycode)@fa[twitter-square text-white pad-left-icon]
90 | @css[git-handle text-white](abbycode)@fa[github-square text-white pad-left-icon]
91 | @css[contact-email text-white](abcode@hotmail.com)@fa[envelope-o text-white pad-left-icon]
92 |
93 | @css[contact-name](Berry Nguyen)
94 | @css[twitter-handle text-white](@BerryNgu)@fa[twitter-square text-white pad-left-icon]
95 | @css[contact-email text-white](B.Nguyen@gmail.com)@fa[envelope-o text-white pad-left-icon]
96 | @snapend 97 | 98 | @snap[west span-50] 99 | ![CONTACT-2](template/img/contact-2.png) 100 | @snapend 101 | 102 | @snap[north-west text-white template-note] 103 | Contact info template. 104 | @snapend 105 | 106 | 107 | +++ 108 | @title[Get In Touch #3] 109 | 110 | @snap[west contact-links] 111 | @css[contact-name](David Russell)
112 | 113 | @fa[twitter-square pad-right-icon]@css[twitter-handle](@gitpitch) 114 |
115 | 116 | @fa[github-square pad-right-icon]@css[git-handle](gitpitch) 117 |
118 | 119 | @fa[envelope-o pad-right-icon]@css[contact-email](david@gitpitch.com) 120 | 121 | @snapend 122 | 123 | @snap[east span-50] 124 | ![](template/img/contact-1.png) 125 | @snapend 126 | 127 | @snap[north-east template-note text-gray] 128 | Contact info template. 129 | @snapend 130 | -------------------------------------------------------------------------------- /template/src/go/server.go: -------------------------------------------------------------------------------- 1 | package funding 2 | 3 | type FundServer struct { 4 | Commands chan interface{} 5 | fund Fund 6 | } 7 | 8 | func NewFundServer(initialBalance int) *FundServer { 9 | server := &FundServer{ 10 | // make() creates builtins like channels 11 | Commands: make(chan interface{}), 12 | fund: NewFund(initialBalance), 13 | } 14 | 15 | // Spawn off the server's main loop immediately 16 | go server.loop() 17 | return server 18 | } 19 | 20 | func (s *FundServer) loop() { 21 | // The built-in "range" clause can iterate 22 | // over channels, amongst other things 23 | for command := range s.Commands { 24 | 25 | // Handle the command 26 | 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /testdata/qc_strings.jsons: -------------------------------------------------------------------------------- 1 | r#""hello\" \\ \/ \b \f\n\r\t""#, 2 | r#""hello\u0234\u005c \uD834\uDD1E""#, 3 | r#""汉语 / 漢語; Hàn\b \tyǔ ""#, 4 | r#""'é' character is one Unicode code point \u00e9 while 'é' \u0065\u0301 ""#, 5 | -------------------------------------------------------------------------------- /testdata/stream1.jsons: -------------------------------------------------------------------------------- 1 | 1 2 | null true false 3 | 102 10.2 0.2 4 | 00 100 001 00.00 5 | 2.00 .2 .02 .00 0.0e1 2e1 02e1 20e1 0.0e-1 2e-1 6 | -------------------------------------------------------------------------------- /testdata/stream11.jsons: -------------------------------------------------------------------------------- 1 | 02e-1 20e-1 0.0e-1 2e-1 -102 -10.2 -0.2 -00 2 | -100 -001 -00.00 -2.00 3 | -.2 -.02 -.00 -2e1 4 | 5 | -------------------------------------------------------------------------------- /testdata/stream2.jsons: -------------------------------------------------------------------------------- 1 | 2 | "hello\" " 3 | 4 | "hello\u0234\u005c \uD834\uDD1E" 5 | "'é' character is one Unicode code point \u00e9 while 'é' \u0065\u0301 " 6 | [] [10] [null,true,false,10,"tru\"e"] 7 | 8 | "汉语 / 漢語; Hàn\b \tyǔ " 9 | 10 | -------------------------------------------------------------------------------- /testdata/stream3.jsons: -------------------------------------------------------------------------------- 1 | [null, true, false, "hello\" \\ \/ \b \f\n\r\t"] [102, 10.2, 0.2, 00, "hello\u0234\u005c \uD834\uDD1E"] 2 | [100,001,00.00,2.00, "汉语 / 漢語; Hàn\b \tyǔ "] 3 | [.2,.02, .00,2e-1, 02e-1] 4 | [-102,-100,-.00, -2e1] 5 | {} {"key1": "value1"} 6 | { "key1" :"value1", "key2" :"value2"} 7 | {"z":1,"a":1,"c":1,"d":1,"f":1,"e":1,"b":1,"x":1} 8 | [ "hello", { "key1": [ "world", { "key2": { "key3": 20 } } ] } ] 9 | 10 | 11 | -------------------------------------------------------------------------------- /testdata/test_simple.jsons: -------------------------------------------------------------------------------- 1 | [ 2 | "null", 3 | "true", 4 | "false", 5 | "102", 6 | "10.2", 7 | "0.2", 8 | "00", 9 | "100", 10 | "001", 11 | "00.00", 12 | "2.00", 13 | ".2", 14 | ".02", 15 | ".00", 16 | "0.0e1", 17 | "2e1", 18 | "02e1", 19 | "20e1", 20 | "0.0e-1", 21 | "2e-1", 22 | "02e-1", 23 | "20e-1", 24 | "0.0e-1", 25 | "2e-1", 26 | "-102", 27 | "-10.2", 28 | "-0.2", 29 | "-00", 30 | "-100", 31 | "-001", 32 | "-00.00", 33 | "-2.00", 34 | "-.2", 35 | "-.02", 36 | "-.00", 37 | "-2e1", 38 | r#""hello\" \\ \/ \b \f\n\r\t""#, 39 | r#""hello\u0234\u005c \uD834\uDD1E""#, 40 | r#""汉语 / 漢語; Hàn\b \tyǔ ""#, 41 | r#""'é' character is one Unicode code point \u00e9 while 'é' \u0065\u0301 ""#, 42 | "[]", 43 | "[10]", 44 | r#" [null,true,false,10,"tru\"e"]"#, 45 | r#"[null, true, false, "hello\" \\ \/ \b \f\n\r\t"]"#, 46 | r#"[102, 10.2, 0.2, 00, "hello\u0234\u005c \uD834\uDD1E"]"#, 47 | r#"[100,001,00.00,2.00, "汉语 / 漢語; Hàn\b \tyǔ "]"#, 48 | "[.2,.02, .00,2e-1, 02e-1]", 49 | "[-102,-100,-.00, -2e1]", 50 | "{}", 51 | r#"{"key1": "value1"}"#, 52 | r#"{ "key1" :"value1", "key2" :"value2"}"#, 53 | r#"{"z":1,"a":1,"c":1,"d":1,"f":1,"e":1,"b":1,"x":1}"#, 54 | ] 55 | -------------------------------------------------------------------------------- /testdata/test_simple.jsons.ref: -------------------------------------------------------------------------------- 1 | [ 2 | Null, 3 | Bool(true), 4 | Bool(false), 5 | Json::new(102), 6 | Json::new(10.2), 7 | Json::new(0.2), 8 | Json::new(0), 9 | Json::new(100), 10 | Json::new(1), 11 | Json::new(0.0), 12 | Json::new(2.0), 13 | Json::new(0.2), 14 | Json::new(0.02), 15 | Json::new(0.0), 16 | Json::new(0.0), 17 | Json::new(20.0), 18 | Json::new(20.0), 19 | Json::new(200.0), 20 | Json::new(0.0), 21 | Json::new(0.2), 22 | Json::new(0.2), 23 | Json::new(2.0), 24 | Json::new(0.0), 25 | Json::new(0.2), 26 | Json::new(-102), 27 | Json::new(-10.2), 28 | Json::new(-0.2), 29 | Json::new(0), 30 | Json::new(-100), 31 | Json::new(-1), 32 | Json::new(-0.0), 33 | Json::new(-2.0), 34 | Json::new(-0.2), 35 | Json::new(-0.02), 36 | Json::new(-0.0), 37 | Json::new(-20.0), 38 | String("hello\" \\ / \u{8} \u{c}\n\r\t".to_string()), 39 | String("helloȴ\\ 𝄞".to_string()), 40 | String("汉语 / 漢語; Hàn\u{8} \tyǔ ".to_string()), 41 | String("'é' character is one Unicode code point \u{00e9} while 'é' \u{0065}\u{0301} ".to_string()), 42 | Array(vec![]), 43 | Array(vec![Json::new(10)]), 44 | Array(vec![ Null, Bool(true), Bool(false), Json::new(10), String("tru\"e".to_string()) ]), 45 | Array(vec![ Null, Bool(true), Bool(false), String("hello\" \\ / \u{8} \u{c}\n\r\t".to_string()) ]), 46 | Array(vec![ Json::new(102), Json::new(10.2), Json::new(0.2), Json::new(0), String("helloȴ\\ 𝄞".to_string()) ]), 47 | Array(vec![ Json::new(100), Json::new(1), Json::new(0.0), Json::new(2.0), String("汉语 / 漢語; Hàn\u{8} \tyǔ ".to_string()) ]), 48 | Array(vec![ Json::new(0.2), Json::new(0.02), Json::new(0.0), Json::new(0.2), Json::new(0.2) ]), 49 | Array(vec![ Json::new(-102), Json::new(-100), Json::new(-0.0), Json::new(-20.0) ]), 50 | Object(vec![]), 51 | Object(vec![Property::new("key1".to_string(), Json::new("value1"))]), 52 | Object(vec![Property::new("key1".to_string(),Json::new("value1")), Property::new("key2".to_string(),Json::new("value2"))]), 53 | Object(vec![ 54 | Property::new("a".to_string(),Json::new(1)), 55 | Property::new("b".to_string(),Json::new(1)), 56 | Property::new("c".to_string(),Json::new(1)), 57 | Property::new("d".to_string(),Json::new(1)), 58 | Property::new("e".to_string(),Json::new(1)), 59 | Property::new("f".to_string(),Json::new(1)), 60 | Property::new("x".to_string(),Json::new(1)), 61 | Property::new("z".to_string(),Json::new(1)), 62 | ]), 63 | ] 64 | -------------------------------------------------------------------------------- /testdata/test_simple.jsons.ref.jsons: -------------------------------------------------------------------------------- 1 | [ 2 | "null", 3 | "true", 4 | "false", 5 | "102", 6 | "1.02e1", 7 | "2e-1", 8 | "0", 9 | "100", 10 | "1", 11 | "0e0", 12 | "2e0", 13 | "2e-1", 14 | "2e-2", 15 | "0e0", 16 | "0e0", 17 | "2e1", 18 | "2e1", 19 | "2e2", 20 | "0e0", 21 | "2e-1", 22 | "2e-1", 23 | "2e0", 24 | "0e0", 25 | "2e-1", 26 | "-102", 27 | "-1.02e1", 28 | "-2e-1", 29 | "0", 30 | "-100", 31 | "-1", 32 | "-0e0", 33 | "-2e0", 34 | "-2e-1", 35 | "-2e-2", 36 | "-0e0", 37 | "-2e1", 38 | r#""hello\" \\ / \b \f\n\r\t""#, 39 | r#""helloȴ\\ 𝄞""#, 40 | r#""汉语 / 漢語; Hàn\b \tyǔ ""#, 41 | r#""'é' character is one Unicode code point é while 'é' é ""#, 42 | "[]", 43 | "[10]", 44 | r#"[null,true,false,10,"tru\"e"]"#, 45 | r#"[null,true,false,"hello\" \\ / \b \f\n\r\t"]"#, 46 | r#"[102,1.02e1,2e-1,0,"helloȴ\\ 𝄞"]"#, 47 | r#"[100,1,0e0,2e0,"汉语 / 漢語; Hàn\b \tyǔ "]"#, 48 | "[2e-1,2e-2,0e0,2e-1,2e-1]", 49 | "[-102,-100,-0e0,-2e1]", 50 | "{}", 51 | r#"{"key1":"value1"}"#, 52 | r#"{"key1":"value1","key2":"value2"}"#, 53 | r#"{"a":1,"b":1,"c":1,"d":1,"e":1,"f":1,"x":1,"z":1}"#, 54 | ] 55 | --------------------------------------------------------------------------------