dtolnay/serde-yaml

Strange error when using a BTreeMap as the key to another map

anthrotype opened this issue · 0 comments

Hello, thanks for this library!

I would like to use serde_yaml to serialize a type that contains BTreeMaps nested inside another BTreeMap, but not as the values but rather as its keys.. I know YAML allows maps to have complex keys whose type is not a mere string or scalar type, using the special ? prefix in front of the complex key.

This seems to be working most of the time, however I am getting a weird error message when the map that contains such complex keys contains only one item:

Error { kind: EMITTER, problem: "expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS" }

Now, the error disappears when the map contains more than one items...

Here's an example below where I hacked serde_yaml's own test_serde.rs from my fork to reproduce this bug.

https://github.com/dtolnay/serde-yaml/compare/master...anthrotype:serde-yaml:map-as-map-key?expand=1

#[test]
fn test_map_as_key_to_another_map() {
    // Trying to use a BTreeMap as the key for an other BTreeMap. If the map only
    // contains one item, a strange error is returned by the libyaml emitter:
    // "expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS". If it contains
    // more than one item, then serialization completes without errors.

    use std::collections::BTreeMap;
    use std::path::PathBuf;

    #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
    struct Glyph {
        name: String,
        sources: BTreeMap<BTreeMap<String, i32>, PathBuf>,
    }

    let yaml = indoc! {"\
        name: foo
        sources:
          ? Weight: 400
          : Font-Regular.ufo/glyphs/foo.glif
    "};

    let glyph = Glyph {
        name: "foo".to_string(),
        sources: vec![
            (
                BTreeMap::from([("Weight".to_string(), 400)]),
                PathBuf::from("Font-Regular.ufo/glyphs/foo.glif"),
            ),
            // // After uncommenting below, it no longer returns an error
            // (
            //     BTreeMap::from([("Weight".to_string(), 700)]),
            //     PathBuf::from("Font-Bold.ufo/glyphs/foo.glif"),
            // ),
        ]
        .into_iter()
        .collect(),
    };

    test_serde(&glyph, yaml);
}

Running cargo test I get this:

failures:

---- test_map_as_key_to_another_map stdout ----
thread 'test_map_as_key_to_another_map' panicked at 'called `Result::unwrap()` on an `Err` value: Error { kind: EMITTER, problem: "expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS" }', tests/test_serde.rs:20:52
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    test_map_as_key_to_another_map

test result: FAILED. 31 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s

error: test failed, to rerun pass `--test test_serde`

Am I doing something wrong? It looks to me it is a bug since it only fails when the map contains one item.