use lunar_lite::{
EarthlyBranch, FourPillars, HeavenlyStem, LunarError, MonthDivide, SolarDate,
StemBranchOptions, YearDivide, four_pillars_from_solar_date_with_options,
solar_to_lunar as convert_solar_to_lunar,
};
use crate::core::error::ChartError;
use crate::core::model::calendar::{SolarDay, SolarMonth};
use crate::core::placement::natal::life_body::{LunarDay, LunarMonth};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct LunarConversion {
lunar_year: i32,
lunar_month: LunarMonth,
lunar_day: LunarDay,
is_leap_month: bool,
birth_year_stem: HeavenlyStem,
birth_year_branch: EarthlyBranch,
four_pillars: FourPillars,
}
impl LunarConversion {
pub(crate) const fn lunar_year(&self) -> i32 {
self.lunar_year
}
pub(crate) const fn lunar_month(&self) -> LunarMonth {
self.lunar_month
}
pub(crate) const fn lunar_day(&self) -> LunarDay {
self.lunar_day
}
pub(crate) const fn is_leap_month(&self) -> bool {
self.is_leap_month
}
pub(crate) const fn birth_year_stem(&self) -> HeavenlyStem {
self.birth_year_stem
}
pub(crate) const fn birth_year_branch(&self) -> EarthlyBranch {
self.birth_year_branch
}
pub(crate) const fn four_pillars(&self) -> FourPillars {
self.four_pillars
}
}
pub(crate) fn solar_to_lunar(
year: i32,
month: SolarMonth,
day: SolarDay,
time_index: u8,
) -> Result<LunarConversion, ChartError> {
let conversion_failed = || ChartError::CalendarConversionFailed {
year,
month: month.value(),
day: day.value(),
};
let solar = SolarDate {
year,
month: month.value(),
day: day.value(),
};
let lunar = convert_solar_to_lunar(solar)
.map_err(|err| map_solar_conversion_error(err, year, month.value(), day.value()))?;
let pillars = four_pillars_from_solar_date_with_options(
solar,
time_index,
StemBranchOptions {
year: YearDivide::Normal,
month: MonthDivide::Normal,
},
)
.map_err(|err| map_solar_conversion_error(err, year, month.value(), day.value()))?;
let lunar_month = LunarMonth::new(lunar.month).map_err(|_| conversion_failed())?;
let lunar_day = LunarDay::new(lunar.day).map_err(|_| conversion_failed())?;
Ok(LunarConversion {
lunar_year: lunar.year,
lunar_month,
lunar_day,
is_leap_month: lunar.is_leap_month,
birth_year_stem: pillars.yearly.stem(),
birth_year_branch: pillars.yearly.branch(),
four_pillars: pillars,
})
}
fn map_solar_conversion_error(err: LunarError, year: i32, month: u8, day: u8) -> ChartError {
match err {
LunarError::InvalidSolarDate { .. } => ChartError::InvalidSolarDate { year, month, day },
LunarError::YearOutOfRange { .. } | LunarError::SolarTermOutOfRange { .. } => {
ChartError::UnsupportedCalendarDate { year, month, day }
}
LunarError::InvalidLunarDate { .. }
| LunarError::InvalidTime { .. }
| LunarError::InvalidTimeIndex { .. } => {
ChartError::CalendarConversionFailed { year, month, day }
}
}
}
#[cfg(test)]
mod tests;