use std::convert::TryFrom;
use std::fmt;
use std::str::FromStr;
use combine::stream::position::Stream;
use crate::parser;
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(try_from = "dt_serde::DatetimeSerde"))]
#[cfg_attr(feature = "serde", serde(into = "dt_serde::DatetimeSerde"))]
pub struct Datetime {
pub date: Option<Date>,
pub time: Option<Time>,
pub offset: Option<Offset>,
}
#[cfg(feature = "serde")]
pub(crate) mod dt_serde {
use std::convert::TryFrom;
use super::Datetime;
use crate::parser;
pub(crate) const NAME: &str = "$__toml_private_Datetime";
pub(crate) const FIELD: &str = "$__toml_private_datetime";
#[derive(
PartialEq, Eq, PartialOrd, Ord, Clone, Debug, serde::Deserialize, serde::Serialize,
)]
#[serde(rename = "$__toml_private_Datetime")]
pub(crate) struct DatetimeSerde {
#[serde(rename = "$__toml_private_datetime")]
field: String,
}
impl From<Datetime> for DatetimeSerde {
fn from(d: Datetime) -> Self {
Self {
field: d.to_string(),
}
}
}
impl TryFrom<DatetimeSerde> for Datetime {
type Error = parser::TomlError;
fn try_from(s: DatetimeSerde) -> Result<Self, Self::Error> {
s.field.parse()
}
}
}
impl From<Date> for Datetime {
fn from(other: Date) -> Self {
Datetime {
date: Some(other),
time: None,
offset: None,
}
}
}
impl From<Time> for Datetime {
fn from(other: Time) -> Self {
Datetime {
date: None,
time: Some(other),
offset: None,
}
}
}
impl FromStr for Datetime {
type Err = parser::TomlError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
use combine::stream::position::{IndexPositioner, Positioner};
use combine::EasyParser;
let b = s.as_bytes();
let result = parser::datetime::date_time().easy_parse(Stream::new(b));
match result {
Ok((_, ref rest)) if !rest.input.is_empty() => Err(parser::TomlError::from_unparsed(
(&rest.positioner
as &dyn Positioner<usize, Position = usize, Checkpoint = IndexPositioner>)
.position(),
b,
)),
Ok((dt, _)) => Ok(dt),
Err(e) => Err(parser::TomlError::new(e, b)),
}
}
}
impl<'a> TryFrom<&'a str> for Datetime {
type Error = parser::TomlError;
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl TryFrom<String> for Datetime {
type Error = parser::TomlError;
fn try_from(s: String) -> Result<Self, Self::Error> {
s.parse()
}
}
impl fmt::Display for Datetime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(ref date) = self.date {
write!(f, "{}", date)?;
}
if let Some(ref time) = self.time {
if self.date.is_some() {
write!(f, "T")?;
}
write!(f, "{}", time)?;
}
if let Some(ref offset) = self.offset {
write!(f, "{}", offset)?;
}
Ok(())
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug)]
pub struct Date {
pub year: u16,
pub month: u8,
pub day: u8,
}
impl FromStr for Date {
type Err = parser::TomlError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
use combine::stream::position::{IndexPositioner, Positioner};
use combine::EasyParser;
let b = s.as_bytes();
let result = parser::datetime::full_date().easy_parse(Stream::new(b));
match result {
Ok((_, ref rest)) if !rest.input.is_empty() => Err(parser::TomlError::from_unparsed(
(&rest.positioner
as &dyn Positioner<usize, Position = usize, Checkpoint = IndexPositioner>)
.position(),
b,
)),
Ok((dt, _)) => Ok(dt),
Err(e) => Err(parser::TomlError::new(e, b)),
}
}
}
impl<'a> TryFrom<&'a str> for Date {
type Error = parser::TomlError;
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl TryFrom<String> for Date {
type Error = parser::TomlError;
fn try_from(s: String) -> Result<Self, Self::Error> {
s.parse()
}
}
impl fmt::Display for Date {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:04}-{:02}-{:02}", self.year, self.month, self.day)
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug)]
pub struct Time {
pub hour: u8,
pub minute: u8,
pub second: u8,
pub nanosecond: u32,
}
impl FromStr for Time {
type Err = parser::TomlError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
use combine::stream::position::{IndexPositioner, Positioner};
use combine::EasyParser;
let b = s.as_bytes();
let result = parser::datetime::partial_time().easy_parse(Stream::new(b));
match result {
Ok((_, ref rest)) if !rest.input.is_empty() => Err(parser::TomlError::from_unparsed(
(&rest.positioner
as &dyn Positioner<usize, Position = usize, Checkpoint = IndexPositioner>)
.position(),
b,
)),
Ok((dt, _)) => Ok(dt),
Err(e) => Err(parser::TomlError::new(e, b)),
}
}
}
impl<'a> TryFrom<&'a str> for Time {
type Error = parser::TomlError;
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl TryFrom<String> for Time {
type Error = parser::TomlError;
fn try_from(s: String) -> Result<Self, Self::Error> {
s.parse()
}
}
impl fmt::Display for Time {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:02}:{:02}:{:02}", self.hour, self.minute, self.second)?;
if self.nanosecond != 0 {
let s = format!("{:09}", self.nanosecond);
write!(f, ".{}", s.trim_end_matches('0'))?;
}
Ok(())
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug)]
pub enum Offset {
Z,
Custom {
hours: i8,
minutes: u8,
},
}
impl fmt::Display for Offset {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Offset::Z => write!(f, "Z"),
Offset::Custom { hours, minutes } => write!(f, "{:+03}:{:02}", hours, minutes),
}
}
}