use serde::{Deserialize, Serialize};
use crate::astro::surpalaces::SurroundedPalaces;
use crate::data::stars::StarKey;
use crate::data::types::*;
use crate::models::palace::PalaceData;
use crate::models::star::Star;
use crate::utils::fix_index;
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct RawLunarDate {
pub lunar_year: i64,
pub lunar_month: u32,
pub lunar_day: u32,
pub is_leap: bool,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct RawChineseDate {
pub yearly: (HeavenlyStem, EarthlyBranch),
pub monthly: (HeavenlyStem, EarthlyBranch),
pub daily: (HeavenlyStem, EarthlyBranch),
pub hourly: (HeavenlyStem, EarthlyBranch),
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct RawDates {
pub lunar_date: RawLunarDate,
pub chinese_date: RawChineseDate,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Astrolabe {
pub gender: Gender,
pub solar_date: String,
pub lunar_date: String,
pub chinese_date: String,
pub time: String,
pub time_range: String,
pub sign: String,
pub zodiac: String,
pub earthly_branch_of_soul_palace: EarthlyBranch,
pub earthly_branch_of_body_palace: EarthlyBranch,
pub soul: StarKey,
pub body: StarKey,
pub five_elements_class: FiveElementsClass,
pub palaces: Vec<PalaceData>,
pub raw_dates: RawDates,
pub time_index: u8,
pub fix_leap: bool,
pub language: Language,
pub config: Config,
}
impl Astrolabe {
pub fn palace(&self, index: usize) -> &PalaceData {
&self.palaces[index]
}
pub fn palace_by_name(&self, name: Palace) -> Option<&PalaceData> {
self.palaces.iter().find(|p| p.name == name)
}
pub fn surrounded_palaces(&self, index: usize) -> SurroundedPalaces<'_> {
let opposite = fix_index(index as i32 + 6, 12);
let career = fix_index(index as i32 + 4, 12);
let wealth = fix_index(index as i32 + 8, 12);
SurroundedPalaces {
target: &self.palaces[index],
opposite: &self.palaces[opposite],
career: &self.palaces[career],
wealth: &self.palaces[wealth],
}
}
pub fn star(&self, key: StarKey) -> Option<(&Star, &PalaceData)> {
for p in &self.palaces {
for s in p
.major_stars
.iter()
.chain(p.minor_stars.iter())
.chain(p.adjective_stars.iter())
{
if s.key == key {
return Some((s, p));
}
}
}
None
}
}
#[cfg(test)]
mod tests {
use crate::astro::builder::by_solar;
use crate::data::stars::StarKey;
use crate::data::types::*;
fn make_astrolabe() -> super::Astrolabe {
by_solar(
"2000-8-16",
2,
Gender::Female,
true,
Language::ZhCN,
Config::default(),
)
.unwrap()
}
#[test]
fn test_palace_by_index() {
let a = make_astrolabe();
for i in 0..12 {
assert_eq!(a.palace(i).index, i);
}
}
#[test]
fn test_palace_by_name() {
let a = make_astrolabe();
let soul = a.palace_by_name(Palace::Soul);
assert!(soul.is_some());
assert_eq!(soul.unwrap().name, Palace::Soul);
let names = [
Palace::Soul,
Palace::Parents,
Palace::Spirit,
Palace::Property,
Palace::Career,
Palace::Friends,
Palace::Surface,
Palace::Health,
Palace::Wealth,
Palace::Children,
Palace::Spouse,
Palace::Siblings,
];
for name in &names {
assert!(a.palace_by_name(*name).is_some());
}
}
#[test]
fn test_star_lookup() {
let a = make_astrolabe();
let major_keys = [
StarKey::ZiweiMaj,
StarKey::TianjiMaj,
StarKey::TaiyangMaj,
StarKey::WuquMaj,
StarKey::TiantongMaj,
StarKey::LianzhenMaj,
StarKey::TianfuMaj,
StarKey::TaiyinMaj,
StarKey::TanlangMaj,
StarKey::JumenMaj,
StarKey::TianxiangMaj,
StarKey::TianliangMaj,
StarKey::QishaMaj,
StarKey::PojunMaj,
];
for key in &major_keys {
let result = a.star(*key);
assert!(result.is_some(), "Star {:?} should be found", key);
let (star, palace) = result.unwrap();
assert_eq!(star.key, *key);
assert!(palace.has(&[*key]));
}
}
#[test]
fn test_surrounded_palaces_indices() {
let a = make_astrolabe();
let sp = a.surrounded_palaces(3);
assert_eq!(sp.target.index, 3);
assert_eq!(sp.opposite.index, 9); assert_eq!(sp.career.index, 7); assert_eq!(sp.wealth.index, 11); }
}