Canop / deser-hjson

A Serde 1.0 compatible Rust deserializer for Hjson

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Possible to expose a `.kind(&str)` function?

dessalines opened this issue · comments

I'm looking to use this to replace hjson-rust, but that library has a feature where you can expose the kind by deferencing the enum:

let value = &serde_hjson::from_str(text)?;
match *value {
  serde_hjson::Value::I64(value) => ...
}

Essentially I'd love to use this, but I need a way to extract what the kind of the simple result would be (and if its complex, just return None).

Edit: To be more specific, I'd like to use deser-hjson in config-rs here: https://github.com/mehcode/config-rs/blob/master/src/file/format/hjson.rs#L21

If I understand your need, it's already possible with just the from_str function, by deserializing into an untagged enum:

    #[derive(Deserialize, PartialEq, Debug)]
    #[serde(untagged)]
    enum Guess {
        Bool(bool),
        F64(f64),
        Char(char),
        String(String),
        Array(Vec<String>),
    }
    assert_eq!(from_str::<Guess>("false").unwrap(), Guess::Bool(false));
    assert_eq!(from_str::<Guess>("45").unwrap(), Guess::F64(45.0));
    assert_eq!(from_str::<Guess>("a").unwrap(), Guess::Char('a'));
    assert_eq!(from_str::<Guess>("abc").unwrap(), Guess::String("abc".to_owned()));
    assert_eq!(from_str::<Guess>("\"abc\"").unwrap(), Guess::String("abc".to_owned()));
    assert_eq!(from_str::<Guess>("[\"abc\"]").unwrap(), Guess::Array(vec!["abc".to_owned()]));

(right now it doesn't recognize integers as such and thinks they're floats but this can be fixed)

But you normally don't need to do explicit type guessing: you deserialize into the type you want and it can be (or include) an untagged enum.

I've published version 1.1.12 with precise type guessing.

See https://github.com/Canop/deser-hjson/blob/main/tests/guess.rs#L8

Thanks a ton!

There's one other kind checking this needs, an Object type:

serde_hjson::Value::Object(ref table) => {
  let mut m = HashMap::new();

  for (key, value) in table {
      m.insert(key.clone(), from_hjson_value(uri, value));
  }

  Value::new(uri, ValueKind::Table(m))
}

Another thing I noticed is that because of the recursive nature of how these are called, they seem to not care about the sub type of the array, just whether its an array or not:

serde_hjson::Value::Array(ref array) => {
  let mut l = Vec::new();

  for value in array {
      l.push(from_hjson_value(uri, value));
  }

  Value::new(uri, ValueKind::Array(l))
}

What you do looks curious to me, it looks like you're rebuilding a deserializer instead of leveraging the one you could have with a derive based deserialization.

Deserializing into a complete complex structure from your hjson file is normally just a call to deser_hjson::from_str.

If you'd like, I'm always on this chat: https://miaou.dystroy.org/3768

Mmmk I'll hit you up there.

Okay nevermind that didn't work, the chat room was all white.

Anyways, config-rs is combining a lot of different file formats, such as toml, ini, json, hjson, environment vars, etc into their own structured hierarchy.

I'm guessing this might work for arrays?

enum Guess {
  Bool(bool)
  ...
  Array(Vec<Guess>),

But I would still need one for Object, so that it can know to call itself recursively.

Look at this: https://github.com/Canop/broot/blob/master/src/conf/conf.rs#L37

The are maps, arrays, options, etc. and it's as deep as you want and it allows alternative formats (using untagged enums). Some parts of the structure are deserialized with specific functions thanks to serde annotations and you might use env variables here if you like.

It's all deserialized either from hjson or toml.

I realized I should probably describe the bigger issue better. A ton of projects found ~ 2 months ago that a dependency, linked_hash_map was broken from rust 1.48 onwards, including hjson-rust (also called serde_hjson) and all its downstream projects.

There is a replacement, called hashlink, but the library maintainers of hjson-rust, are also absent.

So the best option is to swap out hjson-rust for your deser-hjson library, which seems to be the only maintained rust hjson library.

I realize this kind() or Guess method wouldn't be necessary if we were using deser_hjson directly (our hjson conf is also well typed), but config-rs does have some neat features like merging config files, merging with environment vars, etc. And any work that we do getting it working with an actually working hjson deserializer would help other projects that are dependent on config-rs too.

This has been a frustrating rabbit hole for us because we've found that ~ 3 fairly critical rust libraries have absent maintainers.

If all else fails, we could probably use deser_hjson directly, and figure out how to do merging on our own.

I just fell randomly on this...
https://lemmy.ml/post/53026

Reading into an intermediate structure with guessed types is an anti-pattern and a source of bugs in production when ambiguous formats like yaml, or hjson are involved. I'm not fond of supporting this kind of trap.

I found a way around our issue using this library directly, rather than trying to patch this into several abandoned libraries. Thx for this.