use crate::core::error::ChartError;
use lunar_lite::EarthlyBranch;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct SolarMonth(u8);
impl SolarMonth {
pub const fn new(value: u8) -> Result<Self, ChartError> {
if value == 0 || value > 12 {
return Err(ChartError::InvalidSolarMonth { value });
}
Ok(Self(value))
}
pub const fn value(self) -> u8 {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct SolarDay(u8);
impl SolarDay {
pub const fn new(value: u8) -> Result<Self, ChartError> {
if value == 0 || value > 31 {
return Err(ChartError::InvalidSolarDay { value });
}
Ok(Self(value))
}
pub const fn value(self) -> u8 {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CalendarKind {
Solar,
Lunar, }
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct CalendarDate {
kind: CalendarKind,
year: i32,
month: u8,
day: u8,
}
impl CalendarDate {
pub const fn solar(year: i32, month: u8, day: u8) -> Self {
Self {
kind: CalendarKind::Solar,
year,
month,
day,
}
}
pub const fn lunar(year: i32, month: u8, day: u8) -> Self {
Self {
kind: CalendarKind::Lunar,
year,
month,
day,
}
}
pub const fn kind(&self) -> CalendarKind {
self.kind
}
pub const fn year(&self) -> i32 {
self.year
}
pub const fn month(&self) -> u8 {
self.month
}
pub const fn day(&self) -> u8 {
self.day
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Gender {
Female,
Male,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum BirthTime {
EarlyZi,
Chou,
Yin,
Mao,
Chen,
Si,
Wu,
Wei,
Shen,
You,
Xu,
Hai,
LateZi,
}
impl BirthTime {
pub const fn from_iztro_time_index(value: u8) -> Result<Self, ChartError> {
match value {
0 => Ok(Self::EarlyZi),
1 => Ok(Self::Chou),
2 => Ok(Self::Yin),
3 => Ok(Self::Mao),
4 => Ok(Self::Chen),
5 => Ok(Self::Si),
6 => Ok(Self::Wu),
7 => Ok(Self::Wei),
8 => Ok(Self::Shen),
9 => Ok(Self::You),
10 => Ok(Self::Xu),
11 => Ok(Self::Hai),
12 => Ok(Self::LateZi),
value => Err(ChartError::InvalidBirthTimeIndex { value }),
}
}
pub const fn iztro_time_index(self) -> u8 {
match self {
Self::EarlyZi => 0,
Self::Chou => 1,
Self::Yin => 2,
Self::Mao => 3,
Self::Chen => 4,
Self::Si => 5,
Self::Wu => 6,
Self::Wei => 7,
Self::Shen => 8,
Self::You => 9,
Self::Xu => 10,
Self::Hai => 11,
Self::LateZi => 12,
}
}
pub const fn branch(self) -> EarthlyBranch {
match self {
Self::EarlyZi | Self::LateZi => EarthlyBranch::Zi,
Self::Chou => EarthlyBranch::Chou,
Self::Yin => EarthlyBranch::Yin,
Self::Mao => EarthlyBranch::Mao,
Self::Chen => EarthlyBranch::Chen,
Self::Si => EarthlyBranch::Si,
Self::Wu => EarthlyBranch::Wu,
Self::Wei => EarthlyBranch::Wei,
Self::Shen => EarthlyBranch::Shen,
Self::You => EarthlyBranch::You,
Self::Xu => EarthlyBranch::Xu,
Self::Hai => EarthlyBranch::Hai,
}
}
pub const fn is_late_zi(self) -> bool {
matches!(self, Self::LateZi)
}
pub const fn from_branch(value: EarthlyBranch) -> Self {
match value {
EarthlyBranch::Zi => Self::EarlyZi,
EarthlyBranch::Chou => Self::Chou,
EarthlyBranch::Yin => Self::Yin,
EarthlyBranch::Mao => Self::Mao,
EarthlyBranch::Chen => Self::Chen,
EarthlyBranch::Si => Self::Si,
EarthlyBranch::Wu => Self::Wu,
EarthlyBranch::Wei => Self::Wei,
EarthlyBranch::Shen => Self::Shen,
EarthlyBranch::You => Self::You,
EarthlyBranch::Xu => Self::Xu,
EarthlyBranch::Hai => Self::Hai,
}
}
const fn key(self) -> &'static str {
match self {
Self::EarlyZi => "early_zi",
Self::Chou => "chou",
Self::Yin => "yin",
Self::Mao => "mao",
Self::Chen => "chen",
Self::Si => "si",
Self::Wu => "wu",
Self::Wei => "wei",
Self::Shen => "shen",
Self::You => "you",
Self::Xu => "xu",
Self::Hai => "hai",
Self::LateZi => "late_zi",
}
}
}
impl Serialize for BirthTime {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.key())
}
}
impl<'de> Deserialize<'de> for BirthTime {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct BirthTimeVisitor;
impl serde::de::Visitor<'_> for BirthTimeVisitor {
type Value = BirthTime;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a birth time key")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match value {
"early_zi" | "zi" => Ok(BirthTime::EarlyZi),
"chou" => Ok(BirthTime::Chou),
"yin" => Ok(BirthTime::Yin),
"mao" => Ok(BirthTime::Mao),
"chen" => Ok(BirthTime::Chen),
"si" => Ok(BirthTime::Si),
"wu" => Ok(BirthTime::Wu),
"wei" => Ok(BirthTime::Wei),
"shen" => Ok(BirthTime::Shen),
"you" => Ok(BirthTime::You),
"xu" => Ok(BirthTime::Xu),
"hai" => Ok(BirthTime::Hai),
"late_zi" => Ok(BirthTime::LateZi),
other => Err(E::unknown_variant(
other,
&[
"early_zi", "zi", "chou", "yin", "mao", "chen", "si", "wu", "wei",
"shen", "you", "xu", "hai", "late_zi",
],
)),
}
}
}
deserializer.deserialize_str(BirthTimeVisitor)
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct BirthContext {
date: CalendarDate,
birth_time: BirthTime,
gender: Gender,
}
impl BirthContext {
pub const fn new(date: CalendarDate, birth_time: EarthlyBranch, gender: Gender) -> Self {
Self::new_with_birth_time_variant(date, BirthTime::from_branch(birth_time), gender)
}
pub const fn new_with_birth_time_variant(
date: CalendarDate,
birth_time: BirthTime,
gender: Gender,
) -> Self {
Self {
date,
birth_time,
gender,
}
}
pub const fn date(&self) -> &CalendarDate {
&self.date
}
pub const fn birth_time(&self) -> EarthlyBranch {
self.birth_time.branch()
}
pub const fn birth_time_variant(&self) -> BirthTime {
self.birth_time
}
pub const fn gender(&self) -> Gender {
self.gender
}
}