1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use gregorian::{Date, Year, Month, YearMonth, InvalidDate};
use std::ops::Range;

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum PartialDate {
	Year(Year),
	YearMonth(YearMonth),
	YearMonthDay(Date),
}

impl PartialDate {
	/// Interpret the partial date as start date.
	///
	/// This gives the first day of a year or month if more specific fields are not given.
	pub fn as_start_date(self) -> Date {
		match self {
			Self::Year(x) => x.first_day(),
			Self::YearMonth(x) => x.first_day(),
			Self::YearMonthDay(x) => x,
		}
	}

	/// Interpret the partial date as an end date.
	///
	/// This gives the last day of a year or month if more specific fields are not given.
	pub fn as_end_date(self) -> Date {
		match self {
			Self::Year(x) => x.last_day(),
			Self::YearMonth(x) => x.last_day(),
			Self::YearMonthDay(x) => x,
		}
	}

	/// Interpret the partial date as a half-open date range.
	///
	/// This gives a date range comprising of the entire year or month,
	/// or just a single day.
	pub fn as_range(self) -> Range<Date> {
		match self {
			Self::Year(x) => Range {
				start: x.first_day(),
				end: x.next().first_day(),
			},
			Self::YearMonth(x) => Range {
				start: x.first_day(),
				end: x.next().first_day(),
			},
			Self::YearMonthDay(x) => Range {
				start: x,
				end: x.next(),
			},
		}
	}
}

impl std::str::FromStr for PartialDate {
	type Err = ParsePartialDateError;

	fn from_str(data: &str) -> Result<Self, Self::Err> {
		let mut fields = data.splitn(3, '-');
		let year = fields.next().unwrap();
		let month = fields.next();
		let day = fields.next();

		let year: i16 = year.parse().map_err(|_| InvalidPartialDateSyntax::new())?;

		if let Some(month) = month {
			let month: u8 = month.parse().map_err(|_| InvalidPartialDateSyntax::new())?;
			let month = Month::new(month)?;
			if let Some(day) = day {
				let day: u8 = day.parse().map_err(|_| InvalidPartialDateSyntax::new())?;
				Ok(Self::YearMonthDay(Date::new(year, month, day)?))
			} else {
				Ok(Self::YearMonth(YearMonth::new(year, month)))
			}
		} else {
			Ok(Self::Year(year.into()))
		}
	}
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ParsePartialDateError {
	InvalidSyntax(InvalidPartialDateSyntax),
	InvalidDate(InvalidDate),
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct InvalidPartialDateSyntax {
	_private: (),
}

impl std::error::Error for ParsePartialDateError {}
impl std::error::Error for InvalidPartialDateSyntax {}

impl InvalidPartialDateSyntax {
	fn new() -> Self {
		Self { _private: () }
	}
}

impl From<InvalidPartialDateSyntax> for ParsePartialDateError {
	fn from(other: InvalidPartialDateSyntax) -> Self {
		Self::InvalidSyntax(other)
	}
}

impl From<InvalidDate> for ParsePartialDateError {
	fn from(other: InvalidDate) -> Self {
		Self::InvalidDate(other)
	}
}

impl From<gregorian::InvalidMonthNumber> for ParsePartialDateError {
	fn from(other: gregorian::InvalidMonthNumber) -> Self {
		Self::InvalidDate(other.into())
	}
}

impl std::fmt::Display for ParsePartialDateError {
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		match self {
			Self::InvalidSyntax(e) => write!(f, "{}", e),
			Self::InvalidDate(e) => write!(f, "{}", e),
		}
	}
}

impl std::fmt::Display for InvalidPartialDateSyntax {
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		write!(f, "invalid syntax")
	}
}