use std::error; use std::fmt; use std::str::{self, FromStr};
use serde::{de, ser};
/// A parsed TOML datetime value /// /// This structure is intended to represent the datetime primitive type that can /// be encoded into TOML documents. This type is a parsed version that contains /// all metadata internally. /// /// Currently this type is intentionally conservative and only supports /// `to_string` as an accessor. Over time though it's intended that it'll grow /// more support! /// /// Note that if you're using `Deserialize` to deserialize a TOML document, you /// can use this as a placeholder for where you're expecting a datetime to be /// specified. /// /// Also note though that while this type implements `Serialize` and /// `Deserialize` it's only recommended to use this type with the TOML format, /// otherwise encoded in other formats it may look a little odd. /// /// Depending on how the option values are used, this struct will correspond /// with one of the following four datetimes from the [TOML v1.0.0 spec]: /// /// | `date` | `time` | `offset` | TOML type | /// | --------- | --------- | --------- | ------------------ | /// | `Some(_)` | `Some(_)` | `Some(_)` | [Offset Date-Time] | /// | `Some(_)` | `Some(_)` | `None` | [Local Date-Time] | /// | `Some(_)` | `None` | `None` | [Local Date] | /// | `None` | `Some(_)` | `None` | [Local Time] | /// /// **1. Offset Date-Time**: If all the optional values are used, `Datetime` /// corresponds to an [Offset Date-Time]. From the TOML v1.0.0 spec: /// /// > To unambiguously represent a specific instant in time, you may use an /// > RFC 3339 formatted date-time with offset. /// > /// > ```toml /// > odt1 = 1979-05-27T07:32:00Z /// > odt2 = 1979-05-27T00:32:00-07:00 /// > odt3 = 1979-05-27T00:32:00.999999-07:00 /// > ``` /// > /// > For the sake of readability, you may replace the T delimiter between date /// > and time with a space character (as permitted by RFC 3339 section 5.6). /// > /// > ```toml /// > odt4 = 1979-05-27 07:32:00Z /// > ``` /// /// **2. Local Date-Time**: If `date` and `time` are given but `offset` is /// `None`, `Datetime` corresponds to a [Local Date-Time]. From the spec: /// /// > If you omit the offset from an RFC 3339 formatted date-time, it will /// > represent the given date-time without any relation to an offset or /// > timezone. It cannot be converted to an instant in time without additional /// > information. Conversion to an instant, if required, is implementation- /// > specific. /// > /// > ```toml /// > ldt1 = 1979-05-27T07:32:00 /// > ldt2 = 1979-05-27T00:32:00.999999 /// > ``` /// /// **3. Local Date**: If only `date` is given, `Datetime` corresponds to a /// [Local Date]; see the docs for [`Date`]. /// /// **4. Local Time**: If only `time` is given, `Datetime` corresponds to a /// [Local Time]; see the docs for [`Time`]. /// /// [TOML v1.0.0 spec]: https://toml.io/en/v1.0.0 /// [Offset Date-Time]: https://toml.io/en/v1.0.0#offset-date-time /// [Local Date-Time]: https://toml.io/en/v1.0.0#local-date-time /// [Local Date]: https://toml.io/en/v1.0.0#local-date /// [Local Time]: https://toml.io/en/v1.0.0#local-time #[derive(PartialEq, Eq, Clone)] pubstruct Datetime { /// Optional date. /// Required for: *Offset Date-Time*, *Local Date-Time*, *Local Date*. pub date: Option<Date>,
/// Error returned from parsing a `Datetime` in the `FromStr` implementation. #[derive(Debug, Clone)] pubstruct DatetimeParseError {
_private: (),
}
// Currently serde itself doesn't have a datetime type, so we map our `Datetime` // to a special valid in the serde data model. Namely one with these special // fields/struct names. // // In general the TOML encoder/decoder will catch this and not literally emit // these strings but rather emit datetimes as they're intended. pubconst FIELD: &str = "$__toml_private_datetime"; pubconst NAME: &str = "$__toml_private_Datetime";
/// A parsed TOML date value /// /// May be part of a [`Datetime`]. Alone, `Date` corresponds to a [Local Date]. /// From the TOML v1.0.0 spec: /// /// > If you include only the date portion of an RFC 3339 formatted date-time, /// > it will represent that entire day without any relation to an offset or /// > timezone. /// > /// > ```toml /// > ld1 = 1979-05-27 /// > ``` /// /// [Local Date]: https://toml.io/en/v1.0.0#local-date #[derive(PartialEq, Eq, Clone)] pubstruct Date { /// Year: four digits pub year: u16, /// Month: 1 to 12 pub month: u8, /// Day: 1 to {28, 29, 30, 31} (based on month/year) pub day: u8,
}
/// A parsed TOML time value /// /// May be part of a [`Datetime`]. Alone, `Time` corresponds to a [Local Time]. /// From the TOML v1.0.0 spec: /// /// > If you include only the time portion of an RFC 3339 formatted date-time, /// > it will represent that time of day without any relation to a specific /// > day or any offset or timezone. /// > /// > ```toml /// > lt1 = 07:32:00 /// > lt2 = 00:32:00.999999 /// > ``` /// > /// > Millisecond precision is required. Further precision of fractional /// > seconds is implementation-specific. If the value contains greater /// > precision than the implementation can support, the additional precision /// > must be truncated, not rounded. /// /// [Local Time]: https://toml.io/en/v1.0.0#local-time #[derive(PartialEq, Eq, Clone)] pubstruct Time { /// Hour: 0 to 23 pub hour: u8, /// Minute: 0 to 59 pub minute: u8, /// Second: 0 to {58, 59, 60} (based on leap second rules) pub second: u8, /// Nanosecond: 0 to 999_999_999 pub nanosecond: u32,
}
/// A parsed TOML time offset /// #[derive(PartialEq, Eq, Clone)] pubenum Offset { /// > A suffix which, when applied to a time, denotes a UTC offset of 00:00; /// > often spoken "Zulu" from the ICAO phonetic alphabet representation of /// > the letter "Z". --- [RFC 3339 section 2] /// /// [RFC 3339 section 2]: https://datatracker.ietf.org/doc/html/rfc3339#section-2
Z,
/// Offset between local time and UTC
Custom { /// Hours: -12 to +12
hours: i8,
// First up, parse the full date if we can let full_date = if chars.clone().nth(2) == Some(':') {
offset_allowed = false;
None
} else { let y1 = u16::from(digit(&mut chars)?); let y2 = u16::from(digit(&mut chars)?); let y3 = u16::from(digit(&mut chars)?); let y4 = u16::from(digit(&mut chars)?);
// Next parse the "partial-time" if available let next = chars.clone().next(); let partial_time = if full_date.is_some()
&& (next == Some('T') || next == Some('t') || next == Some(' '))
{
chars.next(); true
} else {
full_date.is_none()
};
let time = if partial_time { let h1 = digit(&mut chars)?; let h2 = digit(&mut chars)?; match chars.next() {
Some(':') => {}
_ => return Err(DatetimeParseError { _private: () }),
} let m1 = digit(&mut chars)?; let m2 = digit(&mut chars)?; match chars.next() {
Some(':') => {}
_ => return Err(DatetimeParseError { _private: () }),
} let s1 = digit(&mut chars)?; let s2 = digit(&mut chars)?;
letmut nanosecond = 0; if chars.clone().next() == Some('.') {
chars.next(); let whole = chars.as_str();
letmut end = whole.len(); for (i, byte) in whole.bytes().enumerate() { match byte {
b'0'..=b'9' => { if i < 9 { let p = 10_u32.pow(8 - i as u32);
nanosecond += p * u32::from(byte - b'0');
}
}
_ => {
end = i; break;
}
}
} if end == 0 { return Err(DatetimeParseError { _private: () });
}
chars = whole[end..].chars();
}
let time = Time {
hour: h1 * 10 + h2,
minute: m1 * 10 + m2,
second: s1 * 10 + s2,
nanosecond,
};
// Return an error if we didn't hit eof, otherwise return our parsed // date if chars.next().is_some() { return Err(DatetimeParseError { _private: () });
}
Ok(Datetime {
date: full_date,
time,
offset,
})
}
}
fn digit(chars: &mut str::Chars<'_>) -> Result<u8, DatetimeParseError> { match chars.next() {
Some(c) if ('0'..='9').contains(&c) => Ok(c as u8 - b'0'),
_ => Err(DatetimeParseError { _private: () }),
}
}
impl ser::Serialize for Datetime { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where
S: ser::Serializer,
{ use serde::ser::SerializeStruct;
letmut s = serializer.serialize_struct(NAME, 1)?;
s.serialize_field(FIELD, &self.to_string())?;
s.end()
}
}
impl<'de> de::Deserialize<'de> for Datetime { fn deserialize<D>(deserializer: D) -> Result<Datetime, D::Error> where
D: de::Deserializer<'de>,
{ struct DatetimeVisitor;
impl<'de> de::Visitor<'de> for DatetimeVisitor { type Value = Datetime;
fn visit_str<E>(self, s: &str) -> Result<(), E> where
E: de::Error,
{ if s == FIELD {
Ok(())
} else {
Err(de::Error::custom("expected field with custom name"))
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.