toml icon indicating copy to clipboard operation
toml copied to clipboard

Explicit Deserialization of TOML Datetimes between structs and `Value` fail

Open jhwgh1968 opened this issue 3 years ago • 4 comments

I tried this code: Rust Playground Link

I expected this to happen: both the first "full" deserialization and the second "partial" deserialization behave the same.

Instead this happened: error invalid type: string \"[...]", expected a TOML datetime

Possibly related to #333 very intdirectly? At least it seems related to the weird way that Datetime objects in TOML are handled.

jhwgh1968 avatar Jan 15 '23 18:01 jhwgh1968

If I'm reading this correctly, this is a bug in deserializing a toml::Value into a struct as opposed to deserializing a &str into a struct.

epage avatar Jan 18 '23 18:01 epage

If I'm reading this correctly, this is a bug in deserializing a toml::Value into a struct as opposed to deserializing a &str into a struct.

Correct.

jhwgh1968 avatar Jan 18 '23 22:01 jhwgh1968

Same with serialisation. A call of toml::Value::try_from(toml_datetime) produces a table with the magic field, rather then toml::Value::Datetime.

use serde::{Serialize, Serializer};

fn main() {
    let datetime = toml::value::Datetime {
        date: Some(toml::value::Date {
            year: 2025,
            month: 4,
            day: 20,
        }),
        time: Some(toml::value::Time {
            hour: 17,
            minute: 14,
            second: 69,
            nanosecond: 42,
        }),
        offset: None,
    };
    let value = toml::Value::try_from(datetime).unwrap();
    dbg!(&value);
    assert!(value.is_table());
}

Playground

akhilman avatar Apr 20 '25 11:04 akhilman

Bumped into similar problem - my idea was to have toml::Table with "intermediate" representation (similar to serde_json::Value) to parse toml in a generic place and later deserialize parts into concrete structs.

The simplest repro is:

    let date_time: toml::Time = toml::Value::Datetime(toml::Datetime {
        date: None,
        time: Some(toml::Time {
            hour: 0,
            minute: 0,
            second: 0,
            nanosecond: 0,
        }),
        offset: None,
    })
    .try_into()?;

The culprit is that TOML's datetime doesn't round-trip via serde because it doesn't have direct representation in serde's data model and round-trip via string is not implemented.

Deserialization (aka conversion) from toml::Value into custom struct is done via Deserializer trait of Value which converts DateTime to string: https://github.com/toml-rs/toml/blob/main/crates/toml/src/value.rs#L530. However, the corresponding Visitor for DateTime accepts only struct and falls back to default rejection when called for a string: https://github.com/toml-rs/toml/blob/main/crates/toml_datetime/src/datetime.rs#L809

I guess the only option is to implement parsing from string, but I wish there was a way to preserve type in such cases...

ytimenkov avatar Jul 22 '25 11:07 ytimenkov