├── .gitignore ├── Cargo.toml ├── .vscode └── settings.json ├── .helix └── languages.toml ├── marked-yaml ├── CHANGELOG.md ├── examples │ └── everything.yaml ├── Cargo.toml ├── tests │ ├── character.rs │ └── serde.rs ├── README.md └── src │ ├── lib.rs │ ├── loader.rs │ ├── types.rs │ └── spanned_serde.rs ├── CODE-OF-CONDUCT.md ├── LICENSE-MIT ├── README.md ├── deny.toml ├── .github └── workflows │ └── basic.yaml └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | tarpaulin-report.html 3 | mutants.out* 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "marked-yaml", 4 | ] 5 | resolver = "2" -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.cargo.features": [ 3 | "serde", "serde-path" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.helix/languages.toml: -------------------------------------------------------------------------------- 1 | [language-server.rust-analyzer.config.cargo] 2 | features = [ "serde-path" ] 3 | [language-server.rust-analyzer.config.check] 4 | features = [ "serde-path" ] 5 | -------------------------------------------------------------------------------- /marked-yaml/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # marked-yaml changelog 2 | 3 | ## [Unreleased] 4 | 5 | ### Bug fixes 6 | 7 | * serde: Empty scalars can now be deserialized as a mapping, sequence or unit. 8 | -------------------------------------------------------------------------------- /CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of conduct 2 | 3 | This project operates under the Rust code of conduct which can be found at 4 | 5 | 6 | The current recognised version of that is found at: 7 | 8 | -------------------------------------------------------------------------------- /marked-yaml/examples/everything.yaml: -------------------------------------------------------------------------------- 1 | # This is an example document which exercises all of the properties of 2 | # the marked YAML parser. It covers scalars, mappings, and sequences, and 3 | # verifies that everything which *ought* to be supported is. 4 | 5 | simple: scalar 6 | boolean1: "true" 7 | boolean2: false 8 | integer: "1234" 9 | float: 12.34 10 | nullvalue: null 11 | 12 | mapping: 13 | nesting: is 14 | quite: 15 | quite: possible 16 | 17 | plainsequence: 18 | - simple 19 | - values 20 | - are 21 | - supported 22 | 23 | heterogenous: 24 | - multiple 25 | - types: of 26 | - [values, are supported] 27 | - { such: as, these: values } 28 | -------------------------------------------------------------------------------- /marked-yaml/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "marked-yaml" 3 | version = "0.8.0" 4 | authors = ["Daniel Silverstone "] 5 | edition = "2021" 6 | description = "A simplified YAML structure with provenance spans" 7 | homepage = "https://github.com/kinnison/marked-yaml/" 8 | repository = "https://github.com/kinnison/marked-yaml.git" 9 | readme = "README.md" 10 | keywords = ["yaml", "provenance"] 11 | categories = ["data-structures", "encoding", "parsing"] 12 | license = "MIT" 13 | 14 | [badges] 15 | maintenance = { status = "experimental" } 16 | 17 | [features] 18 | default = [] 19 | serde = ["dep:serde"] 20 | serde-path = ["serde", "dep:serde_path_to_error"] 21 | 22 | [dependencies] 23 | doc-comment = "0.3" 24 | yaml-rust = { version = "0.10.2", package = "yaml-rust2" } 25 | hashlink = "0.10.0" 26 | serde = { version = "1.0.194", optional = true, features = ["derive"] } 27 | serde_path_to_error = { version = "0.1.16", optional = true } 28 | 29 | [package.metadata.docs.rs] 30 | all-features = true 31 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Daniel Silverstone 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Marked data 2 | 3 | This is a collection of libraries which offer data provenance facilities and 4 | then use that to provide a variety of marked data structures. 5 | 6 | The data marks are intended to be similar to, though not entirely the same as 7 | data structures seen in other systems as `Span` or similar. Instead data marks 8 | are a combination of that span-like data and also file sources. The intent here 9 | being that combining data from multiple sources is a safe and sensible thing to 10 | do and that after doing so, it is important to be able to discover where something 11 | came from. 12 | 13 | Currently we provide: 14 | 15 | * `marked-yaml`: A deliberately simplified YAML subset which supports data marking 16 | 17 | ## Marked YAML 18 | 19 | The `marked-yaml` library offers a subset of YAML designed for configuration files 20 | and similar use. The data it reads in is "marked" with its origin. If you want 21 | to use `marked-yaml` with your existing `serde` applications, you can enable the 22 | `serde` feature, and if you want the errors produced by the `marked-yaml` 23 | deserializer to include nice paths to any problem, along with ensuring the marker 24 | for the problem area is populated in any errors, use the `serde-path` feature. 25 | -------------------------------------------------------------------------------- /marked-yaml/tests/character.rs: -------------------------------------------------------------------------------- 1 | //! All the tests in here relate to the ability to get character offsets for things. 2 | 3 | #[test] 4 | fn fix_24_character_offset_works() { 5 | let input = r#"# some comment 6 | 7 | key: value # another comment 8 | "# 9 | .to_string(); 10 | 11 | let document = marked_yaml::parse_yaml(0, &input).unwrap(); 12 | 13 | // With the above shape, we know the document is a mapping, so let's retrieve 14 | // the "key" value from it 15 | 16 | let node = document.as_mapping().unwrap().get_scalar("key").unwrap(); 17 | let span = node.span(); 18 | eprintln!("{span:?}"); 19 | eprintln!("{node:?}"); 20 | let (start, end) = ( 21 | span.start().unwrap().character(), 22 | // Because span.end() isn't reliable for scalar nodes right now, 23 | // if there's no end marker, try and synthesise it from the node's value. 24 | // This is unreliable because the node might have been folded, or had escaped 25 | // characters in it which we won't notice. 26 | span.end() 27 | .map(marked_yaml::Marker::character) 28 | .unwrap_or_else(|| span.start().unwrap().character() + node.as_str().chars().count()), 29 | ); 30 | 31 | let output = input 32 | .chars() 33 | .take(start) 34 | .chain("new_value".chars()) 35 | .chain(input.chars().skip(end)) 36 | .collect::(); 37 | 38 | let expected_output = r#"# some comment 39 | 40 | key: new_value # another comment 41 | "#; 42 | 43 | assert_eq!(output, expected_output); 44 | } 45 | -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | [graph] 2 | all-features = true 3 | 4 | [licenses] 5 | # List of explicitly allowed licenses 6 | # See https://spdx.org/licenses/ for list of possible licenses 7 | # [possible values: any SPDX 3.11 short identifier (+ optional exception)]. 8 | allow = ["MIT", "Apache-2.0", "Unicode-DFS-2016", "BSD-3-Clause"] 9 | # The confidence threshold for detecting a license from license text. 10 | # The higher the value, the more closely the license text must be to the 11 | # canonical license text of a valid SPDX license file. 12 | # [possible values: any between 0.0 and 1.0]. 13 | confidence-threshold = 0.8 14 | # Allow 1 or more licenses on a per-crate basis, so that particular licenses 15 | # aren't accepted for every possible crate as with the normal allow list 16 | exceptions = [ 17 | # Each entry is the crate and version constraint, and its specific allow 18 | # list 19 | #{ allow = ["Zlib"], crate = "adler32" }, 20 | ] 21 | 22 | # Some crates don't have (easily) machine readable licensing information, 23 | # adding a clarification entry for it allows you to manually specify the 24 | # licensing information 25 | #[[licenses.clarify]] 26 | # The package spec the clarification applies to 27 | #crate = "ring" 28 | # The SPDX expression for the license requirements of the crate 29 | #expression = "MIT AND ISC AND OpenSSL" 30 | # One or more files in the crate's source used as the "source of truth" for 31 | # the license expression. If the contents match, the clarification will be used 32 | # when running the license check, otherwise the clarification will be ignored 33 | # and the crate will be checked normally, which may produce warnings or errors 34 | # depending on the rest of your configuration 35 | #license-files = [ 36 | # Each entry is a crate relative path, and the (opaque) hash of its contents 37 | #{ path = "LICENSE", hash = 0xbd0eed23 } 38 | #] 39 | -------------------------------------------------------------------------------- /marked-yaml/README.md: -------------------------------------------------------------------------------- 1 | # Marked YAML 2 | 3 | This library builds atop [`yaml-rust2`][yaml-rust2] to provide a YAML AST which 4 | includes the marks for where the YAML data comes from. It explicitly operates 5 | at a low level, providing only the _base_ **safe** YAML types (i.e. the vanilla 6 | tags `tag:yaml.org,2002:seq`, `tag:yaml.org,2002:map`, and `tag:yaml.org,2002:str`) 7 | 8 | [yaml-rust2]: https://crates.io/crates/yaml-rust2 9 | 10 | The subset of YAML which is supported is quite deliberately limited in order 11 | that users of this crate will implicitly discourage complex use of YAML which 12 | is harder to manage user expectations with. As an example, the mapping type 13 | in this crate explicitly only permits scalars as keys, and since all scalars 14 | are treated as strings, mappings always have string keys. 15 | 16 | The primary value of this kind of representation of YAML data is to allow 17 | applications which want to be very explicit about where input came from an 18 | opportunity to do this in a way which normal YAML parsers do not allow. 19 | 20 | # Using Marked YAML 21 | 22 | Currently this library only supports loading YAML from strings, 23 | but this is sufficient for most users' purposes. We would not 24 | recommend an un-streamed processing engine for massive data anyway. 25 | 26 | To load some YAML you simply need to: 27 | 28 | ```rust 29 | let node = marked_yaml::parse_yaml(0, r#" 30 | toplevel: must be a mapping 31 | but: 32 | - it 33 | - may 34 | - contain lists 35 | and: 36 | mappings: are permitted 37 | as: sub-mappings 38 | "#); 39 | assert!(node.is_ok()); 40 | ``` 41 | 42 | Parsing a valid YAML file may fail because `marked_yaml` adds some 43 | additional constraints: 44 | 45 | - The top level of the YAML **MUST** be one of a mapping or a sequence. This is 46 | controlled by the loader options. 47 | - Mapping keys **MUST** be scalars (strings). 48 | - Aliases and anchors **MAY NOT** be used (though this limit may be lifted in the future). 49 | 50 | In addition, you can convert between `marked_yaml::Node` and `yaml_rust::Yaml` 51 | though doing so will not give you any useful markers. 52 | 53 | # Using `serde` with `marked_yaml` 54 | 55 | If you want to use `marked-yaml` with your existing `serde` applications, you 56 | can enable the `serde-path` feature. 57 | 58 | If you do not need the errors produced by the `marked-yaml` deserializer to 59 | include nice paths to any problem, along with ensuring the marker for the 60 | problem area is populated in any errors, use the simpler `serde` feature. 61 | -------------------------------------------------------------------------------- /marked-yaml/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Marked YAML 2 | //! =========== 3 | //! 4 | //! Currently this library only supports parsing YAML from strings, 5 | //! but this is sufficient for most users' purposes. We would not 6 | //! recommend an un-streamed processing engine for massive data anyway. 7 | //! 8 | //! To parse some YAML you simply need to: 9 | //! 10 | //! ``` 11 | //! let node = marked_yaml::parse_yaml(0, r#" 12 | //! toplevel: must be a mapping 13 | //! but: 14 | //! - it 15 | //! - may 16 | //! - contain lists 17 | //! and: 18 | //! mappings: are permitted 19 | //! as: sub-mappings 20 | //! "#); 21 | //! assert!(node.is_ok()); 22 | //! ``` 23 | //! 24 | //! Parsing a valid YAML string may fail because `marked_yaml` adds some 25 | //! additional constraints: 26 | //! 27 | //! * The top level of the YAML **MUST** be a mapping or a sequence. 28 | //! * Mapping keys **MUST** be scalars (strings). 29 | //! * Aliases and anchors **MAY NOT** be used (though this limit may be lifted in the future). 30 | //! 31 | //! In addition, you can convert between `marked_yaml::Node` and `yaml_rust::Yaml` 32 | //! though doing so will not give you any useful markers. 33 | #![cfg_attr( 34 | feature = "serde", 35 | doc = r#" 36 | ## Serde 37 | 38 | Should you so choose, you may use serde to deserialise YAML 39 | strings directly into structures, any amount of which could be annotated 40 | with the [`Spanned`] type to capture information about where the value came 41 | from in the input. 42 | 43 | ``` 44 | # use marked_yaml::{from_yaml, Marker, Spanned}; 45 | # use std::collections::HashMap; 46 | let YAML = "Daniel: Author\nUser: Not Author\n"; 47 | let roles: HashMap, Spanned> = from_yaml(0, YAML).unwrap(); 48 | 49 | assert_eq!(roles["Daniel"], "Author"); 50 | assert_eq!(roles["User"].span().start().copied(), Some(Marker::new(0, 21, 2, 7))); 51 | ``` 52 | 53 | You do not have to have all values [`Spanned`], and you can deserialize from an already 54 | parsed set of nodes with [`from_node`] instead. 55 | 56 | Empty scalars can be deserialized to empty sequences, maps, the unit type `()` 57 | and structs with a `#[serde(default)]` attribute. 58 | 59 | ### Error spans and paths 60 | 61 | If you want to have the error spans populated, along with string-form paths to 62 | errors in your YAML, then you can use the `serde-path` feature. 63 | "# 64 | )] 65 | #![deny(missing_docs)] 66 | #![cfg_attr(docsrs, feature(doc_cfg))] 67 | 68 | pub mod loader; 69 | pub mod types; 70 | 71 | #[doc(inline)] 72 | pub use loader::{parse_yaml, parse_yaml_with_options, LoadError, LoaderOptions}; 73 | #[doc(inline)] 74 | pub use types::{Marker, Node, Span}; 75 | 76 | #[cfg(feature = "serde")] 77 | #[doc(hidden)] 78 | pub mod spanned_serde; 79 | 80 | #[cfg(feature = "serde")] 81 | #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] 82 | #[doc(inline)] 83 | pub use spanned_serde::{ 84 | from_node, from_yaml, from_yaml_with_options, Error, FromNodeError, FromYamlError, Spanned, 85 | }; 86 | -------------------------------------------------------------------------------- /.github/workflows/basic.yaml: -------------------------------------------------------------------------------- 1 | name: Basic 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "*" 7 | push: 8 | branches: 9 | - "*" 10 | 11 | jobs: 12 | build: 13 | name: Build 14 | runs-on: ${{ matrix.platform }} 15 | strategy: 16 | fail-fast: true 17 | matrix: 18 | platform: 19 | - ubuntu-latest 20 | # - macos-latest 21 | # - windows-latest 22 | toolchain: 23 | - stable 24 | - beta 25 | - nightly 26 | include: 27 | - platform: ubuntu-latest 28 | toolchain: beta 29 | clippyetc: true 30 | steps: 31 | - uses: actions/checkout@v2 32 | - uses: actions-rs/toolchain@v1 33 | name: Acquire minimal Rust ${{ matrix.toolchain }} 34 | with: 35 | profile: minimal 36 | toolchain: ${{ matrix.toolchain }} 37 | override: true 38 | if: "!matrix.clippyetc" 39 | - uses: actions-rs/toolchain@v1 40 | name: Acquire minimal Rust ${{ matrix.toolchain }} with clippy and rustfmt 41 | with: 42 | profile: minimal 43 | toolchain: ${{ matrix.toolchain }} 44 | override: true 45 | components: clippy, rustfmt 46 | if: matrix.clippyetc 47 | - name: Determine the compiler identity 48 | shell: bash 49 | id: rustc 50 | run: | 51 | echo "commit_hash=$(rustc -vV | grep commit-hash | cut -b14-23)" >> $GITHUB_OUTPUT 52 | - name: Cache cargo registry 53 | uses: actions/cache@v4 54 | with: 55 | path: ~/.cargo/registry 56 | key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} 57 | - name: Cache cargo git trees 58 | uses: actions/cache@v4 59 | with: 60 | path: ~/.cargo/git 61 | key: ${{ runner.os }}-cargo-gits-${{ hashFiles('**/Cargo.lock') }} 62 | - name: Cache cargo build 63 | uses: actions/cache@v4 64 | with: 65 | path: target 66 | key: ${{ runner.os }}-cargo-build-${{ matrix.target }}-${{ steps.rustc.outputs.commit_hash }}-${{ hashFiles('**/Cargo.lock') }} 67 | - uses: actions-rs/cargo@v1 68 | name: Build the library in debug mode 69 | with: 70 | command: build 71 | args: --all --all-features 72 | - uses: actions-rs/cargo@v1 73 | name: Run the tests 74 | with: 75 | command: test 76 | args: --all --all-features 77 | - uses: actions-rs/cargo@v1 78 | name: Run clippy 79 | with: 80 | command: clippy 81 | args: --all --all-features 82 | if: matrix.clippyetc 83 | - uses: actions-rs/cargo@v1 84 | name: Check formatting 85 | with: 86 | command: fmt 87 | args: --all -- --check 88 | if: matrix.clippyetc 89 | - name: Clear the cargo caches 90 | run: | 91 | cargo install cargo-cache --no-default-features --features ci-autoclean 92 | cargo-cache 93 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "arraydeque" 7 | version = "0.5.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" 10 | 11 | [[package]] 12 | name = "cfg-if" 13 | version = "1.0.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 16 | 17 | [[package]] 18 | name = "doc-comment" 19 | version = "0.3.3" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" 22 | 23 | [[package]] 24 | name = "encoding_rs" 25 | version = "0.8.35" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 28 | dependencies = [ 29 | "cfg-if", 30 | ] 31 | 32 | [[package]] 33 | name = "foldhash" 34 | version = "0.1.5" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 37 | 38 | [[package]] 39 | name = "hashbrown" 40 | version = "0.15.3" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" 43 | dependencies = [ 44 | "foldhash", 45 | ] 46 | 47 | [[package]] 48 | name = "hashlink" 49 | version = "0.10.0" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" 52 | dependencies = [ 53 | "hashbrown", 54 | ] 55 | 56 | [[package]] 57 | name = "itoa" 58 | version = "1.0.15" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 61 | 62 | [[package]] 63 | name = "marked-yaml" 64 | version = "0.8.0" 65 | dependencies = [ 66 | "doc-comment", 67 | "hashlink", 68 | "serde", 69 | "serde_path_to_error", 70 | "yaml-rust2", 71 | ] 72 | 73 | [[package]] 74 | name = "proc-macro2" 75 | version = "1.0.95" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 78 | dependencies = [ 79 | "unicode-ident", 80 | ] 81 | 82 | [[package]] 83 | name = "quote" 84 | version = "1.0.40" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 87 | dependencies = [ 88 | "proc-macro2", 89 | ] 90 | 91 | [[package]] 92 | name = "serde" 93 | version = "1.0.219" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 96 | dependencies = [ 97 | "serde_derive", 98 | ] 99 | 100 | [[package]] 101 | name = "serde_derive" 102 | version = "1.0.219" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 105 | dependencies = [ 106 | "proc-macro2", 107 | "quote", 108 | "syn", 109 | ] 110 | 111 | [[package]] 112 | name = "serde_path_to_error" 113 | version = "0.1.17" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" 116 | dependencies = [ 117 | "itoa", 118 | "serde", 119 | ] 120 | 121 | [[package]] 122 | name = "syn" 123 | version = "2.0.101" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" 126 | dependencies = [ 127 | "proc-macro2", 128 | "quote", 129 | "unicode-ident", 130 | ] 131 | 132 | [[package]] 133 | name = "unicode-ident" 134 | version = "1.0.18" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 137 | 138 | [[package]] 139 | name = "yaml-rust2" 140 | version = "0.10.2" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "18b783b2c2789414f8bb84ca3318fc9c2d7e7be1c22907d37839a58dedb369d3" 143 | dependencies = [ 144 | "arraydeque", 145 | "encoding_rs", 146 | "hashlink", 147 | ] 148 | -------------------------------------------------------------------------------- /marked-yaml/tests/serde.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "serde")] 2 | //! All these tests require serde 3 | //! 4 | 5 | use std::collections::HashMap; 6 | 7 | use marked_yaml::{ 8 | from_node, from_yaml, from_yaml_with_options, parse_yaml, FromYamlError, LoaderOptions, Span, 9 | Spanned, 10 | }; 11 | use serde::Deserialize; 12 | 13 | const TEST_DOC: &str = r#"# Line one is a comment 14 | top: 15 | - level 16 | - is always 17 | - two 18 | - strings 19 | u8s: [ 0, 1, 2, "255" ] 20 | i8s: [ -128, 0, 127 ] 21 | u32s: [ 65537 ] 22 | thingy: blue 23 | outcome: 24 | bad: stuff 25 | looksee: { ugly: [ first, second ] } 26 | known: { unknown: { name: Jeff, age: 14 } } 27 | kvs: 28 | first: one 29 | second: two 30 | third: banana 31 | falsy: false 32 | truthy: true 33 | yes: true 34 | "#; 35 | 36 | #[derive(Debug, Deserialize)] 37 | #[allow(dead_code)] 38 | struct FullTest { 39 | top: Vec>, 40 | u8s: Spanned>, 41 | i8s: Vec, 42 | u32s: Vec, 43 | missing: Option, 44 | thingy: Colour, 45 | outcome: EnumCheck, 46 | looksee: EnumCheck, 47 | known: EnumCheck, 48 | kvs: HashMap, Spanned>, 49 | falsy: Spanned, 50 | truthy: Spanned, 51 | yes: Spanned, 52 | } 53 | 54 | #[derive(Deserialize, Debug)] 55 | #[allow(dead_code)] 56 | enum EnumCheck { 57 | #[serde(alias = "good")] 58 | Good(String), 59 | #[serde(alias = "bad")] 60 | Bad(Spanned), 61 | #[serde(alias = "ugly")] 62 | Ugly(String, String), 63 | #[serde(alias = "unknown")] 64 | Unknown { name: Spanned, age: i64 }, 65 | } 66 | 67 | #[derive(Deserialize, Debug)] 68 | enum Colour { 69 | #[serde(alias = "blue")] 70 | Blue, 71 | #[serde(alias = "red")] 72 | Red, 73 | } 74 | 75 | #[test] 76 | fn read_everything() { 77 | let nodes = parse_yaml(0, TEST_DOC).unwrap(); 78 | let doc: FullTest = from_node(&nodes).unwrap(); 79 | println!("{doc:?}"); 80 | assert_eq!(doc.top[0].as_str(), "level"); 81 | assert!(doc.falsy == false); 82 | assert!(doc.truthy == true); 83 | assert_ne!(doc.truthy, doc.falsy); 84 | assert_eq!(doc.truthy, doc.yes); 85 | assert_eq!(doc.top[2], "two"); 86 | assert_ne!(doc.top[3], "two"); 87 | let s = String::from("s"); 88 | assert!(doc.top[0] != s); 89 | } 90 | 91 | #[test] 92 | fn ergonomics() { 93 | let doc: FullTest = from_yaml(0, TEST_DOC).unwrap(); 94 | assert_eq!(doc.kvs.get("first").map(|s| s.as_str()), Some("one")); 95 | let k1 = Spanned::new(Span::new_blank(), "k1"); 96 | let mut map = HashMap::new(); 97 | map.insert(k1, "v1"); 98 | let k2 = Spanned::new(Span::new_blank(), "k2"); 99 | assert!(!map.contains_key(&k2)); 100 | assert!(map.contains_key("k1")); 101 | } 102 | 103 | #[test] 104 | fn parse_fails() { 105 | let err = from_yaml::(0, "hello world").err().unwrap(); 106 | assert!(matches!(err, FromYamlError::ParseYaml(_))); 107 | let err = from_yaml::(0, "hello: world").err().unwrap(); 108 | assert!(matches!(err, FromYamlError::FromNode(_))); 109 | let s = format!("{err}"); 110 | assert!(s.starts_with("missing field")); 111 | #[derive(Deserialize)] 112 | #[allow(dead_code)] 113 | struct MiniDoc { 114 | colour: Colour, 115 | } 116 | let err = from_yaml::(0, "colour: {Red: optional}") 117 | .err() 118 | .unwrap(); 119 | let s = format! {"{err}"}; 120 | #[cfg(feature = "serde-path")] 121 | assert_eq!(s, "colour.Red: invalid type: map, expected String"); 122 | #[cfg(not(feature = "serde-path"))] 123 | assert_eq!(s, "invalid type: map, expected String"); 124 | } 125 | 126 | #[test] 127 | fn parse_fails_coerce() { 128 | let options = LoaderOptions::default().prevent_coercion(true); 129 | let err = from_yaml_with_options::(0, TEST_DOC, options) 130 | .err() 131 | .unwrap(); 132 | let s = format!("{err}"); 133 | #[cfg(feature = "serde-path")] 134 | assert!(s.starts_with("u8s[3]")); 135 | #[cfg(feature = "serde-path")] 136 | let s = s.strip_prefix("u8s[3]: ").unwrap(); 137 | assert!(s.starts_with("invalid type: string")); 138 | assert!(s.contains("expected u8")); 139 | } 140 | 141 | #[test] 142 | fn empty_scalar_map() { 143 | #[allow(dead_code)] 144 | #[derive(Debug, Deserialize)] 145 | struct Foo { 146 | foo: HashMap, 147 | } 148 | let _: Foo = from_yaml(0, "foo:").unwrap(); 149 | } 150 | 151 | #[test] 152 | fn empty_scalar_seq() { 153 | #[allow(dead_code)] 154 | #[derive(Debug, Deserialize)] 155 | struct Foo { 156 | foo: Vec, 157 | } 158 | let _: Foo = from_yaml(0, "foo:").unwrap(); 159 | } 160 | 161 | #[test] 162 | fn empty_scalar_unit() { 163 | #[allow(dead_code)] 164 | #[derive(Debug, Deserialize)] 165 | struct Foo { 166 | foo: (), 167 | } 168 | let _: Foo = from_yaml(0, "foo:").unwrap(); 169 | } 170 | 171 | #[test] 172 | fn empty_scalar_struct_with_default() { 173 | #[allow(dead_code)] 174 | #[derive(Default, Debug, Deserialize)] 175 | struct Bar { 176 | buz: Option, 177 | } 178 | 179 | #[allow(dead_code)] 180 | #[derive(Debug, Deserialize)] 181 | struct Foo { 182 | #[serde(default)] 183 | bar: Bar, 184 | } 185 | let _: Foo = from_yaml(0, "bar:").unwrap(); 186 | } 187 | -------------------------------------------------------------------------------- /marked-yaml/src/loader.rs: -------------------------------------------------------------------------------- 1 | //! Loading YAML 2 | //! 3 | 4 | use crate::types::*; 5 | 6 | use hashlink::linked_hash_map::Entry; 7 | use yaml_rust::parser::{Event, MarkedEventReceiver, Parser}; 8 | use yaml_rust::scanner::ScanError; 9 | use yaml_rust::scanner::{Marker as YamlMarker, TScalarStyle}; 10 | 11 | use std::error::Error; 12 | use std::fmt::{self, Display}; 13 | 14 | /// An error indicating that a duplicate key was detected in a mapping 15 | #[derive(Debug, PartialEq, Eq)] 16 | 17 | pub struct DuplicateKeyInner { 18 | /// The first key 19 | pub prev_key: MarkedScalarNode, 20 | /// The second key 21 | pub key: MarkedScalarNode, 22 | } 23 | 24 | /// Errors which can occur during loading of YAML 25 | #[derive(Debug, PartialEq, Eq)] 26 | pub enum LoadError { 27 | /// Something other than a mapping detected at the top level 28 | TopLevelMustBeMapping(Marker), 29 | /// Something other than a sequence detected at the top level 30 | TopLevelMustBeSequence(Marker), 31 | /// Unexpected definition of anchor 32 | UnexpectedAnchor(Marker), 33 | /// Mapping keys must be scalars 34 | MappingKeyMustBeScalar(Marker), 35 | /// An explicit tag was detected 36 | UnexpectedTag(Marker), 37 | /// A YAML scanner error occured 38 | ScanError(Marker, ScanError), 39 | /// A duplicate key was detected in a mapping 40 | DuplicateKey(Box), 41 | } 42 | 43 | /// Options for loading YAML 44 | /// 45 | /// Default options ([`LoaderOptions::default()`]) are: 46 | /// 47 | /// - Permit duplicate keys 48 | /// 49 | #[derive(Debug)] 50 | pub struct LoaderOptions { 51 | error_on_duplicate_keys: bool, 52 | prevent_coercion: bool, 53 | toplevel_is_mapping: bool, 54 | lowercase_keys: bool, 55 | } 56 | 57 | impl Default for LoaderOptions { 58 | fn default() -> Self { 59 | Self { 60 | error_on_duplicate_keys: false, 61 | prevent_coercion: false, 62 | toplevel_is_mapping: true, 63 | lowercase_keys: false, 64 | } 65 | } 66 | } 67 | 68 | impl LoaderOptions { 69 | /// Enable errors on duplicate keys 70 | /// 71 | /// If enabled, duplicate keys in mappings will cause an error. 72 | /// If disabled, the last key/value pair will be used. 73 | pub fn error_on_duplicate_keys(self, enable: bool) -> Self { 74 | Self { 75 | error_on_duplicate_keys: enable, 76 | ..self 77 | } 78 | } 79 | 80 | /// Prevent coercion of scalar nodes 81 | /// 82 | /// If you want to disable things like [`.as_bool()`](crate::types::MarkedScalarNode::as_bool()) 83 | /// then you can call this and set coercion to be prevented. 84 | pub fn prevent_coercion(self, prevent: bool) -> Self { 85 | Self { 86 | prevent_coercion: prevent, 87 | ..self 88 | } 89 | } 90 | 91 | /// Require that the top level is a mapping node 92 | /// 93 | /// This is the default, but you can call this to be explicit. 94 | pub fn toplevel_mapping(self) -> Self { 95 | Self { 96 | toplevel_is_mapping: true, 97 | ..self 98 | } 99 | } 100 | 101 | /// Require that the top level is a sequence node 102 | /// 103 | /// Without calling this, the top level of the YAML is must be a mapping node 104 | pub fn toplevel_sequence(self) -> Self { 105 | Self { 106 | toplevel_is_mapping: false, 107 | ..self 108 | } 109 | } 110 | 111 | /// Whether or not to force-lowercase mapping keys when loading 112 | /// 113 | /// By default, the loader will leave key names alone, but in some 114 | /// cases it can be preferable to normalise them to lowercase 115 | pub fn lowercase_keys(self, force_lowercase: bool) -> Self { 116 | Self { 117 | lowercase_keys: force_lowercase, 118 | ..self 119 | } 120 | } 121 | } 122 | 123 | impl Display for LoadError { 124 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 125 | use LoadError::*; 126 | #[allow(deprecated)] 127 | match self { 128 | TopLevelMustBeMapping(m) => write!(f, "{}: Top level must be a mapping", m), 129 | TopLevelMustBeSequence(m) => write!(f, "{}: Top level must be a sequence", m), 130 | UnexpectedAnchor(m) => write!(f, "{}: Unexpected definition of anchor", m), 131 | MappingKeyMustBeScalar(m) => write!(f, "{}: Keys in mappings must be scalar", m), 132 | UnexpectedTag(m) => write!(f, "{}: Unexpected use of YAML tag", m), 133 | DuplicateKey(inner) => { 134 | let DuplicateKeyInner { prev_key, key } = inner.as_ref(); 135 | write!( 136 | f, 137 | "Duplicate key \"{}\" in mapping at {} and {}", 138 | prev_key.as_str(), 139 | prev_key 140 | .span() 141 | .start() 142 | .map(ToString::to_string) 143 | .unwrap_or_else(|| "?".to_string()), 144 | key.span() 145 | .start() 146 | .map(ToString::to_string) 147 | .unwrap_or_else(|| "?".to_string()), 148 | ) 149 | } 150 | ScanError(m, e) => { 151 | // e.description() is deprecated but it's the only way to get 152 | // the exact info we want out of yaml-rust 153 | write!(f, "{}: {}", m, e.description()) 154 | } 155 | } 156 | } 157 | } 158 | 159 | impl Error for LoadError {} 160 | 161 | #[derive(Debug, PartialEq, Eq)] 162 | enum LoaderState { 163 | Initial, 164 | StartStream, 165 | StartDocument, 166 | MappingWaitingOnKey(Marker, MappingHash), 167 | MappingWaitingOnValue(Marker, MappingHash, MarkedScalarNode), 168 | SequenceWaitingOnValue(Marker, Vec), 169 | Finished(Node), 170 | Error(LoadError), 171 | } 172 | use LoaderState::*; 173 | 174 | impl LoaderState { 175 | fn is_error(&self) -> bool { 176 | matches!(self, Error(_)) 177 | } 178 | } 179 | 180 | struct MarkedLoader { 181 | source: usize, 182 | state_stack: Vec, 183 | options: LoaderOptions, 184 | } 185 | 186 | impl MarkedEventReceiver for MarkedLoader { 187 | fn on_event(&mut self, ev: Event, mark: YamlMarker) { 188 | // Short-circuit if the state stack is in error 189 | if self.state_stack[self.state_stack.len() - 1].is_error() { 190 | return; 191 | } 192 | let mark = self.marker(mark); 193 | let curstate = self 194 | .state_stack 195 | .pop() 196 | .expect("State stack became unbalanced"); 197 | let newstate = match ev { 198 | Event::Alias(_) => unreachable!(), 199 | Event::StreamStart => { 200 | assert_eq!(curstate, Initial); 201 | StartStream 202 | } 203 | Event::DocumentStart => { 204 | assert_eq!(curstate, StartStream); 205 | StartDocument 206 | } 207 | Event::MappingStart(aid, tag) => { 208 | if tag.is_some() { 209 | Error(LoadError::UnexpectedTag(mark)) 210 | } else if aid == 0 { 211 | match curstate { 212 | StartDocument => { 213 | if self.options.toplevel_is_mapping { 214 | MappingWaitingOnKey(mark, MappingHash::new()) 215 | } else { 216 | Error(LoadError::TopLevelMustBeSequence(mark)) 217 | } 218 | } 219 | MappingWaitingOnKey(_, _) => Error(LoadError::MappingKeyMustBeScalar(mark)), 220 | MappingWaitingOnValue(_, _, _) => { 221 | self.state_stack.push(curstate); 222 | MappingWaitingOnKey(mark, MappingHash::new()) 223 | } 224 | SequenceWaitingOnValue(_, _) => { 225 | self.state_stack.push(curstate); 226 | MappingWaitingOnKey(mark, MappingHash::new()) 227 | } 228 | _ => unreachable!(), 229 | } 230 | } else { 231 | Error(LoadError::UnexpectedAnchor(mark)) 232 | } 233 | } 234 | Event::MappingEnd => match curstate { 235 | MappingWaitingOnKey(startmark, map) => { 236 | let span = Span::new_with_marks(startmark, mark); 237 | let node = Node::from(MarkedMappingNode::new(span, map)); 238 | if let Some(topstate) = self.state_stack.pop() { 239 | match topstate { 240 | MappingWaitingOnValue(mark, mut map, key) => { 241 | match map.entry(key.clone()) { 242 | Entry::Occupied(entry) 243 | if self.options.error_on_duplicate_keys => 244 | { 245 | Error(LoadError::DuplicateKey(Box::new( 246 | DuplicateKeyInner { 247 | prev_key: entry.key().clone(), 248 | key, 249 | }, 250 | ))) 251 | } 252 | _ => { 253 | map.insert(key, node); 254 | MappingWaitingOnKey(mark, map) 255 | } 256 | } 257 | } 258 | SequenceWaitingOnValue(mark, mut list) => { 259 | list.push(node); 260 | SequenceWaitingOnValue(mark, list) 261 | } 262 | _ => unreachable!(), 263 | } 264 | } else { 265 | Finished(node) 266 | } 267 | } 268 | _ => unreachable!(), 269 | }, 270 | Event::SequenceStart(aid, tag) => { 271 | if tag.is_some() { 272 | Error(LoadError::UnexpectedTag(mark)) 273 | } else if aid == 0 { 274 | match curstate { 275 | StartDocument => { 276 | if self.options.toplevel_is_mapping { 277 | Error(LoadError::TopLevelMustBeMapping(mark)) 278 | } else { 279 | SequenceWaitingOnValue(mark, Vec::new()) 280 | } 281 | } 282 | MappingWaitingOnKey(_, _) => Error(LoadError::MappingKeyMustBeScalar(mark)), 283 | mv @ MappingWaitingOnValue(_, _, _) => { 284 | self.state_stack.push(mv); 285 | SequenceWaitingOnValue(mark, Vec::new()) 286 | } 287 | sv @ SequenceWaitingOnValue(_, _) => { 288 | self.state_stack.push(sv); 289 | SequenceWaitingOnValue(mark, Vec::new()) 290 | } 291 | _ => unreachable!(), 292 | } 293 | } else { 294 | Error(LoadError::UnexpectedAnchor(mark)) 295 | } 296 | } 297 | Event::SequenceEnd => match curstate { 298 | SequenceWaitingOnValue(startmark, list) => { 299 | let span = Span::new_with_marks(startmark, mark); 300 | let node = Node::from(MarkedSequenceNode::new(span, list)); 301 | if let Some(topstate) = self.state_stack.pop() { 302 | match topstate { 303 | MappingWaitingOnValue(mark, mut map, key) => { 304 | match map.entry(key.clone()) { 305 | Entry::Occupied(entry) 306 | if self.options.error_on_duplicate_keys => 307 | { 308 | Error(LoadError::DuplicateKey(Box::new( 309 | DuplicateKeyInner { 310 | prev_key: entry.key().clone(), 311 | key, 312 | }, 313 | ))) 314 | } 315 | _ => { 316 | map.insert(key, node); 317 | MappingWaitingOnKey(mark, map) 318 | } 319 | } 320 | } 321 | SequenceWaitingOnValue(mark, mut list) => { 322 | list.push(node); 323 | SequenceWaitingOnValue(mark, list) 324 | } 325 | _ => unreachable!(), 326 | } 327 | } else { 328 | Finished(node) 329 | } 330 | } 331 | _ => unreachable!(), 332 | }, 333 | Event::DocumentEnd => match curstate { 334 | Finished(_) => curstate, 335 | _ => unreachable!(), 336 | }, 337 | Event::StreamEnd => match curstate { 338 | StartStream => Finished(Node::from(MarkedMappingNode::new_empty( 339 | Span::new_with_marks(mark, mark), 340 | ))), 341 | Finished(_) => curstate, 342 | _ => unreachable!(), 343 | }, 344 | Event::Scalar(val, kind, aid, tag) => { 345 | if aid == 0 { 346 | if tag.is_some() { 347 | Error(LoadError::UnexpectedTag(mark)) 348 | } else { 349 | let span = Span::new_start(mark); 350 | let val = if matches!(curstate, MappingWaitingOnKey(_, _)) 351 | && self.options.lowercase_keys 352 | { 353 | val.to_lowercase() 354 | } else { 355 | val 356 | }; 357 | let mut node = MarkedScalarNode::new(span, val); 358 | if self.options.prevent_coercion { 359 | node.set_coerce(matches!(kind, TScalarStyle::Plain)); 360 | } 361 | match curstate { 362 | MappingWaitingOnKey(mark, map) => { 363 | MappingWaitingOnValue(mark, map, node) 364 | } 365 | MappingWaitingOnValue(mark, mut map, key) => { 366 | match map.entry(key.clone()) { 367 | Entry::Occupied(entry) 368 | if self.options.error_on_duplicate_keys => 369 | { 370 | Error(LoadError::DuplicateKey(Box::new( 371 | DuplicateKeyInner { 372 | prev_key: entry.key().clone(), 373 | key, 374 | }, 375 | ))) 376 | } 377 | _ => { 378 | map.insert(key, Node::from(node)); 379 | MappingWaitingOnKey(mark, map) 380 | } 381 | } 382 | } 383 | SequenceWaitingOnValue(mark, mut list) => { 384 | list.push(Node::from(node)); 385 | SequenceWaitingOnValue(mark, list) 386 | } 387 | StartDocument => Error(LoadError::TopLevelMustBeMapping(mark)), 388 | _ => unreachable!(), 389 | } 390 | } 391 | } else { 392 | Error(LoadError::UnexpectedAnchor(mark)) 393 | } 394 | } 395 | Event::Nothing => unreachable!(), 396 | }; 397 | self.state_stack.push(newstate); 398 | } 399 | } 400 | 401 | impl MarkedLoader { 402 | fn new(source: usize, options: LoaderOptions) -> Self { 403 | Self { 404 | source, 405 | state_stack: vec![Initial], 406 | options, 407 | } 408 | } 409 | 410 | fn marker(&self, mark: YamlMarker) -> Marker { 411 | Marker::new(self.source, mark.index(), mark.line(), mark.col() + 1) 412 | } 413 | 414 | fn finish(mut self) -> Result { 415 | let top = self.state_stack.pop(); 416 | match top.expect("YAML parser state stack unexpectedly empty") { 417 | Finished(n) => Ok(n), 418 | Error(e) => Err(e), 419 | _ => unreachable!(), 420 | } 421 | } 422 | } 423 | 424 | /// Parse YAML from a string and return a Node representing 425 | /// the content. 426 | /// 427 | /// When parsing YAML, the source is stored into all markers which are 428 | /// in the node spans. This means that later if you only have a node, 429 | /// you can determine which source it came from without needing complex 430 | /// lifetimes to bind strings or other non-copy data to nodes. 431 | /// 432 | /// This function requires that the top level be a mapping, but the returned 433 | /// type here is the generic Node enumeration to make it potentially easier 434 | /// for callers to use. Regardless, it's always possible to treat the 435 | /// returned node as a mapping node without risk of panic. 436 | /// 437 | /// If you wish to load a sequence instead of a mapping, then you will 438 | /// need to use [`parse_yaml_with_options`] to request that. 439 | /// 440 | /// ``` 441 | /// # use marked_yaml::*; 442 | /// let node = parse_yaml(0, include_str!("../examples/everything.yaml")) 443 | /// .unwrap() 444 | /// .as_mapping() 445 | /// .unwrap(); 446 | /// ``` 447 | pub fn parse_yaml(source: usize, yaml: S) -> Result 448 | where 449 | S: AsRef, 450 | { 451 | let options = LoaderOptions::default(); 452 | 453 | parse_yaml_with_options(source, yaml, options) 454 | } 455 | 456 | /// Parse YAML from a string and return a Node representing 457 | /// the content. 458 | /// 459 | /// Takes an additional LoaderOptions struct to control the behavior of the loader. 460 | /// 461 | /// This is the way to parse a file with a top-level sequence instead of a mapping 462 | /// node. 463 | /// 464 | /// See `parse_yaml` for more information. 465 | pub fn parse_yaml_with_options( 466 | source: usize, 467 | yaml: S, 468 | options: LoaderOptions, 469 | ) -> Result 470 | where 471 | S: AsRef, 472 | { 473 | let mut loader = MarkedLoader::new(source, options); 474 | let mut parser = Parser::new(yaml.as_ref().chars()); 475 | parser.load(&mut loader, false).map_err(|se| { 476 | let mark = loader.marker(*se.marker()); 477 | LoadError::ScanError(mark, se) 478 | })?; 479 | loader.finish() 480 | } 481 | 482 | #[cfg(test)] 483 | mod test { 484 | use super::*; 485 | 486 | #[test] 487 | fn smoke_basics() { 488 | let node = parse_yaml(0, "{}").unwrap(); 489 | assert!(node.as_mapping().is_some()); 490 | } 491 | 492 | #[test] 493 | fn load_everything() { 494 | let node = parse_yaml(0, include_str!("../examples/everything.yaml")).unwrap(); 495 | let map = node.as_mapping().unwrap(); 496 | assert_eq!(map.get_scalar("simple").unwrap().as_str(), "scalar"); 497 | assert_eq!(map.get_scalar("boolean1").unwrap().as_bool(), Some(true)); 498 | assert_eq!(map.get_scalar("boolean2").unwrap().as_bool(), Some(false)); 499 | } 500 | 501 | #[test] 502 | fn prevent_coercion() { 503 | let node = parse_yaml_with_options( 504 | 0, 505 | include_str!("../examples/everything.yaml"), 506 | LoaderOptions::default().prevent_coercion(true), 507 | ) 508 | .unwrap(); 509 | let map = node.as_mapping().unwrap(); 510 | assert_eq!(map.get_scalar("simple").unwrap().as_str(), "scalar"); 511 | assert_eq!(map.get_scalar("boolean1").unwrap().as_str(), "true"); 512 | assert_eq!(map.get_scalar("boolean1").unwrap().as_bool(), None); 513 | assert_eq!(map.get_scalar("boolean2").unwrap().as_str(), "false"); 514 | assert_eq!(map.get_scalar("boolean2").unwrap().as_bool(), Some(false)); 515 | assert_eq!(map.get_scalar("integer").unwrap().as_str(), "1234"); 516 | assert_eq!(map.get_scalar("integer").unwrap().as_i32(), None); 517 | assert_eq!(map.get_scalar("float").unwrap().as_str(), "12.34"); 518 | assert_eq!(map.get_scalar("float").unwrap().as_f32(), Some(12.34)); 519 | } 520 | 521 | #[test] 522 | fn toplevel_is_empty() { 523 | let node = parse_yaml(0, "").unwrap(); 524 | let map = node.as_mapping().unwrap(); 525 | assert!(map.is_empty()); 526 | } 527 | 528 | #[test] 529 | fn toplevel_is_empty_inline() { 530 | let node = parse_yaml(0, "{}").unwrap(); 531 | let map = node.as_mapping().unwrap(); 532 | assert!(map.is_empty()); 533 | } 534 | 535 | #[test] 536 | fn toplevel_is_scalar() { 537 | let err = parse_yaml(0, "foo"); 538 | assert_eq!( 539 | err, 540 | Err(LoadError::TopLevelMustBeMapping(Marker::new(0, 0, 1, 1))) 541 | ); 542 | assert!(format!("{}", err.err().unwrap()).contains("1:1: ")); 543 | } 544 | 545 | #[test] 546 | fn toplevel_is_sequence() { 547 | assert_eq!( 548 | parse_yaml(0, "[]"), 549 | Err(LoadError::TopLevelMustBeMapping(Marker::new(0, 0, 1, 1))) 550 | ); 551 | } 552 | 553 | #[test] 554 | fn duplicate_key() { 555 | let err = parse_yaml_with_options( 556 | 0, 557 | "{foo: bar, foo: baz}", 558 | LoaderOptions::default().error_on_duplicate_keys(true), 559 | ); 560 | 561 | assert_eq!( 562 | err, 563 | Err(LoadError::DuplicateKey(Box::new(DuplicateKeyInner { 564 | prev_key: MarkedScalarNode::new(Span::new_start(Marker::new(0, 0, 1, 1)), "foo"), 565 | key: MarkedScalarNode::new(Span::new_start(Marker::new(0, 10, 1, 11)), "foo") 566 | }))) 567 | ); 568 | 569 | assert_eq!( 570 | format!("{}", err.err().unwrap()), 571 | "Duplicate key \"foo\" in mapping at 1:2 and 1:12" 572 | ); 573 | 574 | // Without error_on_duplicate_keys, the last key wins 575 | let node = parse_yaml(0, "{foo: bar, foo: baz}").unwrap(); 576 | let map = node.as_mapping().unwrap(); 577 | assert_eq!(map.get_scalar("foo").unwrap().as_str(), "baz"); 578 | } 579 | 580 | #[test] 581 | fn unexpected_anchor() { 582 | let err = parse_yaml(0, "&foo {}"); 583 | assert_eq!( 584 | err, 585 | Err(LoadError::UnexpectedAnchor(Marker::new(0, 5, 1, 6))) 586 | ); 587 | assert!(format!("{}", err.err().unwrap()).starts_with("1:6: ")); 588 | } 589 | 590 | #[test] 591 | fn unexpected_anchor2() { 592 | assert_eq!( 593 | parse_yaml(0, "{bar: &foo []}"), 594 | Err(LoadError::UnexpectedAnchor(Marker::new(0, 11, 1, 12))) 595 | ); 596 | } 597 | 598 | #[test] 599 | fn unexpected_anchor3() { 600 | assert_eq!( 601 | parse_yaml(0, "{bar: &foo susan}"), 602 | Err(LoadError::UnexpectedAnchor(Marker::new(0, 11, 1, 12))) 603 | ); 604 | } 605 | 606 | #[test] 607 | fn mapping_key_mapping() { 608 | let err = parse_yaml(0, "{? {} : {}}"); 609 | assert_eq!( 610 | err, 611 | Err(LoadError::MappingKeyMustBeScalar(Marker::new(0, 3, 1, 4))) 612 | ); 613 | assert!(format!("{}", err.err().unwrap()).starts_with("1:4: ")); 614 | } 615 | 616 | #[test] 617 | fn mapping_key_sequence() { 618 | assert_eq!( 619 | parse_yaml(0, "{? [] : {}}"), 620 | Err(LoadError::MappingKeyMustBeScalar(Marker::new(0, 3, 1, 4))) 621 | ); 622 | } 623 | 624 | #[test] 625 | fn unexpected_tag() { 626 | let err = parse_yaml(0, "{foo: !!str bar}"); 627 | assert_eq!( 628 | err, 629 | Err(LoadError::UnexpectedTag(Marker::new(0, 12, 1, 13))) 630 | ); 631 | assert!(format!("{}", err.err().unwrap()).starts_with("1:13: ")); 632 | } 633 | 634 | #[test] 635 | fn nested_mapping_key_mapping() { 636 | assert_eq!( 637 | parse_yaml(0, "{foo: {? [] : {}}}"), 638 | Err(LoadError::MappingKeyMustBeScalar(Marker::new(0, 9, 1, 10))) 639 | ); 640 | } 641 | 642 | #[test] 643 | fn malformed_yaml_for_scanerror() { 644 | let err = parse_yaml(0, "{"); 645 | assert!(err.is_err()); 646 | assert!(format!("{}", err.err().unwrap()).starts_with("2:1: ")); 647 | } 648 | 649 | #[test] 650 | fn toplevel_sequence_wanted() { 651 | let node = 652 | parse_yaml_with_options(0, "[yaml]", LoaderOptions::default().toplevel_sequence()) 653 | .unwrap(); 654 | assert!(node.as_sequence().is_some()); 655 | } 656 | 657 | #[test] 658 | fn toplevel_sequence_wanted_got_mapping() { 659 | assert_eq!( 660 | parse_yaml_with_options(0, "{}", LoaderOptions::default().toplevel_sequence()), 661 | Err(LoadError::TopLevelMustBeSequence(Marker::new(0, 0, 1, 1))) 662 | ); 663 | } 664 | 665 | #[test] 666 | fn lowercase_keys() { 667 | let node = parse_yaml_with_options( 668 | 0, 669 | "KEY: VALUE", 670 | LoaderOptions::default().lowercase_keys(false), 671 | ) 672 | .unwrap(); 673 | assert!(node.as_mapping().unwrap().contains_key("KEY")); 674 | assert!(!node.as_mapping().unwrap().contains_key("key")); 675 | 676 | let node = parse_yaml_with_options( 677 | 0, 678 | "KEY: VALUE", 679 | LoaderOptions::default().lowercase_keys(true), 680 | ) 681 | .unwrap(); 682 | assert!(!node.as_mapping().unwrap().contains_key("KEY")); 683 | assert!(node.as_mapping().unwrap().contains_key("key")); 684 | } 685 | } 686 | -------------------------------------------------------------------------------- /marked-yaml/src/types.rs: -------------------------------------------------------------------------------- 1 | //! Various basic types for YAML handling 2 | //! 3 | 4 | use doc_comment::doc_comment; 5 | use hashlink::LinkedHashMap; 6 | use std::borrow::{Borrow, Cow}; 7 | use std::fmt::{self, Display}; 8 | use std::hash::{Hash, Hasher}; 9 | use std::ops::{Deref, DerefMut}; 10 | use yaml_rust::Yaml as YamlNode; 11 | 12 | /// A displayable marker for a YAML node 13 | /// 14 | /// While `Marker` can be `Display`'d it doesn't understand what its source 15 | /// means. This struct is the result of asking a Marker to render itself. 16 | /// 17 | /// ``` 18 | /// # use marked_yaml::*; 19 | /// let filenames = vec!["examples/everything.yaml"]; 20 | /// let nodes: Vec<_> = filenames 21 | /// .iter() 22 | /// .enumerate() 23 | /// .map(|(i, name)| parse_yaml(i, std::fs::read_to_string(name).unwrap()).unwrap()) 24 | /// .collect(); 25 | /// for (n, node) in nodes.iter().enumerate() { 26 | /// let marker = node.span().start().unwrap(); 27 | /// let rendered = format!("{}", marker.render(|i| filenames[i])); 28 | /// assert!(rendered.starts_with(filenames[n])); 29 | /// } 30 | /// ``` 31 | pub struct RenderedMarker { 32 | source: D, 33 | line: usize, 34 | column: usize, 35 | } 36 | 37 | impl Display for RenderedMarker 38 | where 39 | D: Display, 40 | { 41 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 42 | self.source.fmt(f)?; 43 | write!(f, ":{}:{}", self.line, self.column) 44 | } 45 | } 46 | 47 | /// A marker for a YAML node 48 | /// 49 | /// This indicates where a node started or ended. 50 | /// 51 | /// ``` 52 | /// use marked_yaml::{parse_yaml, Marker}; 53 | /// let node = parse_yaml(100, "{foo: bar}").unwrap(); 54 | /// let map = node.as_mapping().unwrap(); 55 | /// let bar = map.get("foo").unwrap(); 56 | /// // the "bar" string started on line 1, column 7 of source ID 100. 57 | /// assert_eq!(bar.span().start(), Some(&Marker::new(100, 6, 1, 7))); 58 | /// ``` 59 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 60 | pub struct Marker { 61 | source: usize, 62 | character: usize, 63 | line: usize, 64 | column: usize, 65 | } 66 | 67 | impl Marker { 68 | /// Create a new Marker 69 | /// 70 | /// This will typically not be used because markers will come from 71 | /// parsing YAML, however it is provided for completeness and in case 72 | /// you need it for your own tests. 73 | /// 74 | /// ``` 75 | /// # use marked_yaml::Marker; 76 | /// let marker = Marker::new(0, 3, 1, 2); 77 | /// # assert_eq!(marker.source(), 0); 78 | /// # assert_eq!(marker.character(), 3); 79 | /// # assert_eq!(marker.line(), 1); 80 | /// # assert_eq!(marker.column(), 2); 81 | /// ``` 82 | pub fn new(source: usize, character: usize, line: usize, column: usize) -> Self { 83 | Self { 84 | source, 85 | character, 86 | line, 87 | column, 88 | } 89 | } 90 | 91 | /// The source index given for this marker. 92 | /// 93 | /// When parsing YAML, we record where nodes start (and often finish). 94 | /// This is the source index provided when parsing the YAML. Likely this 95 | /// refers to a vector of PathBuf recording where input came from, but that 96 | /// is entirely up to the user of this library crate. 97 | /// 98 | /// This is likely most useful to computers, not humans. 99 | /// 100 | /// ``` 101 | /// # use marked_yaml::Marker; 102 | /// # let marker = Marker::new(0, 3, 1, 2); 103 | /// assert_eq!(marker.source(), 0); 104 | /// ``` 105 | pub fn source(&self) -> usize { 106 | self.source 107 | } 108 | 109 | /// The character index at which this marker resides 110 | /// 111 | /// When parsing YAML, we record where nodes start (and often finish). 112 | /// This is the character index into the source text of where this 113 | /// marker resides. Character indices start with zero since they're 114 | /// meant for software rather than humans. 115 | /// 116 | /// ``` 117 | /// # use marked_yaml::Marker; 118 | /// # let marker = Marker::new(0, 3, 1, 2); 119 | /// assert_eq!(marker.character(), 3); 120 | /// ``` 121 | pub fn character(&self) -> usize { 122 | self.character 123 | } 124 | 125 | /// The line number on which this marker resides, 1-indexed 126 | /// 127 | /// When parsing YAML, we record where nodes start (and often finish). 128 | /// This is the line number of where this marker resides. Line numbers 129 | /// start with 1 to make them more useful to humans. 130 | /// 131 | /// ``` 132 | /// # use marked_yaml::Marker; 133 | /// # let marker = Marker::new(0, 3, 1, 2); 134 | /// assert_eq!(marker.line(), 1); 135 | /// ``` 136 | pub fn line(&self) -> usize { 137 | self.line 138 | } 139 | 140 | /// The column number at which this marker resides, 1-indexed 141 | /// 142 | /// When parsing YAML, we record where nodes start (and often finish). 143 | /// This is the column number of where this marker resides. Column numbers 144 | /// start with 1 to make them more useful to humans. 145 | /// 146 | /// ``` 147 | /// # use marked_yaml::Marker; 148 | /// # let marker = Marker::new(0, 3, 1, 2); 149 | /// assert_eq!(marker.column(), 2); 150 | /// ``` 151 | pub fn column(&self) -> usize { 152 | self.column 153 | } 154 | 155 | /// Render this marker 156 | /// 157 | /// Markers have a source identifier, typically as passed to `parse_yaml()` 158 | /// but have no way in and of themselves to turn that into a useful name. 159 | /// This function allows you to create a rendered marker which knows how 160 | /// to show the source name. 161 | /// 162 | /// ``` 163 | /// # use marked_yaml::Marker; 164 | /// # let marker = Marker::new(0, 3, 1, 2); 165 | /// let rendered = marker.render(|_| "name"); 166 | /// assert_eq!(format!("{}", rendered), "name:1:2") 167 | /// ``` 168 | pub fn render(self, renderfn: F) -> RenderedMarker 169 | where 170 | D: Display, 171 | F: FnOnce(usize) -> D, 172 | { 173 | RenderedMarker { 174 | source: renderfn(self.source), 175 | line: self.line, 176 | column: self.column, 177 | } 178 | } 179 | 180 | /// Set the source index for this marker 181 | /// 182 | /// ``` 183 | /// # use marked_yaml::Marker; 184 | /// # let mut marker = Marker::new(0, 0, 0, 0); 185 | /// assert_ne!(marker.source(), 1); 186 | /// marker.set_source(1); 187 | /// assert_eq!(marker.source(), 1); 188 | /// ``` 189 | pub fn set_source(&mut self, source: usize) { 190 | self.source = source; 191 | } 192 | 193 | /// Set the character index for this marker 194 | /// 195 | /// 196 | /// ``` 197 | /// # use marked_yaml::Marker; 198 | /// # let mut marker = Marker::new(0, 0, 0, 0); 199 | /// assert_ne!(marker.character(), 1); 200 | /// marker.set_character(1); 201 | /// assert_eq!(marker.character(), 1); 202 | /// ``` 203 | pub fn set_character(&mut self, character: usize) { 204 | self.character = character; 205 | } 206 | 207 | /// Set the line number for this marker 208 | /// 209 | /// ``` 210 | /// # use marked_yaml::Marker; 211 | /// # let mut marker = Marker::new(0, 0, 0, 0); 212 | /// assert_ne!(marker.line(), 1); 213 | /// marker.set_line(1); 214 | /// assert_eq!(marker.line(), 1); 215 | /// ``` 216 | pub fn set_line(&mut self, line: usize) { 217 | self.line = line; 218 | } 219 | 220 | /// Set the column number for this marker 221 | /// 222 | /// ``` 223 | /// # use marked_yaml::Marker; 224 | /// # let mut marker = Marker::new(0, 0, 0, 0); 225 | /// assert_ne!(marker.column(), 1); 226 | /// marker.set_column(1); 227 | /// assert_eq!(marker.column(), 1); 228 | /// ``` 229 | pub fn set_column(&mut self, column: usize) { 230 | self.column = column; 231 | } 232 | } 233 | 234 | impl Display for Marker { 235 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 236 | write!(f, "{}:{}", self.line, self.column) 237 | } 238 | } 239 | 240 | /// The span for a YAML marked node 241 | /// 242 | /// ``` 243 | /// use marked_yaml::{parse_yaml, Marker, Span}; 244 | /// let node = parse_yaml(100, "{foo: bar}").unwrap(); 245 | /// let map = node.as_mapping().unwrap(); 246 | /// assert_eq!(map.span(), &Span::new_with_marks(Marker::new(100, 0, 1, 1), Marker::new(100, 9, 1, 10))); 247 | /// ``` 248 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 249 | pub struct Span { 250 | start: Option, 251 | end: Option, 252 | } 253 | 254 | impl Span { 255 | /// Create a span with no marker information 256 | /// 257 | /// Sometimes we simply do not know where information came from (for example 258 | /// if it was created by software) and in that case we can create a blank 259 | /// span. 260 | /// 261 | /// ``` 262 | /// # use marked_yaml::Span; 263 | /// let blank = Span::new_blank(); 264 | /// # assert_eq!(blank.start(), None); 265 | /// # assert_eq!(blank.end(), None); 266 | /// ``` 267 | pub fn new_blank() -> Self { 268 | Self { 269 | start: None, 270 | end: None, 271 | } 272 | } 273 | 274 | /// Create a span with only start information 275 | /// 276 | /// Sometimes when creating a span we know where it started but not where 277 | /// it ends. This might be during parsing, or for some other reason. 278 | /// 279 | /// ``` 280 | /// # use marked_yaml::{Marker, Span}; 281 | /// let span = Span::new_start(Marker::new(0, 1, 1, 2)); 282 | /// # assert_eq!(span.start().unwrap(), &Marker::new(0, 1, 1, 2)); 283 | /// # assert_eq!(span.end(), None); 284 | /// ``` 285 | pub fn new_start(start: Marker) -> Self { 286 | Self { 287 | start: Some(start), 288 | end: None, 289 | } 290 | } 291 | 292 | /// Create a span with both start and end markers 293 | /// 294 | /// When we know both the start and end of a node, we can create a span 295 | /// which has all that knowledge. 296 | /// 297 | /// ``` 298 | /// # use marked_yaml::{Marker,Span}; 299 | /// let span = Span::new_with_marks(Marker::new(0, 0, 1, 1), Marker::new(10, 1, 2, 1)); 300 | /// # assert_eq!(span.start().unwrap(), &Marker::new(0, 0, 1, 1)); 301 | /// # assert_eq!(span.end().unwrap(), &Marker::new(10, 1, 2, 1)); 302 | /// ``` 303 | pub fn new_with_marks(start: Marker, end: Marker) -> Self { 304 | Self { 305 | start: Some(start), 306 | end: Some(end), 307 | } 308 | } 309 | 310 | /// The start of the span 311 | /// 312 | /// ``` 313 | /// # use marked_yaml::{Marker, Span}; 314 | /// # let span = Span::new_with_marks(Marker::new(0, 0, 1, 1), Marker::new(10, 1, 2, 1)); 315 | /// assert_eq!(span.start(), Some(&Marker::new(0, 0, 1, 1))); 316 | /// ``` 317 | pub fn start(&self) -> Option<&Marker> { 318 | self.start.as_ref() 319 | } 320 | 321 | /// The end of the span 322 | /// 323 | /// ``` 324 | /// # use marked_yaml::{Marker, Span}; 325 | /// # let span = Span::new_with_marks(Marker::new(0, 0, 1, 1), Marker::new(10, 1, 2, 1)); 326 | /// assert_eq!(span.end(), Some(&Marker::new(10, 1, 2, 1))); 327 | /// ``` 328 | pub fn end(&self) -> Option<&Marker> { 329 | self.end.as_ref() 330 | } 331 | 332 | /// The start of the span, mutably 333 | /// 334 | /// ``` 335 | /// # use marked_yaml::{Marker, Span}; 336 | /// # let mut span = Span::new_with_marks(Marker::new(0, 0, 1, 1), Marker::new(10, 1, 2, 1)); 337 | /// span.start_mut().unwrap().set_line(5); 338 | /// assert_eq!(span.start(), Some(&Marker::new(0, 0, 5, 1))); 339 | /// ``` 340 | pub fn start_mut(&mut self) -> Option<&mut Marker> { 341 | self.start.as_mut() 342 | } 343 | 344 | /// The end of the span, mutably 345 | /// 346 | /// ``` 347 | /// # use marked_yaml::{Marker, Span}; 348 | /// # let mut span = Span::new_with_marks(Marker::new(0, 0, 1, 1), Marker::new(10, 1, 2, 1)); 349 | /// span.end_mut().unwrap().set_line(5); 350 | /// assert_eq!(span.end(), Some(&Marker::new(10, 1, 5, 1))); 351 | /// ``` 352 | pub fn end_mut(&mut self) -> Option<&mut Marker> { 353 | self.end.as_mut() 354 | } 355 | 356 | /// Replace the start of the span 357 | /// 358 | /// ``` 359 | /// # use marked_yaml::{Marker, Span}; 360 | /// # let mut span = Span::new_blank(); 361 | /// assert_eq!(span.start(), None); 362 | /// span.set_start(Some(Marker::new(0, 1, 1, 2))); 363 | /// assert_eq!(span.start(), Some(&Marker::new(0, 1, 1, 2))); 364 | /// ``` 365 | pub fn set_start(&mut self, start: Option) { 366 | self.start = start; 367 | } 368 | 369 | /// Replace the end of the span 370 | /// 371 | /// ``` 372 | /// # use marked_yaml::{Marker, Span}; 373 | /// # let mut span = Span::new_blank(); 374 | /// assert_eq!(span.end(), None); 375 | /// span.set_end(Some(Marker::new(0, 1, 1, 2))); 376 | /// assert_eq!(span.end(), Some(&Marker::new(0, 1, 1, 2))); 377 | /// ``` 378 | pub fn set_end(&mut self, end: Option) { 379 | self.end = end; 380 | } 381 | } 382 | 383 | /// A marked YAML node 384 | /// 385 | /// **NOTE**: Nodes are considered equal even if they don't come from the 386 | /// same place. *i.e. their spans are ignored for equality and hashing* 387 | /// 388 | /// ``` 389 | /// use marked_yaml::parse_yaml; 390 | /// let node = parse_yaml(100, "{foo: bar}").unwrap(); 391 | /// assert!(node.as_mapping().is_some()); 392 | /// ``` 393 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] 394 | pub enum Node { 395 | /// A YAML scalar 396 | /// 397 | /// You can test if a node is a scalar, and retrieve it as one if you 398 | /// so wish. 399 | Scalar(MarkedScalarNode), 400 | /// A YAML mapping 401 | /// 402 | /// You can test if a node is a mapping, and retrieve it as one if you 403 | /// so wish. 404 | Mapping(MarkedMappingNode), 405 | /// A YAML sequence 406 | /// 407 | /// You can test if a node is a sequence, and retrieve it as one if you 408 | /// so wish. 409 | Sequence(MarkedSequenceNode), 410 | } 411 | 412 | /// A marked scalar YAML node 413 | /// 414 | /// Scalar nodes are treated by this crate as strings, though a few special 415 | /// values are processed into the types which YAML would ascribe. In particular 416 | /// strings of the value `null`, `true`, `false`, etc. are able to present as 417 | /// their special values to make it a bit easier for users of the crate. 418 | /// 419 | /// **NOTE**: Nodes are considered equal even if they don't come from the 420 | /// same place. *i.e. their spans are ignored for equality and hashing* 421 | /// 422 | /// ``` 423 | /// use marked_yaml::{parse_yaml, Marker}; 424 | /// let node = parse_yaml(100, "{foo: bar}").unwrap(); 425 | /// let map = node.as_mapping().unwrap(); 426 | /// let bar = map.get("foo").unwrap(); 427 | /// // the "bar" string started on line 1, column 7 of source ID 100. 428 | /// assert_eq!(bar.span().start(), Some(&Marker::new(100, 6, 1, 7))); 429 | /// ``` 430 | #[derive(Clone, Debug)] 431 | pub struct MarkedScalarNode { 432 | span: Span, 433 | value: String, 434 | may_coerce: bool, 435 | } 436 | 437 | pub(crate) type MappingHash = LinkedHashMap; 438 | 439 | /// A marked YAML mapping node 440 | /// 441 | /// Mapping nodes in YAML are defined as a key/value mapping where the keys are 442 | /// unique and always scalars, whereas values may be YAML nodes of any kind. 443 | /// 444 | /// Because *some* users of this crate may need to care about insertion order 445 | /// we use [`hashlink::LinkedHashMap`] for this. 446 | /// 447 | /// **NOTE**: Nodes are considered equal even if they don't come from the 448 | /// same place. *i.e. their spans are ignored for equality and hashing* 449 | /// 450 | /// ``` 451 | /// use marked_yaml::{parse_yaml, Marker, Span}; 452 | /// let node = parse_yaml(100, "{foo: bar}").unwrap(); 453 | /// let map = node.as_mapping().unwrap(); 454 | /// assert_eq!(map.span(), &Span::new_with_marks(Marker::new(100, 0, 1, 1), Marker::new(100, 9, 1, 10))); 455 | /// ``` 456 | #[derive(Clone, Debug)] 457 | pub struct MarkedMappingNode { 458 | span: Span, 459 | value: MappingHash, 460 | } 461 | 462 | /// A marked YAML sequence node 463 | /// 464 | /// Sequence nodes in YAML are simply ordered lists of YAML nodes. 465 | /// 466 | /// **NOTE**: Nodes are considered equal even if they don't come from the 467 | /// same place. *i.e. their spans are ignored for equality and hashing* 468 | /// 469 | /// ``` 470 | /// use marked_yaml::{parse_yaml, Marker, Span}; 471 | /// let node = parse_yaml(100, "{foo: [bar]}").unwrap(); 472 | /// let map = node.as_mapping().unwrap(); 473 | /// let seq = map.get("foo").unwrap(); 474 | /// let seq = seq.as_sequence().unwrap(); 475 | /// assert_eq!(seq.span(), &Span::new_with_marks(Marker::new(100, 6, 1, 7), Marker::new(100, 10, 1, 11))); 476 | /// ``` 477 | #[derive(Clone, Debug)] 478 | pub struct MarkedSequenceNode { 479 | span: Span, 480 | value: Vec, 481 | } 482 | 483 | macro_rules! basic_traits { 484 | ($t:path) => { 485 | impl PartialEq for $t { 486 | fn eq(&self, other: &Self) -> bool { 487 | self.value == other.value 488 | } 489 | 490 | #[allow(clippy::partialeq_ne_impl)] 491 | fn ne(&self, other: &Self) -> bool { 492 | self.value != other.value 493 | } 494 | } 495 | 496 | impl Eq for $t {} 497 | 498 | impl Hash for $t { 499 | fn hash(&self, state: &mut H) { 500 | self.value.hash(state); 501 | } 502 | } 503 | }; 504 | } 505 | 506 | basic_traits!(MarkedScalarNode); 507 | basic_traits!(MarkedSequenceNode); 508 | basic_traits!(MarkedMappingNode); 509 | 510 | impl From for Node 511 | where 512 | T: Into, 513 | { 514 | fn from(value: T) -> Node { 515 | Node::Scalar(value.into()) 516 | } 517 | } 518 | 519 | impl From for Node { 520 | fn from(value: MarkedSequenceNode) -> Node { 521 | Node::Sequence(value) 522 | } 523 | } 524 | 525 | impl From> for Node 526 | where 527 | T: Into, 528 | { 529 | fn from(value: Vec) -> Node { 530 | Node::Sequence(value.into_iter().collect()) 531 | } 532 | } 533 | 534 | impl From for Node { 535 | fn from(value: MarkedMappingNode) -> Node { 536 | Node::Mapping(value) 537 | } 538 | } 539 | 540 | impl From for Node { 541 | fn from(value: MappingHash) -> Node { 542 | Node::Mapping(value.into()) 543 | } 544 | } 545 | 546 | macro_rules! node_span { 547 | ($t:path) => { 548 | impl $t { 549 | doc_comment!( 550 | concat!( 551 | r#"Retrieve the Span from this node. 552 | 553 | ``` 554 | # use marked_yaml::types::*; 555 | let node = "#, 556 | stringify!($t), 557 | r#"::new_empty(Span::new_blank()); 558 | assert_eq!(node.span(), &Span::new_blank()); 559 | ```"# 560 | ), 561 | pub fn span(&self) -> &Span { 562 | &self.span 563 | } 564 | ); 565 | doc_comment!( 566 | concat!( 567 | r#"Retrieve the Span from this node mutably. 568 | 569 | ``` 570 | # use marked_yaml::types::*; 571 | let mut node = "#, 572 | stringify!($t), 573 | r#"::new_empty(Span::new_blank()); 574 | node.span_mut().set_start(Some(Marker::new(0, 0, 1, 0))); 575 | assert_eq!(node.span().start(), Some(&Marker::new(0, 0, 1, 0))); 576 | ```"# 577 | ), 578 | pub fn span_mut(&mut self) -> &mut Span { 579 | &mut self.span 580 | } 581 | ); 582 | } 583 | }; 584 | } 585 | 586 | node_span!(MarkedScalarNode); 587 | node_span!(MarkedMappingNode); 588 | node_span!(MarkedSequenceNode); 589 | 590 | impl Node { 591 | /// Retrieve the Span from the contained Node 592 | /// 593 | /// ``` 594 | /// # use marked_yaml::types::*; 595 | /// let node: Node = "foobar".into(); 596 | /// let span = node.span(); 597 | /// assert_eq!(span.start(), None); 598 | /// ``` 599 | pub fn span(&self) -> &Span { 600 | match self { 601 | Node::Scalar(msn) => msn.span(), 602 | Node::Sequence(msn) => msn.span(), 603 | Node::Mapping(mmn) => mmn.span(), 604 | } 605 | } 606 | 607 | /// Retrieve the Span from the contained Node, mutably 608 | /// 609 | /// ``` 610 | /// # use marked_yaml::types::*; 611 | /// let mut node: Node = "foobar".into(); 612 | /// let mut span = node.span_mut(); 613 | /// assert_eq!(span.start(), None); 614 | /// span.set_start(Some(Marker::new(0, 0, 1, 0))); 615 | /// assert_eq!(span.start(), Some(&Marker::new(0, 0, 1, 0))); 616 | /// ``` 617 | pub fn span_mut(&mut self) -> &mut Span { 618 | match self { 619 | Node::Scalar(msn) => msn.span_mut(), 620 | Node::Sequence(msn) => msn.span_mut(), 621 | Node::Mapping(mmn) => mmn.span_mut(), 622 | } 623 | } 624 | 625 | /// Retrieve the scalar from this node if there is one 626 | /// 627 | /// ``` 628 | /// # use marked_yaml::types::*; 629 | /// let node: Node = "foobar".into(); 630 | /// let scalar = node.as_scalar(); 631 | /// assert!(scalar.is_some()); 632 | /// ``` 633 | pub fn as_scalar(&self) -> Option<&MarkedScalarNode> { 634 | match self { 635 | Node::Scalar(msn) => Some(msn), 636 | _ => None, 637 | } 638 | } 639 | 640 | /// Retrieve the sequence from this node if there is one 641 | /// 642 | /// ``` 643 | /// # use marked_yaml::types::*; 644 | /// let node: Node = vec!["foobar"].into(); 645 | /// let sequence = node.as_sequence(); 646 | /// assert!(sequence.is_some()); 647 | /// ``` 648 | pub fn as_sequence(&self) -> Option<&MarkedSequenceNode> { 649 | match self { 650 | Node::Sequence(msn) => Some(msn), 651 | _ => None, 652 | } 653 | } 654 | 655 | /// Retrieve the mapping from this node if there is one 656 | /// 657 | /// ``` 658 | /// # use marked_yaml::*; 659 | /// let node: Node = parse_yaml(0, "{foobar: baz}").unwrap(); 660 | /// let mapping = node.as_mapping(); 661 | /// assert!(mapping.is_some()); 662 | /// ``` 663 | pub fn as_mapping(&self) -> Option<&MarkedMappingNode> { 664 | match self { 665 | Node::Mapping(mmn) => Some(mmn), 666 | _ => None, 667 | } 668 | } 669 | 670 | /// Retrieve the scalar from this node if there is one, mutably 671 | /// 672 | /// ``` 673 | /// # use marked_yaml::types::*; 674 | /// let mut node: Node = "foobar".into(); 675 | /// let mut scalar = node.as_scalar_mut(); 676 | /// assert!(scalar.is_some()); 677 | /// ``` 678 | pub fn as_scalar_mut(&mut self) -> Option<&mut MarkedScalarNode> { 679 | match self { 680 | Node::Scalar(msn) => Some(msn), 681 | _ => None, 682 | } 683 | } 684 | 685 | /// Retrieve the sequence from this node if there is one, mutably 686 | /// 687 | /// ``` 688 | /// # use marked_yaml::types::*; 689 | /// let mut node: Node = vec!["foobar"].into(); 690 | /// let mut sequence = node.as_sequence_mut(); 691 | /// assert!(sequence.is_some()); 692 | /// ``` 693 | pub fn as_sequence_mut(&mut self) -> Option<&mut MarkedSequenceNode> { 694 | match self { 695 | Node::Sequence(msn) => Some(msn), 696 | _ => None, 697 | } 698 | } 699 | 700 | /// Retrieve the mapping from this node if there is one, mutably 701 | /// 702 | /// ``` 703 | /// # use marked_yaml::*; 704 | /// let mut node: Node = parse_yaml(0, "{foobar: baz}").unwrap(); 705 | /// let mut mapping = node.as_mapping_mut(); 706 | /// assert!(mapping.is_some()); 707 | /// ``` 708 | pub fn as_mapping_mut(&mut self) -> Option<&mut MarkedMappingNode> { 709 | match self { 710 | Node::Mapping(mmn) => Some(mmn), 711 | _ => None, 712 | } 713 | } 714 | } 715 | 716 | impl MarkedScalarNode { 717 | /// Create a new scalar node with no value 718 | /// 719 | /// ``` 720 | /// # use marked_yaml::types::*; 721 | /// let node = MarkedScalarNode::new_empty(Span::new_blank()); 722 | /// ``` 723 | pub fn new_empty(span: Span) -> Self { 724 | Self { 725 | span, 726 | value: String::new(), 727 | may_coerce: true, 728 | } 729 | } 730 | 731 | /// Create a new scalar node 732 | /// 733 | /// ``` 734 | /// # use marked_yaml::types::*; 735 | /// let node = MarkedScalarNode::new(Span::new_blank(), "foobar"); 736 | /// ``` 737 | pub fn new<'a, S: Into>>(span: Span, content: S) -> Self { 738 | Self { 739 | span, 740 | value: content.into().into_owned(), 741 | may_coerce: true, 742 | } 743 | } 744 | 745 | /// Treat the scalar node as a string 746 | /// 747 | /// Since scalars are always stringish, this is always safe. 748 | /// 749 | /// ``` 750 | /// # use marked_yaml::types::*; 751 | /// let node: MarkedScalarNode = "foobar".into(); 752 | /// assert_eq!(node.as_str(), "foobar"); 753 | /// ``` 754 | pub fn as_str(&self) -> &str { 755 | self.value.as_str() 756 | } 757 | 758 | /// Enable or disable permission to coerce values 759 | /// 760 | /// The various [`as_bool()`][Self::as_bool()] and other methods 761 | /// which can coerce a string can be forced to always deny 762 | /// coercion. 763 | /// 764 | #[cfg_attr( 765 | feature = "serde", 766 | doc = r#" 767 | Note: this also applies for deserializing nodes via serde. 768 | 769 | "# 770 | )] 771 | /// ``` 772 | /// # use marked_yaml::types::*; 773 | /// let mut node: MarkedScalarNode = "true".into(); 774 | /// assert_eq!(node.as_bool(), Some(true)); 775 | /// node.set_coerce(false); 776 | /// assert_eq!(node.as_bool(), None); 777 | /// ``` 778 | pub fn set_coerce(&mut self, may_coerce: bool) { 779 | self.may_coerce = may_coerce; 780 | } 781 | 782 | /// Retrieve whether or not this node is set to be coerceable 783 | /// 784 | /// The various [`as_bool()`][Self::as_bool()] and other methods 785 | /// which can coerce a string can be forced to deny coercion. 786 | /// 787 | #[cfg_attr( 788 | feature = "serde", 789 | doc = r#" 790 | Note: this also applies for deserializing nodes via serde. 791 | 792 | "# 793 | )] 794 | /// ``` 795 | /// # use marked_yaml::types::*; 796 | /// let mut node: MarkedScalarNode = "true".into(); 797 | /// assert_eq!(node.as_bool(), Some(true)); 798 | /// assert_eq!(node.may_coerce(), true); 799 | /// node.set_coerce(false); 800 | /// assert_eq!(node.as_bool(), None); 801 | /// assert_eq!(node.may_coerce(), false); 802 | /// ``` 803 | pub fn may_coerce(&self) -> bool { 804 | self.may_coerce 805 | } 806 | 807 | /// Treat the scalar node as a boolean 808 | /// 809 | /// If the scalar contains any of the following then it is true: 810 | /// 811 | /// * `true` 812 | /// * `True` 813 | /// * `TRUE` 814 | /// 815 | /// The following are considered false: 816 | /// 817 | /// * `false` 818 | /// * `False` 819 | /// * `FALSE` 820 | /// 821 | /// Everything else is not a boolean and so will return None 822 | /// 823 | /// Note: If you have done [`.set_coerce(false)`](MarkedScalarNode::set_coerce()) 824 | /// then no matter the string's value, this will return `None`. 825 | /// 826 | /// ``` 827 | /// # use marked_yaml::types::*; 828 | /// let node: MarkedScalarNode = "true".into(); 829 | /// assert_eq!(node.as_bool(), Some(true)); 830 | /// let node: MarkedScalarNode = "FALSE".into(); 831 | /// assert_eq!(node.as_bool(), Some(false)); 832 | /// let node: MarkedScalarNode = "NO".into(); // YAML boolean, but not for us 833 | /// assert_eq!(node.as_bool(), None); 834 | /// let mut node: MarkedScalarNode = "true".into(); 835 | /// node.set_coerce(false); 836 | /// assert_eq!(node.as_bool(), None); 837 | /// ``` 838 | pub fn as_bool(&self) -> Option { 839 | if self.may_coerce { 840 | match self.value.as_str() { 841 | "true" | "True" | "TRUE" => Some(true), 842 | "false" | "False" | "FALSE" => Some(false), 843 | _ => None, 844 | } 845 | } else { 846 | None 847 | } 848 | } 849 | 850 | #[cfg(feature = "serde")] 851 | pub(crate) fn is_empty_scalar(&self) -> bool { 852 | self.value.is_empty() && self.may_coerce 853 | } 854 | } 855 | 856 | impl<'a> From<&'a str> for MarkedScalarNode { 857 | /// Convert from any borrowed string into a node 858 | /// 859 | /// ``` 860 | /// # use marked_yaml::types::*; 861 | /// let node: MarkedScalarNode = "foobar".into(); 862 | /// ``` 863 | fn from(value: &'a str) -> Self { 864 | Self::new(Span::new_blank(), value) 865 | } 866 | } 867 | 868 | impl From for MarkedScalarNode { 869 | /// Convert from any owned string into a node 870 | /// 871 | /// ``` 872 | /// # use marked_yaml::types::*; 873 | /// let foobar = "foobar".to_string(); 874 | /// let node: MarkedScalarNode = foobar.into(); 875 | /// ``` 876 | fn from(value: String) -> Self { 877 | Self::new(Span::new_blank(), value) 878 | } 879 | } 880 | 881 | impl From for MarkedScalarNode { 882 | /// Convert from a boolean into a node 883 | /// 884 | /// ``` 885 | /// # use marked_yaml::types::*; 886 | /// let node = MarkedScalarNode::from(true); 887 | /// ``` 888 | fn from(value: bool) -> Self { 889 | if value { 890 | "true".into() 891 | } else { 892 | "false".into() 893 | } 894 | } 895 | } 896 | 897 | macro_rules! scalar_from_to_number { 898 | ($t:ident, $as:ident) => { 899 | scalar_from_to_number!($t, $as, 0); 900 | }; 901 | 902 | ($t:ident, $as:ident, $zero:expr) => { 903 | impl From<$t> for MarkedScalarNode { 904 | doc_comment!( 905 | concat!( 906 | "Convert from ", 907 | stringify!($t), 908 | r#" into a node 909 | 910 | ``` 911 | # use marked_yaml::types::*; 912 | let value: "#, 913 | stringify!($t), 914 | " = ", 915 | stringify!($zero), 916 | r#"; 917 | let node: MarkedScalarNode = value.into(); 918 | assert_eq!(&*node, "0"); 919 | ```"# 920 | ), 921 | fn from(value: $t) -> Self { 922 | format!("{}", value).into() 923 | } 924 | ); 925 | } 926 | 927 | impl MarkedScalarNode { 928 | doc_comment!( 929 | concat!( 930 | "Treat the scalar node as ", 931 | stringify!($t), 932 | r#". 933 | 934 | If this scalar node's value can be represented properly as 935 | a number of the right kind then return it. This is essentially 936 | a shortcut for using the `FromStr` trait on the return value of 937 | `.as_str()`. 938 | 939 | Note, this honours the setting of [`MarkedScalarNode::set_coerce()`] 940 | 941 | ``` 942 | # use marked_yaml::types::*; 943 | let mut node: MarkedScalarNode = "0".into(); 944 | assert_eq!(node.as_"#, 945 | stringify!($t), 946 | r#"(), Some(0"#, 947 | stringify!($t), 948 | r#")); 949 | node.set_coerce(false); 950 | assert_eq!(node.as_"#, 951 | stringify!($t), 952 | r#"(), None); 953 | ```"# 954 | ), 955 | pub fn $as(&self) -> Option<$t> { 956 | if self.may_coerce { 957 | use std::str::FromStr; 958 | $t::from_str(&self.value).ok() 959 | } else { 960 | None 961 | } 962 | } 963 | ); 964 | } 965 | }; 966 | } 967 | 968 | scalar_from_to_number!(i8, as_i8); 969 | scalar_from_to_number!(i16, as_i16); 970 | scalar_from_to_number!(i32, as_i32); 971 | scalar_from_to_number!(i64, as_i64); 972 | scalar_from_to_number!(i128, as_i128); 973 | scalar_from_to_number!(isize, as_isize); 974 | scalar_from_to_number!(u8, as_u8); 975 | scalar_from_to_number!(u16, as_u16); 976 | scalar_from_to_number!(u32, as_u32); 977 | scalar_from_to_number!(u64, as_u64); 978 | scalar_from_to_number!(u128, as_u128); 979 | scalar_from_to_number!(usize, as_usize); 980 | scalar_from_to_number!(f32, as_f32, 0.0); 981 | scalar_from_to_number!(f64, as_f64, 0.0); 982 | 983 | impl Deref for MarkedScalarNode { 984 | type Target = str; 985 | 986 | /// Borrow the string value inside this scalar node 987 | /// 988 | /// ``` 989 | /// # use marked_yaml::types::*; 990 | /// # use std::str::FromStr; 991 | /// let truth: MarkedScalarNode = "true".into(); 992 | /// assert!(bool::from_str(&truth).unwrap()) 993 | /// ``` 994 | fn deref(&self) -> &Self::Target { 995 | &self.value 996 | } 997 | } 998 | 999 | impl Borrow for MarkedScalarNode { 1000 | fn borrow(&self) -> &str { 1001 | &self.value 1002 | } 1003 | } 1004 | 1005 | impl MarkedSequenceNode { 1006 | /// Create a new empty sequence node 1007 | /// 1008 | /// ``` 1009 | /// # use marked_yaml::types::*; 1010 | /// let node = MarkedSequenceNode::new_empty(Span::new_blank()); 1011 | /// ``` 1012 | pub fn new_empty(span: Span) -> Self { 1013 | Self { 1014 | span, 1015 | value: Vec::new(), 1016 | } 1017 | } 1018 | 1019 | /// Create a new sequence node from a vector of nodes 1020 | /// 1021 | /// ``` 1022 | /// # use marked_yaml::types::*; 1023 | /// let node = MarkedSequenceNode::new(Span::new_blank(), Vec::new()); 1024 | /// ``` 1025 | pub fn new(span: Span, value: Vec) -> Self { 1026 | Self { span, value } 1027 | } 1028 | 1029 | /// Get the node at the given index 1030 | /// 1031 | /// If the index is invalid then None will be returned 1032 | /// 1033 | /// ``` 1034 | /// # use marked_yaml::types::*; 1035 | /// let seq: MarkedSequenceNode = vec!["foobar"].into_iter().collect(); 1036 | /// assert_eq!(seq.get_node(0) 1037 | /// .and_then(Node::as_scalar) 1038 | /// .map(MarkedScalarNode::as_str) 1039 | /// .unwrap(), 1040 | /// "foobar"); 1041 | pub fn get_node(&self, index: usize) -> Option<&Node> { 1042 | self.value.get(index) 1043 | } 1044 | 1045 | /// Get the scalar at the given index 1046 | /// 1047 | /// If the index is invalid, or the node at that index is not a scalar 1048 | /// node, then None will be returned. 1049 | /// 1050 | /// ``` 1051 | /// # use marked_yaml::types::*; 1052 | /// let seq: MarkedSequenceNode = vec!["foobar"].into_iter().collect(); 1053 | /// assert_eq!(seq.get_scalar(0) 1054 | /// .map(MarkedScalarNode::as_str) 1055 | /// .unwrap(), 1056 | /// "foobar"); 1057 | /// ``` 1058 | pub fn get_scalar(&self, index: usize) -> Option<&MarkedScalarNode> { 1059 | self.get_node(index).and_then(Node::as_scalar) 1060 | } 1061 | 1062 | /// Get the sequence at the given index 1063 | /// 1064 | /// If the index is invalid, or the node at that index is not a sequence 1065 | /// node, then None will be returned. 1066 | /// 1067 | /// ``` 1068 | /// # use marked_yaml::types::*; 1069 | /// let seq: MarkedSequenceNode = vec![vec!["foobar"]].into_iter().collect(); 1070 | /// assert_eq!(seq.get_sequence(0) 1071 | /// .and_then(|s| s.get_scalar(0)) 1072 | /// .map(MarkedScalarNode::as_str) 1073 | /// .unwrap(), 1074 | /// "foobar"); 1075 | /// ``` 1076 | pub fn get_sequence(&self, index: usize) -> Option<&MarkedSequenceNode> { 1077 | self.get_node(index).and_then(Node::as_sequence) 1078 | } 1079 | 1080 | /// Get the mapping at the given index 1081 | /// 1082 | /// If the index is invalid, or the node at that index is not a mapping 1083 | /// node, then None will be returned. 1084 | /// 1085 | /// ``` 1086 | /// # use marked_yaml::types::*; 1087 | /// # use hashlink::LinkedHashMap; 1088 | /// # let map: LinkedHashMap = LinkedHashMap::new(); 1089 | /// let seq: MarkedSequenceNode = vec![map].into_iter().collect(); 1090 | /// assert!(seq.get_mapping(0).is_some()); 1091 | /// ``` 1092 | pub fn get_mapping(&self, index: usize) -> Option<&MarkedMappingNode> { 1093 | self.get_node(index).and_then(Node::as_mapping) 1094 | } 1095 | } 1096 | 1097 | impl Deref for MarkedSequenceNode { 1098 | type Target = Vec; 1099 | fn deref(&self) -> &Self::Target { 1100 | &self.value 1101 | } 1102 | } 1103 | 1104 | impl DerefMut for MarkedSequenceNode { 1105 | fn deref_mut(&mut self) -> &mut Self::Target { 1106 | &mut self.value 1107 | } 1108 | } 1109 | 1110 | impl FromIterator for MarkedSequenceNode 1111 | where 1112 | T: Into, 1113 | { 1114 | /// Allow collecting things into a sequence node 1115 | /// 1116 | /// ``` 1117 | /// # use marked_yaml::types::*; 1118 | /// let node: MarkedSequenceNode = vec!["hello", "world"].into_iter().collect(); 1119 | /// ``` 1120 | fn from_iter>(iter: I) -> Self { 1121 | let value: Vec = iter.into_iter().map(Into::into).collect(); 1122 | let span = match value.len() { 1123 | 0 => Span::new_blank(), 1124 | 1 => *value[0].span(), 1125 | _ => Span { 1126 | start: value[0].span().start, 1127 | end: value[value.len() - 1].span().end, 1128 | }, 1129 | }; 1130 | Self { span, value } 1131 | } 1132 | } 1133 | 1134 | impl From> for MarkedSequenceNode 1135 | where 1136 | T: Into, 1137 | { 1138 | /// Allow converting from vectors of things to sequence nodes 1139 | /// 1140 | /// ``` 1141 | /// # use marked_yaml::types::*; 1142 | /// let node: MarkedSequenceNode = vec!["hello", "world"].into(); 1143 | /// ``` 1144 | fn from(value: Vec) -> Self { 1145 | let value: Vec = value.into_iter().map(Into::into).collect(); 1146 | let span = match value.len() { 1147 | 0 => Span::new_blank(), 1148 | 1 => *value[0].span(), 1149 | _ => { 1150 | let start = value[0].span().start; 1151 | let end = value[value.len() - 1].span().end; 1152 | Span { start, end } 1153 | } 1154 | }; 1155 | Self { span, value } 1156 | } 1157 | } 1158 | 1159 | impl MarkedMappingNode { 1160 | /// Create a new empty mapping node 1161 | /// 1162 | /// ``` 1163 | /// # use marked_yaml::types::*; 1164 | /// let node = MarkedMappingNode::new_empty(Span::new_blank()); 1165 | /// ``` 1166 | pub fn new_empty(span: Span) -> Self { 1167 | Self { 1168 | span, 1169 | value: LinkedHashMap::new(), 1170 | } 1171 | } 1172 | 1173 | /// Create a new mapping node from the given hash table 1174 | /// 1175 | /// ``` 1176 | /// # use marked_yaml::types::*; 1177 | /// # use hashlink::LinkedHashMap; 1178 | /// let node = MarkedMappingNode::new(Span::new_blank(), LinkedHashMap::new()); 1179 | /// ``` 1180 | pub fn new(span: Span, value: MappingHash) -> Self { 1181 | Self { span, value } 1182 | } 1183 | 1184 | /// Get the node for the given string key 1185 | /// 1186 | /// If the index is not found then None is returned. 1187 | /// 1188 | /// ``` 1189 | /// # use marked_yaml::types::*; 1190 | /// # use marked_yaml::parse_yaml; 1191 | /// let node = parse_yaml(0, "{key: value}").unwrap(); 1192 | /// let map = node.as_mapping().unwrap(); 1193 | /// assert_eq!(map.get_node("key") 1194 | /// .and_then(Node::as_scalar) 1195 | /// .map(MarkedScalarNode::as_str) 1196 | /// .unwrap(), 1197 | /// "value"); 1198 | /// ``` 1199 | pub fn get_node(&self, index: &str) -> Option<&Node> { 1200 | self.value.get(index) 1201 | } 1202 | 1203 | /// Get the scalar for the given string key 1204 | /// 1205 | /// If the key is not found, or the node for that key is not a scalar 1206 | /// node, then None will be returned. 1207 | /// 1208 | /// ``` 1209 | /// # use marked_yaml::types::*; 1210 | /// # use marked_yaml::parse_yaml; 1211 | /// let node = parse_yaml(0, "{key: value}").unwrap(); 1212 | /// let map = node.as_mapping().unwrap(); 1213 | /// assert_eq!(map.get_scalar("key") 1214 | /// .map(MarkedScalarNode::as_str) 1215 | /// .unwrap(), 1216 | /// "value"); 1217 | /// ``` 1218 | pub fn get_scalar(&self, index: &str) -> Option<&MarkedScalarNode> { 1219 | self.get_node(index).and_then(Node::as_scalar) 1220 | } 1221 | 1222 | /// Get the sequence at the given index 1223 | /// 1224 | /// If the key is not found, or the node for that key is not a sequence 1225 | /// node, then None will be returned. 1226 | /// 1227 | /// ``` 1228 | /// # use marked_yaml::types::*; 1229 | /// # use marked_yaml::parse_yaml; 1230 | /// let node = parse_yaml(0, "{key: [value]}").unwrap(); 1231 | /// let map = node.as_mapping().unwrap(); 1232 | /// assert_eq!(map.get_sequence("key") 1233 | /// .and_then(|s| s.get_scalar(0)) 1234 | /// .map(MarkedScalarNode::as_str) 1235 | /// .unwrap(), 1236 | /// "value"); 1237 | /// ``` 1238 | pub fn get_sequence(&self, index: &str) -> Option<&MarkedSequenceNode> { 1239 | self.get_node(index).and_then(Node::as_sequence) 1240 | } 1241 | 1242 | /// Get the mapping at the given index 1243 | /// 1244 | /// If the key is not found, or the node for that key is not a mapping 1245 | /// node, then None will be returned. 1246 | /// 1247 | /// ``` 1248 | /// # use marked_yaml::types::*; 1249 | /// # use marked_yaml::parse_yaml; 1250 | /// let node = parse_yaml(0, "{key: {inner: value}}").unwrap(); 1251 | /// let map = node.as_mapping().unwrap(); 1252 | /// assert_eq!(map.get_mapping("key") 1253 | /// .and_then(|m| m.get_scalar("inner")) 1254 | /// .map(MarkedScalarNode::as_str) 1255 | /// .unwrap(), 1256 | /// "value"); 1257 | /// ``` 1258 | pub fn get_mapping(&self, index: &str) -> Option<&MarkedMappingNode> { 1259 | self.get_node(index).and_then(Node::as_mapping) 1260 | } 1261 | } 1262 | 1263 | impl Deref for MarkedMappingNode { 1264 | type Target = MappingHash; 1265 | fn deref(&self) -> &Self::Target { 1266 | &self.value 1267 | } 1268 | } 1269 | 1270 | impl DerefMut for MarkedMappingNode { 1271 | fn deref_mut(&mut self) -> &mut Self::Target { 1272 | &mut self.value 1273 | } 1274 | } 1275 | 1276 | impl From for MarkedMappingNode { 1277 | fn from(value: MappingHash) -> Self { 1278 | Self::new(Span::new_blank(), value) 1279 | } 1280 | } 1281 | 1282 | impl FromIterator<(T, U)> for MarkedMappingNode 1283 | where 1284 | T: Into, 1285 | U: Into, 1286 | { 1287 | /// Allow collecting into a mapping node 1288 | /// 1289 | /// ``` 1290 | /// # use marked_yaml::types::*; 1291 | /// # use std::collections::HashMap; 1292 | /// # let mut hashmap = HashMap::new(); 1293 | /// hashmap.insert("hello", vec!["world".to_string()]); 1294 | /// hashmap.insert("key", vec!["value".to_string()]); 1295 | /// let node: MarkedMappingNode = hashmap.into_iter().collect(); 1296 | /// ``` 1297 | fn from_iter>(iter: I) -> Self { 1298 | let value: MappingHash = iter 1299 | .into_iter() 1300 | .map(|(k, v)| (k.into(), v.into())) 1301 | .collect(); 1302 | let span = match value.len() { 1303 | 0 => Span::new_blank(), 1304 | // Unwrap is safe because there's at least one span here 1305 | 1 => { 1306 | let (k, v) = value.iter().next().unwrap(); 1307 | Span { 1308 | start: k.span().start, 1309 | end: v.span().end, 1310 | } 1311 | } 1312 | _ => { 1313 | let mut iter = value.iter(); 1314 | // Unwraps save because there's at least two spans here 1315 | let start = iter.next().unwrap().0.span().start; 1316 | let end = iter.next_back().unwrap().1.span().end; 1317 | Span { start, end } 1318 | } 1319 | }; 1320 | Self { span, value } 1321 | } 1322 | } 1323 | 1324 | /// Errors which could be encountered while converting from a `yaml_rust::Yaml` 1325 | #[derive(Debug, PartialEq, Eq)] 1326 | pub enum YamlConversionError { 1327 | /// An alias was encountered while converting 1328 | Alias, 1329 | /// A BadValue was encountered while converting 1330 | BadValue, 1331 | /// A non-scalar value was encountered when a scalar was expected 1332 | NonScalar, 1333 | } 1334 | 1335 | impl TryFrom for MarkedScalarNode { 1336 | type Error = YamlConversionError; 1337 | 1338 | fn try_from(value: YamlNode) -> Result { 1339 | match value { 1340 | YamlNode::Alias(_) => Err(YamlConversionError::Alias), 1341 | YamlNode::Array(_) => Err(YamlConversionError::NonScalar), 1342 | YamlNode::BadValue => Err(YamlConversionError::BadValue), 1343 | YamlNode::Boolean(b) => Ok(b.into()), 1344 | YamlNode::Hash(_) => Err(YamlConversionError::NonScalar), 1345 | YamlNode::Integer(i) => Ok(i.into()), 1346 | YamlNode::Null => Ok("null".into()), 1347 | YamlNode::Real(s) => Ok(s.into()), 1348 | YamlNode::String(s) => Ok(s.into()), 1349 | } 1350 | } 1351 | } 1352 | 1353 | impl TryFrom for Node { 1354 | type Error = YamlConversionError; 1355 | 1356 | /// Convert from any `yaml_rust::Yaml` to a Node 1357 | /// 1358 | /// ``` 1359 | /// # use yaml_rust::YamlLoader; 1360 | /// # use marked_yaml::types::*; 1361 | /// # use std::convert::TryFrom; 1362 | /// let docs = YamlLoader::load_from_str("[1, 2]").unwrap(); 1363 | /// let yaml = docs.into_iter().next().unwrap(); 1364 | /// let node = Node::try_from(yaml).unwrap(); 1365 | /// ``` 1366 | fn try_from(value: YamlNode) -> Result { 1367 | match value { 1368 | YamlNode::Array(arr) => Ok(Node::Sequence( 1369 | arr.into_iter() 1370 | .map(Node::try_from) 1371 | .collect::>()?, 1372 | )), 1373 | YamlNode::Hash(h) => Ok(Node::Mapping( 1374 | h.into_iter() 1375 | .map(|(k, v)| Ok((MarkedScalarNode::try_from(k)?, Node::try_from(v)?))) 1376 | .collect::>()?, 1377 | )), 1378 | scalar => Ok(Node::Scalar(MarkedScalarNode::try_from(scalar)?)), 1379 | } 1380 | } 1381 | } 1382 | 1383 | impl From for YamlNode { 1384 | fn from(value: MarkedScalarNode) -> Self { 1385 | YamlNode::String(value.value) 1386 | } 1387 | } 1388 | 1389 | impl From for YamlNode { 1390 | fn from(value: MarkedSequenceNode) -> Self { 1391 | YamlNode::Array(value.value.into_iter().map(Into::into).collect()) 1392 | } 1393 | } 1394 | 1395 | impl From for YamlNode { 1396 | fn from(value: MarkedMappingNode) -> Self { 1397 | YamlNode::Hash( 1398 | value 1399 | .value 1400 | .into_iter() 1401 | .map(|(k, v)| (k.into(), v.into())) 1402 | .collect(), 1403 | ) 1404 | } 1405 | } 1406 | 1407 | impl From for YamlNode { 1408 | fn from(value: Node) -> Self { 1409 | match value { 1410 | Node::Scalar(msn) => msn.into(), 1411 | Node::Sequence(msn) => msn.into(), 1412 | Node::Mapping(mmn) => mmn.into(), 1413 | } 1414 | } 1415 | } 1416 | 1417 | #[cfg(test)] 1418 | mod test { 1419 | use super::super::*; 1420 | use super::*; 1421 | 1422 | #[test] 1423 | fn basic_marker_checks() { 1424 | let marker = Marker::new(0, 3, 1, 2); 1425 | assert_eq!(marker.source(), 0); 1426 | assert_eq!(marker.character(), 3); 1427 | assert_eq!(marker.line(), 1); 1428 | assert_eq!(marker.column(), 2); 1429 | assert_eq!(format!("{}", marker), "1:2"); 1430 | let rendered = marker.render(|n| { 1431 | assert_eq!(n, 0); 1432 | "name" 1433 | }); 1434 | assert_eq!(format!("{}", rendered), "name:1:2"); 1435 | } 1436 | 1437 | #[test] 1438 | fn basic_span_checks() { 1439 | let span = Span::new_blank(); 1440 | assert_eq!(span.start(), None); 1441 | assert_eq!(span.end(), None); 1442 | let mark = Marker::new(0, 1, 1, 2); 1443 | let mark2 = Marker::new(3, 9, 4, 5); 1444 | let span = Span::new_start(mark); 1445 | assert_eq!(span.start(), Some(&mark)); 1446 | assert_eq!(span.end(), None); 1447 | let span = Span::new_with_marks(mark, mark2); 1448 | assert_eq!(span.start(), Some(&mark)); 1449 | assert_eq!(span.end(), Some(&mark2)); 1450 | } 1451 | 1452 | #[test] 1453 | fn basic_explore_load_test() { 1454 | let node = parse_yaml(0, include_str!("../examples/everything.yaml")).unwrap(); 1455 | let map = node.as_mapping().unwrap(); 1456 | assert_eq!(node.as_scalar(), None); 1457 | assert_eq!(node.as_sequence(), None); 1458 | assert_eq!(map.get_node("XXX NOT PRESENT XXX"), None); 1459 | assert_eq!(map.get_scalar("mapping"), None); 1460 | assert_eq!(map.get_sequence("mapping"), None); 1461 | assert_eq!(map.get_mapping("simple"), None); 1462 | // This actually uses .eq() 1463 | assert_ne!(map.get_scalar("boolean1"), map.get_scalar("boolean2")); 1464 | // Whereas this uses .ne() 1465 | assert!(map.get_scalar("boolean1") != map.get_scalar("boolean2")); 1466 | // Now check the spans 1467 | assert_eq!(node.span(), map.span()); 1468 | let seq = map.get_sequence("heterogenous").unwrap(); 1469 | assert_eq!(seq.span().start(), Some(&Marker::new(0, 431, 24, 3))); 1470 | assert_eq!(seq.span(), map.get_node("heterogenous").unwrap().span()); 1471 | // Helpers for the sequence node 1472 | assert_eq!(seq.get_node(0), seq.first()); 1473 | assert_ne!(seq.get_node(0), None); 1474 | assert_ne!(seq.get_scalar(0), None); 1475 | assert_ne!(seq.get_mapping(1), None); 1476 | assert_ne!(seq.get_sequence(2), None); 1477 | } 1478 | 1479 | #[test] 1480 | fn basic_scalar_features() { 1481 | let scalar1 = MarkedScalarNode::new(Span::new_blank(), ""); 1482 | let scalar2 = MarkedScalarNode::new_empty(Span::new_blank()); 1483 | assert_eq!(scalar1, scalar2); 1484 | assert_eq!(scalar1.as_str(), ""); 1485 | assert_eq!(scalar1.as_bool(), None); 1486 | assert_eq!(scalar1.as_usize(), None); 1487 | let truth: MarkedScalarNode = "true".into(); 1488 | assert_eq!(truth.as_bool(), Some(true)); 1489 | let falsehood: MarkedScalarNode = "false".to_string().into(); 1490 | assert_eq!(falsehood.as_bool(), Some(false)); 1491 | assert_eq!(truth, true.into()); 1492 | assert_eq!(falsehood, false.into()); 1493 | let zero: MarkedScalarNode = "0".into(); 1494 | assert_eq!(zero.as_usize(), Some(0)); 1495 | assert_eq!(zero, 0usize.into()); 1496 | assert_eq!(&*zero, "0"); 1497 | } 1498 | 1499 | #[test] 1500 | fn basic_sequence_features() { 1501 | // For features not covered by other tests 1502 | let mut seq = MarkedSequenceNode::new_empty(Span::new_blank()); 1503 | let seq2: MarkedSequenceNode = vec!["foo"].into_iter().collect(); 1504 | let scalar: MarkedScalarNode = "foo".into(); 1505 | seq.push(Node::from(scalar)); 1506 | assert_eq!(seq, seq2); 1507 | assert_eq!(seq, vec!["foo"].into()); 1508 | let seq3: MarkedSequenceNode = vec!["foo", "bar"].into_iter().collect(); 1509 | seq.push(Node::from("bar")); 1510 | assert_eq!(seq, seq3); 1511 | assert_eq!(seq, vec!["foo", "bar"].into()); 1512 | } 1513 | 1514 | #[test] 1515 | fn basic_mapping_features() { 1516 | // For features not covered by other tests 1517 | let mut map = MarkedMappingNode::new_empty(Span::new_blank()); 1518 | let mut hash = MappingHash::new(); 1519 | hash.insert("foo".into(), "bar".into()); 1520 | let map2 = MarkedMappingNode::from(hash); 1521 | map.insert("foo".into(), "bar".into()); 1522 | assert_eq!(map, map2); 1523 | assert_eq!(map.get("foo").unwrap().as_scalar().unwrap().as_str(), "bar"); 1524 | let map3: MarkedMappingNode = vec![("foo", "bar")].into_iter().collect(); 1525 | assert_eq!(map, map3); 1526 | map.insert("baz".into(), "meta".into()); 1527 | let map4: MarkedMappingNode = vec![("foo", "bar"), ("baz", "meta")].into_iter().collect(); 1528 | assert_eq!(map, map4); 1529 | } 1530 | 1531 | #[test] 1532 | fn extra_node_impls() { 1533 | let node = Node::from(vec!["foo"]); 1534 | assert_ne!(node.as_sequence(), None); 1535 | let node = Node::from(MappingHash::new()); 1536 | assert!(node.as_mapping().unwrap().is_empty()); 1537 | } 1538 | 1539 | #[test] 1540 | fn yaml_conversions() { 1541 | use yaml_rust::YamlLoader; 1542 | let mut everything = 1543 | YamlLoader::load_from_str(include_str!("../examples/everything.yaml")).unwrap(); 1544 | let everything = everything.pop().unwrap(); 1545 | let node = Node::try_from(everything.clone()).unwrap(); 1546 | assert!(node.as_mapping().is_some()); 1547 | let badscalar = MarkedScalarNode::try_from(everything); 1548 | assert_eq!(badscalar, Err(YamlConversionError::NonScalar)); 1549 | let badscalar = MarkedScalarNode::try_from(YamlNode::BadValue); 1550 | assert_eq!(badscalar, Err(YamlConversionError::BadValue)); 1551 | let badscalar = MarkedScalarNode::try_from(YamlNode::Array(vec![])); 1552 | assert_eq!(badscalar, Err(YamlConversionError::NonScalar)); 1553 | } 1554 | 1555 | fn flatten(node: YamlNode) -> YamlNode { 1556 | match node { 1557 | YamlNode::Array(arr) => YamlNode::Array(arr.into_iter().map(flatten).collect()), 1558 | YamlNode::Boolean(b) => { 1559 | YamlNode::String((if b { "true" } else { "false" }).to_string()) 1560 | } 1561 | YamlNode::Hash(h) => YamlNode::Hash( 1562 | h.into_iter() 1563 | .map(|(k, v)| (flatten(k), flatten(v))) 1564 | .collect(), 1565 | ), 1566 | YamlNode::Integer(i) => YamlNode::String(format!("{}", i)), 1567 | YamlNode::Null => YamlNode::String("null".to_string()), 1568 | YamlNode::Real(r) => YamlNode::String(r), 1569 | other => other, 1570 | } 1571 | } 1572 | 1573 | #[test] 1574 | fn back_yaml_conversion() { 1575 | use yaml_rust::YamlLoader; 1576 | let mut everything = 1577 | YamlLoader::load_from_str(include_str!("../examples/everything.yaml")).unwrap(); 1578 | let everything = everything.pop().unwrap(); 1579 | let node = Node::try_from(everything.clone()).unwrap(); 1580 | let other: YamlNode = node.into(); 1581 | let flat = flatten(everything); 1582 | assert_eq!(flat, other); 1583 | } 1584 | } 1585 | -------------------------------------------------------------------------------- /marked-yaml/src/spanned_serde.rs: -------------------------------------------------------------------------------- 1 | //! Serde support for marked data deserialisation 2 | 3 | use std::{ 4 | borrow::Borrow, 5 | fmt, 6 | hash::Hash, 7 | iter::Peekable, 8 | marker::PhantomData, 9 | num::{ParseFloatError, ParseIntError}, 10 | ops::Deref, 11 | }; 12 | 13 | use serde::{ 14 | de::{ 15 | value::BorrowedStrDeserializer, DeserializeOwned, EnumAccess, IntoDeserializer, MapAccess, 16 | SeqAccess, Unexpected, VariantAccess, Visitor, 17 | }, 18 | forward_to_deserialize_any, Deserialize, Deserializer, Serialize, 19 | }; 20 | 21 | use crate::{ 22 | types::{MarkedMappingNode, MarkedScalarNode, MarkedSequenceNode}, 23 | LoaderOptions, Marker, Node, Span, 24 | }; 25 | 26 | /// Wrapper which can be used when deserialising data from [`Node`] 27 | /// 28 | /// You must use a compatible deserializer if you want to deserialize these values. 29 | /// 30 | /// ``` 31 | /// use marked_yaml::{from_yaml, Spanned}; 32 | /// use serde::Deserialize; 33 | /// 34 | /// #[derive(Deserialize)] 35 | /// struct MyStuff { 36 | /// num: Spanned, 37 | /// } 38 | /// let stuff: MyStuff = from_yaml(0, "num: 12").unwrap(); 39 | /// ``` 40 | /// 41 | /// You can also serialize these values, 42 | /// however when serializing you will lose the span information so do not expect 43 | /// to round-trip these values. 44 | #[derive(Clone, Debug)] 45 | pub struct Spanned { 46 | span: Span, 47 | inner: T, 48 | } 49 | 50 | impl Spanned { 51 | /// Wrap an instance of something with the given span 52 | /// 53 | /// ``` 54 | /// # use marked_yaml::{Spanned, Span}; 55 | /// let spanned = Spanned::new(Span::new_blank(), "Hello World"); 56 | /// ``` 57 | pub fn new(span: Span, inner: T) -> Self { 58 | Self { span, inner } 59 | } 60 | 61 | /// The span associated with this value 62 | /// 63 | /// ``` 64 | /// # use marked_yaml::{Spanned, Span, Marker}; 65 | /// # let span = Span::new_start(Marker::new(0, 1, 1, 2)); 66 | /// let spanned = Spanned::new(span, "Hello World"); 67 | /// assert_eq!(spanned.span(), &span); 68 | /// ``` 69 | pub fn span(&self) -> &Span { 70 | &self.span 71 | } 72 | } 73 | 74 | impl Deref for Spanned { 75 | type Target = T; 76 | 77 | fn deref(&self) -> &Self::Target { 78 | &self.inner 79 | } 80 | } 81 | 82 | impl PartialEq for Spanned 83 | where 84 | T: PartialEq, 85 | { 86 | fn eq(&self, other: &Self) -> bool { 87 | self.inner == other.inner 88 | } 89 | } 90 | 91 | impl PartialEq for Spanned 92 | where 93 | T: PartialEq, 94 | { 95 | fn eq(&self, other: &T) -> bool { 96 | (&self.inner as &dyn PartialEq).eq(other) 97 | } 98 | } 99 | 100 | impl PartialEq<&str> for Spanned { 101 | fn eq(&self, other: &&str) -> bool { 102 | self.inner == *other 103 | } 104 | } 105 | 106 | impl Eq for Spanned where T: Eq {} 107 | 108 | impl Hash for Spanned 109 | where 110 | T: Hash, 111 | { 112 | fn hash(&self, state: &mut H) { 113 | self.inner.hash(state); 114 | } 115 | } 116 | 117 | impl Borrow for Spanned { 118 | fn borrow(&self) -> &str { 119 | self.inner.borrow() 120 | } 121 | } 122 | 123 | impl Borrow for Spanned<&'_ str> { 124 | fn borrow(&self) -> &str { 125 | self.inner 126 | } 127 | } 128 | 129 | // ------------------------------------------------------------------------------- 130 | 131 | // Convention for these markers comes from the toml crates 132 | 133 | const SPANNED_TYPE: &str = "$___::marked_data::serde::Spanned"; 134 | const SPANNED_SPAN_START_SOURCE: &str = "$___::marked_data::serde::Spanned::span_start_source"; 135 | const SPANNED_SPAN_START_CHARACTER: &str = "$___::marked_data::serde::Spanned::span_start_char"; 136 | const SPANNED_SPAN_START_LINE: &str = "$___::marked_data::serde::Spanned::span_start_line"; 137 | const SPANNED_SPAN_START_COLUMN: &str = "$___::marked_data::serde::Spanned::span_start_column"; 138 | const SPANNED_SPAN_END_SOURCE: &str = "$___::marked_data::serde::Spanned::span_end_source"; 139 | const SPANNED_SPAN_END_CHARACTER: &str = "$___::marked_data::serde::Spanned::span_end_char"; 140 | const SPANNED_SPAN_END_LINE: &str = "$___::marked_data::serde::Spanned::span_end_line"; 141 | const SPANNED_SPAN_END_COLUMN: &str = "$___::marked_data::serde::Spanned::span_end_column"; 142 | const SPANNED_INNER: &str = "$___::marked_data::serde::Spanned::inner"; 143 | 144 | const SPANNED_FIELDS: [&str; 9] = [ 145 | SPANNED_SPAN_START_SOURCE, 146 | SPANNED_SPAN_START_CHARACTER, 147 | SPANNED_SPAN_START_LINE, 148 | SPANNED_SPAN_START_COLUMN, 149 | SPANNED_SPAN_END_SOURCE, 150 | SPANNED_SPAN_END_CHARACTER, 151 | SPANNED_SPAN_END_LINE, 152 | SPANNED_SPAN_END_COLUMN, 153 | SPANNED_INNER, 154 | ]; 155 | 156 | impl<'de, T> Deserialize<'de> for Spanned 157 | where 158 | T: Deserialize<'de>, 159 | { 160 | fn deserialize(deserializer: D) -> Result 161 | where 162 | D: serde::Deserializer<'de>, 163 | { 164 | struct MarkedNodeVisitor(PhantomData); 165 | 166 | impl<'de, T> Visitor<'de> for MarkedNodeVisitor 167 | where 168 | T: Deserialize<'de>, 169 | { 170 | type Value = Spanned; 171 | 172 | fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 173 | write!(f, "a verbatim marked_yaml::Node") 174 | } 175 | 176 | fn visit_map(self, mut visitor: V) -> Result 177 | where 178 | V: MapAccess<'de>, 179 | { 180 | let mut key: Option<&str> = visitor.next_key()?; 181 | 182 | let span_start = if key == Some(SPANNED_SPAN_START_SOURCE) { 183 | let source: usize = visitor.next_value()?; 184 | if visitor.next_key()? != Some(SPANNED_SPAN_START_CHARACTER) { 185 | return Err(serde::de::Error::custom( 186 | "marked node span start character missing", 187 | )); 188 | } 189 | let character: usize = visitor.next_value()?; 190 | if visitor.next_key()? != Some(SPANNED_SPAN_START_LINE) { 191 | return Err(serde::de::Error::custom( 192 | "marked node span start line missing", 193 | )); 194 | } 195 | let line: usize = visitor.next_value()?; 196 | if visitor.next_key()? != Some(SPANNED_SPAN_START_COLUMN) { 197 | return Err(serde::de::Error::custom( 198 | "marked node span start column missing", 199 | )); 200 | } 201 | let column: usize = visitor.next_value()?; 202 | key = visitor.next_key()?; 203 | Some(Marker::new(source, character, line, column)) 204 | } else { 205 | None 206 | }; 207 | 208 | let span_end = if key == Some(SPANNED_SPAN_END_SOURCE) { 209 | let source: usize = visitor.next_value()?; 210 | if visitor.next_key()? != Some(SPANNED_SPAN_END_CHARACTER) { 211 | return Err(serde::de::Error::custom( 212 | "marked node span end character missing", 213 | )); 214 | } 215 | let character: usize = visitor.next_value()?; 216 | if visitor.next_key()? != Some(SPANNED_SPAN_END_LINE) { 217 | return Err(serde::de::Error::custom( 218 | "marked node span end line missing", 219 | )); 220 | } 221 | let line: usize = visitor.next_value()?; 222 | if visitor.next_key()? != Some(SPANNED_SPAN_END_COLUMN) { 223 | return Err(serde::de::Error::custom( 224 | "marked node span end column missing", 225 | )); 226 | } 227 | let column: usize = visitor.next_value()?; 228 | key = visitor.next_key()?; 229 | Some(Marker::new(source, character, line, column)) 230 | } else { 231 | None 232 | }; 233 | 234 | if key != Some(SPANNED_INNER) { 235 | return Err(serde::de::Error::custom( 236 | "marked node inner value not found", 237 | )); 238 | } 239 | let inner: T = visitor.next_value()?; 240 | 241 | let mut span = Span::new_blank(); 242 | span.set_start(span_start); 243 | span.set_end(span_end); 244 | 245 | Ok(Spanned::new(span, inner)) 246 | } 247 | } 248 | let visitor = MarkedNodeVisitor(PhantomData); 249 | 250 | deserializer.deserialize_struct(SPANNED_TYPE, &SPANNED_FIELDS, visitor) 251 | } 252 | } 253 | 254 | impl Serialize for Spanned 255 | where 256 | T: Serialize, 257 | { 258 | fn serialize(&self, serializer: S) -> Result 259 | where 260 | S: serde::Serializer, 261 | { 262 | self.inner.serialize(serializer) 263 | } 264 | } 265 | 266 | #[cfg(test)] 267 | mod spanned_tests { 268 | use super::Spanned; 269 | use serde::{forward_to_deserialize_any, Deserialize, Deserializer}; 270 | 271 | #[test] 272 | fn spanned_always_map() { 273 | struct NotSpanned; 274 | impl<'de> Deserializer<'de> for NotSpanned { 275 | type Error = super::Error; 276 | 277 | fn deserialize_any(self, visitor: V) -> Result 278 | where 279 | V: serde::de::Visitor<'de>, 280 | { 281 | visitor.visit_bool(false) 282 | } 283 | 284 | forward_to_deserialize_any! [ 285 | bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes byte_buf 286 | unit unit_struct newtype_struct seq tuple tuple_struct 287 | map identifier ignored_any option struct enum 288 | ]; 289 | } 290 | type T = Spanned; 291 | assert!(T::deserialize(NotSpanned).is_err()); 292 | } 293 | } 294 | 295 | // ------------------------------------------------------------------------------- 296 | 297 | /// Errors which can come from deserialisation 298 | #[derive(Debug)] 299 | pub enum Error { 300 | /// The value was not a valid boolean 301 | NotBoolean(Span), 302 | /// Failed to parse integer 303 | IntegerParseFailure(ParseIntError, Span), 304 | /// Failed to parse float 305 | FloatParseFailure(ParseFloatError, Span), 306 | /// An unknown field was encountered 307 | UnknownFieldError(String, &'static [&'static str], Span), 308 | /// Some other error occurred 309 | Other(String, Span), 310 | } 311 | 312 | impl Error { 313 | fn set_span(&mut self, span: Span) { 314 | let spanloc = match self { 315 | Error::NotBoolean(s) => s, 316 | Error::IntegerParseFailure(_, s) => s, 317 | Error::FloatParseFailure(_, s) => s, 318 | Error::UnknownFieldError(_, _, s) => s, 319 | Error::Other(_, s) => s, 320 | }; 321 | *spanloc = span; 322 | } 323 | 324 | /// Retrieve the start marker if there is one 325 | /// 326 | /// Most spans which are generated by the loader only have start 327 | /// marks (containers have end marks as well, but these failures) 328 | /// are unlikely to exist here. 329 | /// 330 | /// ``` 331 | /// # use marked_yaml::*; 332 | /// # use serde::Deserialize; 333 | /// const YAML: &str = r#" 334 | /// bad: float 335 | /// "#; 336 | /// 337 | /// #[derive(Deserialize)] 338 | /// struct Example { 339 | /// bad: Spanned, 340 | /// } 341 | /// 342 | /// let nodes = parse_yaml(0, YAML).unwrap(); 343 | /// let err = from_node::(&nodes).err().unwrap(); 344 | /// 345 | /// assert!(matches!(&*err, Error::FloatParseFailure(_,_))); 346 | /// 347 | /// let mark = err.start_mark().unwrap(); 348 | /// 349 | /// assert_eq!(mark.source(), 0); 350 | /// assert_eq!(mark.line(), 2); 351 | /// assert_eq!(mark.column(), 6); 352 | /// ``` 353 | pub fn start_mark(&self) -> Option { 354 | let spanloc = match self { 355 | Error::NotBoolean(s) => s, 356 | Error::IntegerParseFailure(_, s) => s, 357 | Error::FloatParseFailure(_, s) => s, 358 | Error::UnknownFieldError(_, _, s) => s, 359 | Error::Other(_, s) => s, 360 | }; 361 | spanloc.start().copied() 362 | } 363 | } 364 | 365 | impl fmt::Display for Error { 366 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 367 | match self { 368 | Error::NotBoolean(_) => f.write_str("Value was not a boolean"), 369 | Error::IntegerParseFailure(e, _) => e.fmt(f), 370 | Error::FloatParseFailure(e, _) => e.fmt(f), 371 | Error::UnknownFieldError(field, expected, _) => match expected.len() { 372 | 0 => write!(f, "Unknown field `{field}`, there are no fields"), 373 | 1 => write!(f, "Unknown field `{field}`, expected `{}`", expected[0]), 374 | 2 => write!( 375 | f, 376 | "Unknown field `{field}`, expected `{}` or `{}`", 377 | expected[0], expected[1] 378 | ), 379 | _ => { 380 | write!(f, "Unknown field `{field}`, expected one of ")?; 381 | let last = expected[expected.len() - 1]; 382 | for v in expected[..=expected.len() - 2].iter() { 383 | write!(f, "`{v}`, ")?; 384 | } 385 | write!(f, "or `{last}`") 386 | } 387 | }, 388 | Error::Other(e, _) => e.fmt(f), 389 | } 390 | } 391 | } 392 | 393 | impl std::error::Error for Error {} 394 | 395 | impl serde::de::Error for Error { 396 | fn custom(msg: T) -> Self 397 | where 398 | T: fmt::Display, 399 | { 400 | Error::Other(msg.to_string(), Span::new_blank()) 401 | } 402 | 403 | fn unknown_field(field: &str, expected: &'static [&'static str]) -> Self { 404 | Self::UnknownFieldError(field.to_string(), expected, Span::new_blank()) 405 | } 406 | } 407 | 408 | impl From for Error { 409 | fn from(value: ParseIntError) -> Self { 410 | Error::IntegerParseFailure(value, Span::new_blank()) 411 | } 412 | } 413 | 414 | impl From for Error { 415 | fn from(value: ParseFloatError) -> Self { 416 | Error::FloatParseFailure(value, Span::new_blank()) 417 | } 418 | } 419 | 420 | trait AddSpans { 421 | fn addspans(self, span: Span) -> Result; 422 | } 423 | 424 | impl AddSpans for Result 425 | where 426 | E: Into, 427 | { 428 | fn addspans(self, span: Span) -> Result { 429 | self.map_err(|e| { 430 | let mut e: Error = e.into(); 431 | e.set_span(span); 432 | e 433 | }) 434 | } 435 | } 436 | 437 | // ------------------------------------------------------------------------------- 438 | 439 | impl<'de> IntoDeserializer<'de, Error> for &'de Node { 440 | type Deserializer = NodeDeserializer<'de>; 441 | 442 | fn into_deserializer(self) -> Self::Deserializer { 443 | NodeDeserializer { node: self } 444 | } 445 | } 446 | 447 | /// Deserializer for nodes 448 | pub struct NodeDeserializer<'node> { 449 | node: &'node Node, 450 | } 451 | 452 | impl<'node> NodeDeserializer<'node> { 453 | /// Create a new deserializer over a borrowed node 454 | pub fn new(node: &'node Node) -> Self { 455 | Self { node } 456 | } 457 | } 458 | 459 | // ------------------------------------------------------------------------------- 460 | 461 | /// The error returned by [`from_node`] 462 | /// 463 | /// From here you can get the logical path to the error if 464 | /// one is available, and then via the error: the marker 465 | /// indicating where the error occurred if it's available. 466 | /// Finally you may extract the error itself. 467 | #[derive(Debug)] 468 | pub struct FromNodeError { 469 | error: Box, 470 | path: Option, 471 | } 472 | 473 | impl FromNodeError { 474 | #[cfg_attr(docsrs, doc(cfg(feature = "serde-path")))] 475 | /// The logical path representing where the error occurred. 476 | /// 477 | /// Note: this likely will only ever be `Some(value)` if 478 | /// you enable the `serde-path` feature, though the function 479 | /// itself will be available with the `serde` feature. 480 | pub fn path(&self) -> Option<&str> { 481 | self.path.as_deref() 482 | } 483 | 484 | /// Extract the inner error 485 | pub fn into_inner(self) -> Error { 486 | *self.error 487 | } 488 | } 489 | 490 | impl Deref for FromNodeError { 491 | type Target = Error; 492 | 493 | fn deref(&self) -> &Self::Target { 494 | &self.error 495 | } 496 | } 497 | 498 | impl fmt::Display for FromNodeError { 499 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 500 | if let Some(path) = self.path() { 501 | write!(f, "{}: {}", path, self.error) 502 | } else { 503 | (&self.error as &dyn fmt::Display).fmt(f) 504 | } 505 | } 506 | } 507 | 508 | // ------------------------------------------------------------------------------- 509 | 510 | /// Errors which can occur when deserialising from YAML 511 | #[derive(Debug)] 512 | pub enum FromYamlError { 513 | /// A problem was encountered when parsing YAML 514 | ParseYaml(crate::LoadError), 515 | /// A problem was encountered when deserializing from nodes 516 | FromNode(FromNodeError), 517 | } 518 | 519 | impl fmt::Display for FromYamlError { 520 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 521 | match self { 522 | FromYamlError::ParseYaml(e) => write!(f, "{e}"), 523 | FromYamlError::FromNode(e) => write!(f, "{e}"), 524 | } 525 | } 526 | } 527 | 528 | impl std::error::Error for FromYamlError {} 529 | 530 | impl From for FromYamlError { 531 | fn from(value: crate::LoadError) -> Self { 532 | Self::ParseYaml(value) 533 | } 534 | } 535 | 536 | impl From for FromYamlError { 537 | fn from(value: FromNodeError) -> Self { 538 | Self::FromNode(value) 539 | } 540 | } 541 | 542 | // ------------------------------------------------------------------------------- 543 | 544 | /// Deserialize some YAML into the requisite type 545 | /// 546 | /// This permits deserialisation of a YAML string into 547 | /// any structure which [`serde`] can deserialize. In 548 | /// addition, if any part of the type tree is [`Spanned`] 549 | /// then the spans are provided from the requisite marked 550 | /// node. 551 | /// 552 | /// ``` 553 | /// # use serde::Deserialize; 554 | /// # use marked_yaml::Spanned; 555 | /// const YAML: &str = "hello: world\n"; 556 | /// #[derive(Deserialize)] 557 | /// struct Greeting { 558 | /// hello: Spanned, 559 | /// } 560 | /// let greets: Greeting = marked_yaml::from_yaml(0, YAML).unwrap(); 561 | /// let start = greets.hello.span().start().unwrap(); 562 | /// assert_eq!(start.line(), 1); 563 | /// assert_eq!(start.column(), 8); 564 | /// ``` 565 | #[allow(clippy::result_large_err)] 566 | pub fn from_yaml(source: usize, yaml: &str) -> Result 567 | where 568 | T: DeserializeOwned, 569 | { 570 | from_yaml_with_options(source, yaml, LoaderOptions::default()) 571 | } 572 | 573 | /// Deserialize some YAML into the requisite type 574 | /// 575 | /// This permits deserialisation of a YAML string into 576 | /// any structure which [`serde`] can deserialize. In 577 | /// addition, if any part of the type tree is [`Spanned`] 578 | /// then the spans are provided from the requisite marked 579 | /// node. 580 | /// 581 | /// ``` 582 | /// # use serde::Deserialize; 583 | /// # use marked_yaml::{Spanned, LoaderOptions}; 584 | /// const YAML: &str = "hello: world\n"; 585 | /// #[derive(Deserialize)] 586 | /// struct Greeting { 587 | /// hello: Spanned, 588 | /// } 589 | /// let greets: Greeting = marked_yaml::from_yaml_with_options(0, YAML, LoaderOptions::default()).unwrap(); 590 | /// let start = greets.hello.span().start().unwrap(); 591 | /// assert_eq!(start.line(), 1); 592 | /// assert_eq!(start.column(), 8); 593 | /// ``` 594 | #[allow(clippy::result_large_err)] 595 | pub fn from_yaml_with_options( 596 | source: usize, 597 | yaml: &str, 598 | options: LoaderOptions, 599 | ) -> Result 600 | where 601 | T: DeserializeOwned, 602 | { 603 | let node = crate::parse_yaml_with_options(source, yaml, options)?; 604 | Ok(from_node(&node)?) 605 | } 606 | 607 | // ------------------------------------------------------------------------------- 608 | 609 | /// Deserialize some [`Node`] into the requisite type 610 | /// 611 | /// This permits deserialisation of [`Node`]s into any structure 612 | /// which [`serde`] can deserialize. In addition, if any part of 613 | /// the type tree is [`Spanned`] then the spans are provided 614 | /// from the requisite marked node. 615 | /// 616 | /// ``` 617 | /// # use serde::Deserialize; 618 | /// # use marked_yaml::Spanned; 619 | /// const YAML: &str = "hello: world\n"; 620 | /// let node = marked_yaml::parse_yaml(0, YAML).unwrap(); 621 | /// #[derive(Deserialize)] 622 | /// struct Greeting { 623 | /// hello: Spanned, 624 | /// } 625 | /// let greets: Greeting = marked_yaml::from_node(&node).unwrap(); 626 | /// let start = greets.hello.span().start().unwrap(); 627 | /// assert_eq!(start.line(), 1); 628 | /// assert_eq!(start.column(), 8); 629 | /// ``` 630 | #[allow(clippy::result_large_err)] 631 | pub fn from_node<'de, T>(node: &'de Node) -> Result 632 | where 633 | T: Deserialize<'de>, 634 | { 635 | #[cfg(not(feature = "serde-path"))] 636 | { 637 | T::deserialize(NodeDeserializer::new(node)).map_err(|e| FromNodeError { 638 | error: Box::new(e), 639 | path: None, 640 | }) 641 | } 642 | 643 | #[cfg(feature = "serde-path")] 644 | { 645 | use serde_path_to_error::Segment; 646 | 647 | let p2e: Result = serde_path_to_error::deserialize(NodeDeserializer::new(node)); 648 | 649 | p2e.map_err(|e| { 650 | if e.inner().start_mark().is_none() { 651 | let p = e.path().clone(); 652 | let path = render_path(&p); 653 | let mut e = e.into_inner(); 654 | let mut prev_best_node = node; 655 | let mut best_node = node; 656 | for seg in p.iter() { 657 | match seg { 658 | Segment::Seq { index } => { 659 | if let Some(seq) = best_node.as_sequence() { 660 | if let Some(node) = seq.get(*index) { 661 | prev_best_node = best_node; 662 | best_node = node; 663 | } else { 664 | // We can't traverse this? 665 | break; 666 | } 667 | } else { 668 | // We can't traverse this? 669 | break; 670 | } 671 | } 672 | Segment::Map { key } => { 673 | if let Some(map) = best_node.as_mapping() { 674 | // What we want here is the entry which matches the key 675 | // if there is one 676 | if let Some(node) = map.get(key.as_str()) { 677 | prev_best_node = best_node; 678 | best_node = node; 679 | } else { 680 | // We can't traverse this? 681 | break; 682 | } 683 | } else { 684 | // We can't traverse this? 685 | break; 686 | } 687 | } 688 | Segment::Enum { .. } => break, 689 | Segment::Unknown => break, 690 | } 691 | } 692 | let mut best_span = *best_node.span(); 693 | if let Error::UnknownFieldError(field, _, _) = &e { 694 | // We actually would prefer to point at the key not the value, 695 | if let Some(map) = prev_best_node.as_mapping() { 696 | for (k, _) in map.iter() { 697 | if k.as_str() == field.as_str() { 698 | best_span = *k.span(); 699 | break; 700 | } 701 | } 702 | } 703 | } 704 | e.set_span(best_span); 705 | FromNodeError { 706 | error: Box::new(e), 707 | path, 708 | } 709 | } else { 710 | let path = render_path(e.path()); 711 | FromNodeError { 712 | error: Box::new(e.into_inner()), 713 | path, 714 | } 715 | } 716 | }) 717 | } 718 | } 719 | 720 | #[cfg(feature = "serde-path")] 721 | fn render_path(path: &serde_path_to_error::Path) -> Option { 722 | use serde_path_to_error::Segment::*; 723 | use std::fmt::Write; 724 | let mut ret = String::new(); 725 | let mut separator = ""; 726 | for segment in path.iter() { 727 | if let Map { key } = segment { 728 | if key == SPANNED_INNER { 729 | continue; 730 | } 731 | } 732 | if !matches!(segment, Seq { .. }) { 733 | write!(ret, "{separator}{segment}").expect("Cannot format"); 734 | } else { 735 | write!(ret, "{segment}").expect("Cannot format"); 736 | } 737 | separator = "."; 738 | } 739 | if ret.is_empty() { 740 | None 741 | } else { 742 | Some(ret) 743 | } 744 | } 745 | 746 | // ------------------------------------------------------------------------------- 747 | 748 | macro_rules! forward_to_nodes { 749 | () => { 750 | forward_to_nodes! [ 751 | deserialize_any() 752 | deserialize_bool() 753 | deserialize_i8() 754 | deserialize_i16() 755 | deserialize_i32() 756 | deserialize_i64() 757 | deserialize_i128() 758 | deserialize_u8() 759 | deserialize_u16() 760 | deserialize_u32() 761 | deserialize_u64() 762 | deserialize_u128() 763 | deserialize_f32() 764 | deserialize_f64() 765 | deserialize_char() 766 | deserialize_str() 767 | deserialize_string() 768 | deserialize_bytes() 769 | deserialize_byte_buf() 770 | deserialize_option() 771 | deserialize_unit() 772 | deserialize_unit_struct(name: &'static str) 773 | deserialize_newtype_struct(name: &'static str) 774 | deserialize_seq() 775 | deserialize_tuple(len: usize) 776 | deserialize_tuple_struct(name: &'static str, len: usize) 777 | deserialize_map() 778 | deserialize_struct(name: &'static str, fields: &'static [&'static str]) 779 | deserialize_enum(name: &'static str, variants: &'static [&'static str]) 780 | deserialize_identifier() 781 | deserialize_ignored_any() 782 | ]; 783 | }; 784 | 785 | ($($meth:ident($($arg:ident: $ty:ty),*))*) => { 786 | $( 787 | fn $meth(self, $($arg: $ty,)* visitor: V) -> Result 788 | where 789 | V: Visitor<'de>, 790 | { 791 | match self.node { 792 | Node::Scalar(s) => s 793 | .into_deserializer() 794 | .$meth($($arg,)* visitor), 795 | Node::Mapping(m) => m 796 | .into_deserializer() 797 | .$meth($($arg,)* visitor), 798 | Node::Sequence(s) => s 799 | .into_deserializer() 800 | .$meth($($arg,)* visitor), 801 | } 802 | } 803 | )* 804 | }; 805 | } 806 | 807 | impl<'de> Deserializer<'de> for NodeDeserializer<'de> { 808 | type Error = Error; 809 | 810 | forward_to_nodes!(); 811 | } 812 | 813 | // ------------------------------------------------------------------------------- 814 | 815 | trait MarkedValue { 816 | fn mark_span(&self) -> &Span; 817 | } 818 | 819 | impl MarkedValue for MarkedScalarNode { 820 | fn mark_span(&self) -> &Span { 821 | self.span() 822 | } 823 | } 824 | 825 | impl MarkedValue for MarkedMappingNode { 826 | fn mark_span(&self) -> &Span { 827 | self.span() 828 | } 829 | } 830 | 831 | impl MarkedValue for MarkedSequenceNode { 832 | fn mark_span(&self) -> &Span { 833 | self.span() 834 | } 835 | } 836 | 837 | impl MarkedValue for Node { 838 | fn mark_span(&self) -> &Span { 839 | self.span() 840 | } 841 | } 842 | 843 | // ------------------------------------------------------------------------------- 844 | 845 | struct SpannedDeserializer<'de, T> { 846 | node: &'de T, 847 | state: SpannedDeserializerState, 848 | } 849 | 850 | enum SpannedDeserializerState { 851 | SendStartSource, 852 | SendStartCharacter, 853 | SendStartLine, 854 | SendStartColumn, 855 | SendEndSource, 856 | SendEndCharacter, 857 | SendEndLine, 858 | SendEndColumn, 859 | SendValue, 860 | Done, 861 | } 862 | 863 | impl<'de, T> SpannedDeserializer<'de, T> 864 | where 865 | T: MarkedValue, 866 | { 867 | fn new(node: &'de T) -> Self { 868 | let state = if node.mark_span().start().is_some() { 869 | SpannedDeserializerState::SendStartSource 870 | } else if node.mark_span().end().is_some() { 871 | SpannedDeserializerState::SendEndSource 872 | } else { 873 | SpannedDeserializerState::SendValue 874 | }; 875 | Self { node, state } 876 | } 877 | } 878 | 879 | impl<'de, T> MapAccess<'de> for SpannedDeserializer<'de, T> 880 | where 881 | T: MarkedValue, 882 | &'de T: IntoDeserializer<'de, Error>, 883 | { 884 | type Error = Error; 885 | 886 | fn next_key_seed(&mut self, seed: K) -> Result, Self::Error> 887 | where 888 | K: serde::de::DeserializeSeed<'de>, 889 | { 890 | let key = match self.state { 891 | SpannedDeserializerState::SendStartSource => SPANNED_SPAN_START_SOURCE, 892 | SpannedDeserializerState::SendStartCharacter => SPANNED_SPAN_START_CHARACTER, 893 | SpannedDeserializerState::SendStartLine => SPANNED_SPAN_START_LINE, 894 | SpannedDeserializerState::SendStartColumn => SPANNED_SPAN_START_COLUMN, 895 | SpannedDeserializerState::SendEndSource => SPANNED_SPAN_END_SOURCE, 896 | SpannedDeserializerState::SendEndCharacter => SPANNED_SPAN_END_CHARACTER, 897 | SpannedDeserializerState::SendEndLine => SPANNED_SPAN_END_LINE, 898 | SpannedDeserializerState::SendEndColumn => SPANNED_SPAN_END_COLUMN, 899 | SpannedDeserializerState::SendValue => SPANNED_INNER, 900 | SpannedDeserializerState::Done => return Ok(None), 901 | }; 902 | seed.deserialize(BorrowedStrDeserializer::new(key)) 903 | .map(Some) 904 | } 905 | 906 | fn next_value_seed(&mut self, seed: V) -> Result 907 | where 908 | V: serde::de::DeserializeSeed<'de>, 909 | { 910 | match self.state { 911 | SpannedDeserializerState::SendStartSource => { 912 | let v = self 913 | .node 914 | .mark_span() 915 | .start() 916 | .expect("Span missing start") 917 | .source(); 918 | self.state = SpannedDeserializerState::SendStartCharacter; 919 | seed.deserialize(v.into_deserializer()) 920 | } 921 | SpannedDeserializerState::SendStartCharacter => { 922 | let v = self 923 | .node 924 | .mark_span() 925 | .start() 926 | .expect("Span missing start") 927 | .character(); 928 | self.state = SpannedDeserializerState::SendStartLine; 929 | seed.deserialize(v.into_deserializer()) 930 | } 931 | SpannedDeserializerState::SendStartLine => { 932 | let v = self 933 | .node 934 | .mark_span() 935 | .start() 936 | .expect("Span missing start") 937 | .line(); 938 | self.state = SpannedDeserializerState::SendStartColumn; 939 | seed.deserialize(v.into_deserializer()) 940 | } 941 | SpannedDeserializerState::SendStartColumn => { 942 | let v = self 943 | .node 944 | .mark_span() 945 | .start() 946 | .expect("Span missing start") 947 | .column(); 948 | self.state = if self.node.mark_span().end().is_some() { 949 | SpannedDeserializerState::SendEndSource 950 | } else { 951 | SpannedDeserializerState::SendValue 952 | }; 953 | seed.deserialize(v.into_deserializer()) 954 | } 955 | SpannedDeserializerState::SendEndSource => { 956 | let v = self 957 | .node 958 | .mark_span() 959 | .end() 960 | .expect("Span missing end") 961 | .source(); 962 | self.state = SpannedDeserializerState::SendEndCharacter; 963 | seed.deserialize(v.into_deserializer()) 964 | } 965 | SpannedDeserializerState::SendEndCharacter => { 966 | let v = self 967 | .node 968 | .mark_span() 969 | .end() 970 | .expect("Span missing end") 971 | .character(); 972 | self.state = SpannedDeserializerState::SendEndLine; 973 | seed.deserialize(v.into_deserializer()) 974 | } 975 | SpannedDeserializerState::SendEndLine => { 976 | let v = self 977 | .node 978 | .mark_span() 979 | .end() 980 | .expect("Span missing end") 981 | .line(); 982 | self.state = SpannedDeserializerState::SendEndColumn; 983 | seed.deserialize(v.into_deserializer()) 984 | } 985 | SpannedDeserializerState::SendEndColumn => { 986 | let v = self 987 | .node 988 | .mark_span() 989 | .end() 990 | .expect("Span missing end") 991 | .column(); 992 | self.state = SpannedDeserializerState::SendValue; 993 | seed.deserialize(v.into_deserializer()) 994 | } 995 | SpannedDeserializerState::SendValue => { 996 | self.state = SpannedDeserializerState::Done; 997 | seed.deserialize(self.node.into_deserializer()) 998 | } 999 | SpannedDeserializerState::Done => panic!("next_value_seed called before next_key_seed"), 1000 | } 1001 | } 1002 | } 1003 | 1004 | // ------------------------------------------------------------------------------- 1005 | 1006 | impl<'de> IntoDeserializer<'de, Error> for &'de MarkedScalarNode { 1007 | type Deserializer = MarkedScalarNodeDeserializer<'de>; 1008 | fn into_deserializer(self) -> MarkedScalarNodeDeserializer<'de> { 1009 | MarkedScalarNodeDeserializer { node: self } 1010 | } 1011 | } 1012 | 1013 | /// Deserializer for scalar nodes 1014 | pub struct MarkedScalarNodeDeserializer<'node> { 1015 | node: &'node MarkedScalarNode, 1016 | } 1017 | 1018 | macro_rules! scalar_fromstr { 1019 | () => { 1020 | scalar_fromstr!(deserialize_u8 visit_u8 u8); 1021 | scalar_fromstr!(deserialize_u16 visit_u16 u16); 1022 | scalar_fromstr!(deserialize_u32 visit_u32 u32); 1023 | scalar_fromstr!(deserialize_u64 visit_u64 u64); 1024 | scalar_fromstr!(deserialize_u128 visit_u128 u128); 1025 | scalar_fromstr!(deserialize_i8 visit_i8 i8); 1026 | scalar_fromstr!(deserialize_i16 visit_i16 i16); 1027 | scalar_fromstr!(deserialize_i32 visit_i32 i32); 1028 | scalar_fromstr!(deserialize_i64 visit_i64 i64); 1029 | scalar_fromstr!(deserialize_i128 visit_i128 i128); 1030 | scalar_fromstr!(deserialize_f32 visit_f32 f32); 1031 | scalar_fromstr!(deserialize_f64 visit_f64 f64); 1032 | }; 1033 | 1034 | ($meth:ident $visit:ident $ty:ty) => { 1035 | fn $meth(self, visitor: V) -> Result 1036 | where 1037 | V: Visitor<'de>, 1038 | { 1039 | if self.node.may_coerce() { 1040 | let value: $ty = self.node.as_str().parse().addspans(*self.node.span())?; 1041 | visitor.$visit(value) 1042 | } else { 1043 | visitor.visit_str(self.node.deref()) 1044 | } 1045 | } 1046 | }; 1047 | } 1048 | 1049 | impl<'de> Deserializer<'de> for MarkedScalarNodeDeserializer<'de> { 1050 | type Error = Error; 1051 | 1052 | fn deserialize_any(self, visitor: V) -> Result 1053 | where 1054 | V: Visitor<'de>, 1055 | { 1056 | self.node 1057 | .deref() 1058 | .into_deserializer() 1059 | .deserialize_any(visitor) 1060 | } 1061 | 1062 | fn deserialize_bool(self, visitor: V) -> Result 1063 | where 1064 | V: Visitor<'de>, 1065 | { 1066 | visitor.visit_bool( 1067 | self.node 1068 | .as_bool() 1069 | .ok_or(Error::NotBoolean(*self.node.span()))?, 1070 | ) 1071 | } 1072 | 1073 | scalar_fromstr!(); 1074 | 1075 | fn deserialize_struct( 1076 | self, 1077 | name: &'static str, 1078 | fields: &'static [&'static str], 1079 | visitor: V, 1080 | ) -> Result 1081 | where 1082 | V: Visitor<'de>, 1083 | { 1084 | if name == SPANNED_TYPE && fields == SPANNED_FIELDS { 1085 | return visitor.visit_map(SpannedDeserializer::new(self.node)); 1086 | } 1087 | 1088 | if self.node.is_empty_scalar() { 1089 | return visitor.visit_map(EmptyMap); 1090 | } 1091 | 1092 | self.deserialize_any(visitor) 1093 | } 1094 | 1095 | fn deserialize_option(self, visitor: V) -> Result 1096 | where 1097 | V: Visitor<'de>, 1098 | { 1099 | // Since we're here, there is no none, so visit as a some 1100 | visitor.visit_some(self) 1101 | } 1102 | 1103 | fn deserialize_enum( 1104 | self, 1105 | _name: &'static str, 1106 | _variants: &'static [&'static str], 1107 | visitor: V, 1108 | ) -> Result 1109 | where 1110 | V: Visitor<'de>, 1111 | { 1112 | visitor.visit_enum(self.node.as_str().into_deserializer()) 1113 | } 1114 | 1115 | fn deserialize_map(self, visitor: V) -> Result 1116 | where 1117 | V: Visitor<'de>, 1118 | { 1119 | if self.node.is_empty_scalar() { 1120 | return visitor.visit_map(EmptyMap); 1121 | } 1122 | 1123 | self.deserialize_any(visitor) 1124 | } 1125 | 1126 | fn deserialize_seq(self, visitor: V) -> Result 1127 | where 1128 | V: Visitor<'de>, 1129 | { 1130 | if self.node.is_empty_scalar() { 1131 | return visitor.visit_seq(EmptySeq); 1132 | } 1133 | 1134 | self.deserialize_any(visitor) 1135 | } 1136 | 1137 | fn deserialize_unit(self, visitor: V) -> Result 1138 | where 1139 | V: Visitor<'de>, 1140 | { 1141 | if self.node.is_empty_scalar() { 1142 | return visitor.visit_unit(); 1143 | } 1144 | 1145 | self.deserialize_any(visitor) 1146 | } 1147 | 1148 | forward_to_deserialize_any! [ 1149 | char str string bytes byte_buf tuple 1150 | unit_struct newtype_struct tuple_struct 1151 | identifier ignored_any 1152 | ]; 1153 | } 1154 | 1155 | struct EmptyMap; 1156 | 1157 | impl<'de> MapAccess<'de> for EmptyMap { 1158 | type Error = Error; // FUTURE: change to never type once stable 1159 | 1160 | fn next_key_seed(&mut self, _seed: K) -> Result, Self::Error> 1161 | where 1162 | K: serde::de::DeserializeSeed<'de>, 1163 | { 1164 | Ok(None) 1165 | } 1166 | 1167 | fn next_value_seed(&mut self, _seed: V) -> Result 1168 | where 1169 | V: serde::de::DeserializeSeed<'de>, 1170 | { 1171 | unreachable!() 1172 | } 1173 | } 1174 | 1175 | struct EmptySeq; 1176 | 1177 | impl<'de> SeqAccess<'de> for EmptySeq { 1178 | type Error = Error; // FUTURE: change to never type once stable 1179 | 1180 | fn next_element_seed(&mut self, _seed: T) -> Result, Self::Error> 1181 | where 1182 | T: serde::de::DeserializeSeed<'de>, 1183 | { 1184 | Ok(None) 1185 | } 1186 | } 1187 | 1188 | // ------------------------------------------------------------------------------- 1189 | 1190 | type MappingValueSeq<'de> = hashlink::linked_hash_map::Iter<'de, MarkedScalarNode, Node>; 1191 | struct MappingAccess<'de> { 1192 | items: Peekable>, 1193 | } 1194 | 1195 | impl<'de> MappingAccess<'de> { 1196 | fn new(items: MappingValueSeq<'de>) -> Self { 1197 | Self { 1198 | items: items.peekable(), 1199 | } 1200 | } 1201 | } 1202 | 1203 | impl<'de> MapAccess<'de> for MappingAccess<'de> { 1204 | type Error = Error; 1205 | 1206 | fn next_key_seed(&mut self, seed: K) -> Result, Self::Error> 1207 | where 1208 | K: serde::de::DeserializeSeed<'de>, 1209 | { 1210 | if let Some(next_key) = self.items.peek().map(|(k, _v)| k) { 1211 | seed.deserialize(next_key.into_deserializer()).map(Some) 1212 | } else { 1213 | Ok(None) 1214 | } 1215 | } 1216 | 1217 | fn next_value_seed(&mut self, seed: V) -> Result 1218 | where 1219 | V: serde::de::DeserializeSeed<'de>, 1220 | { 1221 | seed.deserialize( 1222 | self.items 1223 | .next() 1224 | .expect("next_value_seed called before next_key_seed") 1225 | .1 1226 | .into_deserializer(), 1227 | ) 1228 | } 1229 | } 1230 | 1231 | // ------------------------------------------------------------------------------- 1232 | 1233 | struct MarkedMappingNodeEnumAccess<'de> { 1234 | node: &'de MarkedMappingNode, 1235 | } 1236 | 1237 | impl<'de> MarkedMappingNodeEnumAccess<'de> { 1238 | fn first(&self) -> &'de Node { 1239 | self.node 1240 | .values() 1241 | .next() 1242 | .expect("variant accessed before variant seed") 1243 | } 1244 | } 1245 | 1246 | impl<'de> EnumAccess<'de> for MarkedMappingNodeEnumAccess<'de> { 1247 | type Error = Error; 1248 | type Variant = Self; 1249 | 1250 | fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error> 1251 | where 1252 | V: serde::de::DeserializeSeed<'de>, 1253 | { 1254 | if let Some(first) = self.node.keys().next() { 1255 | seed.deserialize(first.into_deserializer()) 1256 | .map(|v| (v, self)) 1257 | } else { 1258 | Err(serde::de::Error::custom( 1259 | "Unexpected empty map when looking for enum variant", 1260 | )) 1261 | } 1262 | } 1263 | } 1264 | 1265 | impl<'de> VariantAccess<'de> for MarkedMappingNodeEnumAccess<'de> { 1266 | type Error = Error; 1267 | 1268 | fn unit_variant(self) -> Result<(), Self::Error> { 1269 | Err(serde::de::Error::invalid_type(Unexpected::Map, &"String")) 1270 | } 1271 | 1272 | fn newtype_variant_seed(self, seed: T) -> Result 1273 | where 1274 | T: serde::de::DeserializeSeed<'de>, 1275 | { 1276 | seed.deserialize(self.first().into_deserializer()) 1277 | } 1278 | 1279 | fn tuple_variant(self, _len: usize, visitor: V) -> Result 1280 | where 1281 | V: Visitor<'de>, 1282 | { 1283 | self.first().into_deserializer().deserialize_seq(visitor) 1284 | } 1285 | 1286 | fn struct_variant( 1287 | self, 1288 | _fields: &'static [&'static str], 1289 | visitor: V, 1290 | ) -> Result 1291 | where 1292 | V: Visitor<'de>, 1293 | { 1294 | self.first().into_deserializer().deserialize_map(visitor) 1295 | } 1296 | } 1297 | 1298 | // ------------------------------------------------------------------------------- 1299 | 1300 | impl<'de> IntoDeserializer<'de, Error> for &'de MarkedMappingNode { 1301 | type Deserializer = MarkedMappingNodeDeserializer<'de>; 1302 | 1303 | fn into_deserializer(self) -> Self::Deserializer { 1304 | MarkedMappingNodeDeserializer { node: self } 1305 | } 1306 | } 1307 | 1308 | /// Deserializer for mapping nodes 1309 | pub struct MarkedMappingNodeDeserializer<'de> { 1310 | node: &'de MarkedMappingNode, 1311 | } 1312 | 1313 | impl<'de> Deserializer<'de> for MarkedMappingNodeDeserializer<'de> { 1314 | type Error = Error; 1315 | 1316 | fn deserialize_any(self, visitor: V) -> Result 1317 | where 1318 | V: Visitor<'de>, 1319 | { 1320 | visitor.visit_map(MappingAccess::new(self.node.iter())) 1321 | } 1322 | 1323 | fn deserialize_struct( 1324 | self, 1325 | name: &'static str, 1326 | fields: &'static [&'static str], 1327 | visitor: V, 1328 | ) -> Result 1329 | where 1330 | V: Visitor<'de>, 1331 | { 1332 | if name == SPANNED_TYPE && fields == SPANNED_FIELDS { 1333 | return visitor.visit_map(SpannedDeserializer::new(self.node)); 1334 | } 1335 | 1336 | self.deserialize_any(visitor) 1337 | } 1338 | 1339 | fn deserialize_option(self, visitor: V) -> Result 1340 | where 1341 | V: Visitor<'de>, 1342 | { 1343 | // Since we're here, there is no none, so visit as a some 1344 | visitor.visit_some(self) 1345 | } 1346 | 1347 | fn deserialize_enum( 1348 | self, 1349 | _name: &'static str, 1350 | _variants: &'static [&'static str], 1351 | visitor: V, 1352 | ) -> Result 1353 | where 1354 | V: Visitor<'de>, 1355 | { 1356 | match self.node.len() { 1357 | 0 => Err(serde::de::Error::custom( 1358 | "Expected map with one value, got empty map", 1359 | )), 1360 | 1 => visitor.visit_enum(MarkedMappingNodeEnumAccess { node: self.node }), 1361 | n => Err(serde::de::Error::custom(format!( 1362 | "Expected map with one value, got {n} values" 1363 | ))), 1364 | } 1365 | } 1366 | 1367 | forward_to_deserialize_any! [ 1368 | bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes byte_buf 1369 | unit unit_struct newtype_struct seq tuple tuple_struct 1370 | map identifier ignored_any 1371 | ]; 1372 | } 1373 | 1374 | // ------------------------------------------------------------------------------- 1375 | 1376 | struct SequenceAccess<'de> { 1377 | items: &'de [Node], 1378 | pos: usize, 1379 | } 1380 | 1381 | impl<'de> SequenceAccess<'de> { 1382 | fn new(items: &'de [Node]) -> Self { 1383 | Self { items, pos: 0 } 1384 | } 1385 | } 1386 | 1387 | impl<'de> SeqAccess<'de> for SequenceAccess<'de> { 1388 | type Error = Error; 1389 | 1390 | fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> 1391 | where 1392 | T: serde::de::DeserializeSeed<'de>, 1393 | { 1394 | if self.pos == self.items.len() { 1395 | return Ok(None); 1396 | } 1397 | let pos = self.pos; 1398 | self.pos += 1; 1399 | 1400 | seed.deserialize(self.items[pos].into_deserializer()) 1401 | .map(Some) 1402 | } 1403 | } 1404 | 1405 | // ------------------------------------------------------------------------------- 1406 | 1407 | impl<'de> IntoDeserializer<'de, Error> for &'de MarkedSequenceNode { 1408 | type Deserializer = MarkedSequenceNodeDeserializer<'de>; 1409 | 1410 | fn into_deserializer(self) -> Self::Deserializer { 1411 | MarkedSequenceNodeDeserializer { node: self } 1412 | } 1413 | } 1414 | 1415 | /// Deserializer for sequence nodes 1416 | pub struct MarkedSequenceNodeDeserializer<'de> { 1417 | node: &'de MarkedSequenceNode, 1418 | } 1419 | 1420 | impl<'de> Deserializer<'de> for MarkedSequenceNodeDeserializer<'de> { 1421 | type Error = Error; 1422 | 1423 | fn deserialize_any(self, visitor: V) -> Result 1424 | where 1425 | V: Visitor<'de>, 1426 | { 1427 | visitor.visit_seq(SequenceAccess::new(self.node.as_slice())) 1428 | } 1429 | 1430 | fn deserialize_struct( 1431 | self, 1432 | name: &'static str, 1433 | fields: &'static [&'static str], 1434 | visitor: V, 1435 | ) -> Result 1436 | where 1437 | V: Visitor<'de>, 1438 | { 1439 | if name == SPANNED_TYPE && fields == SPANNED_FIELDS { 1440 | return visitor.visit_map(SpannedDeserializer::new(self.node)); 1441 | } 1442 | 1443 | self.deserialize_any(visitor) 1444 | } 1445 | 1446 | fn deserialize_option(self, visitor: V) -> Result 1447 | where 1448 | V: Visitor<'de>, 1449 | { 1450 | // Since we're here, there is no none, so visit as a some 1451 | visitor.visit_some(self) 1452 | } 1453 | 1454 | forward_to_deserialize_any! [ 1455 | bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes byte_buf 1456 | unit unit_struct newtype_struct seq tuple tuple_struct 1457 | map enum identifier ignored_any 1458 | ]; 1459 | } 1460 | 1461 | // ------------------------------------------------------------------------------- 1462 | 1463 | #[cfg(test)] 1464 | mod test { 1465 | use std::collections::HashMap; 1466 | 1467 | use super::*; 1468 | 1469 | const TEST_DOC: &str = r#"hello: world 1470 | some: [ value, or, other ] 1471 | says: { grow: nothing, or: die } 1472 | numbers: [ 1, 2, 3, 500 ] 1473 | success: true 1474 | failure: False 1475 | shouting: TRUE 1476 | "#; 1477 | 1478 | #[test] 1479 | #[allow(dead_code)] 1480 | fn basic_deserialize() { 1481 | #[derive(Deserialize, Debug)] 1482 | struct TestDoc { 1483 | hello: String, 1484 | some: Vec, 1485 | says: HashMap, 1486 | } 1487 | let node = crate::parse_yaml(0, TEST_DOC).unwrap(); 1488 | let doc: TestDoc = from_node(&node).unwrap(); 1489 | println!("{doc:#?}"); 1490 | } 1491 | 1492 | #[test] 1493 | #[allow(dead_code)] 1494 | fn basic_deserialize_spanned_scalars() { 1495 | #[derive(Deserialize, Debug)] 1496 | struct TestDoc { 1497 | hello: Spanned, 1498 | some: Vec>, 1499 | says: HashMap, Spanned>, 1500 | } 1501 | let node = crate::parse_yaml(0, TEST_DOC).unwrap(); 1502 | let doc: TestDoc = from_node(&node).unwrap(); 1503 | println!("{doc:#?}"); 1504 | } 1505 | 1506 | #[test] 1507 | #[allow(dead_code)] 1508 | fn basic_deserialize_spanned_everything() { 1509 | #[derive(Deserialize, Debug)] 1510 | struct TestDoc { 1511 | hello: Spanned, 1512 | some: Spanned>>, 1513 | says: Spanned, Spanned>>, 1514 | } 1515 | let node = crate::parse_yaml(0, TEST_DOC).unwrap(); 1516 | let doc: Spanned = from_node(&node).unwrap(); 1517 | println!("{doc:#?}"); 1518 | } 1519 | 1520 | #[test] 1521 | #[allow(dead_code)] 1522 | fn basic_deserialize_numbers() { 1523 | #[derive(Deserialize, Debug)] 1524 | struct TestDoc { 1525 | numbers: Vec, 1526 | } 1527 | let node = crate::parse_yaml(0, TEST_DOC).unwrap(); 1528 | let doc: Spanned = from_node(&node).unwrap(); 1529 | println!("{doc:#?}"); 1530 | } 1531 | 1532 | #[test] 1533 | #[allow(dead_code)] 1534 | #[cfg(not(feature = "serde-path"))] 1535 | fn basic_deserialize_bad_numbers() { 1536 | #[derive(Deserialize, Debug)] 1537 | struct TestDoc { 1538 | numbers: Vec, 1539 | } 1540 | let node = crate::parse_yaml(0, TEST_DOC).unwrap(); 1541 | let err = from_node::(&node).err().unwrap().into_inner(); 1542 | match err { 1543 | Error::IntegerParseFailure(_e, s) => { 1544 | let start = s.start().unwrap(); 1545 | assert_eq!(start.source(), 0); 1546 | assert_eq!(start.character(), 93); 1547 | assert_eq!(start.line(), 4); 1548 | assert_eq!(start.column(), 21); 1549 | } 1550 | _ => panic!("Unexpected error"), 1551 | } 1552 | } 1553 | 1554 | #[test] 1555 | #[allow(dead_code)] 1556 | #[cfg(feature = "serde-path")] 1557 | fn basic_deserialize_bad_numbers() { 1558 | #[derive(Deserialize, Debug)] 1559 | struct TestDoc { 1560 | numbers: Vec, 1561 | } 1562 | let node = crate::parse_yaml(0, TEST_DOC).unwrap(); 1563 | let err = from_node::(&node).err().unwrap(); 1564 | assert_eq!(err.path(), Some("numbers[3]")); 1565 | let err = err.into_inner(); 1566 | match err { 1567 | Error::IntegerParseFailure(_e, s) => { 1568 | let start = s.start().unwrap(); 1569 | assert_eq!(start.source(), 0); 1570 | assert_eq!(start.line(), 4); 1571 | assert_eq!(start.column(), 21); 1572 | } 1573 | _ => panic!("Unexpected error"), 1574 | } 1575 | } 1576 | 1577 | #[test] 1578 | #[allow(dead_code)] 1579 | fn basic_deserialize_spanned_numbers() { 1580 | #[derive(Deserialize, Debug)] 1581 | struct TestDoc { 1582 | numbers: Vec>, 1583 | } 1584 | let node = crate::parse_yaml(0, TEST_DOC).unwrap(); 1585 | let doc: Spanned = from_node(&node).unwrap(); 1586 | println!("{doc:#?}"); 1587 | } 1588 | 1589 | #[test] 1590 | #[allow(dead_code)] 1591 | fn basic_deserialize_bools() { 1592 | #[derive(Deserialize, Debug)] 1593 | struct TestDoc { 1594 | success: bool, 1595 | failure: Spanned, 1596 | shouting: Spanned, 1597 | } 1598 | let node = crate::parse_yaml(0, TEST_DOC).unwrap(); 1599 | let doc: Spanned = from_node(&node).unwrap(); 1600 | println!("{doc:#?}"); 1601 | } 1602 | 1603 | #[test] 1604 | #[allow(dead_code)] 1605 | fn disallowed_keys() { 1606 | #[derive(Deserialize)] 1607 | #[serde(deny_unknown_fields)] 1608 | struct TestDoc { 1609 | success: bool, 1610 | } 1611 | let node = crate::parse_yaml(0, TEST_DOC).unwrap(); 1612 | let err = from_node::(&node).err().unwrap(); 1613 | #[cfg(feature = "serde-path")] 1614 | { 1615 | assert_eq!(err.path(), Some("hello")); 1616 | let mark = err.start_mark().unwrap(); 1617 | assert_eq!(mark.source(), 0); 1618 | assert_eq!(mark.line(), 1); 1619 | assert_eq!(mark.column(), 1); 1620 | }; 1621 | assert!(matches!(&*err, Error::UnknownFieldError(_, _, _))); 1622 | } 1623 | } 1624 | --------------------------------------------------------------------------------