use crate::core::error::ChartError;
use crate::core::model::chart::Chart;
use crate::core::placement::natal::adjective::{
AdjectiveStarPlacementInput, AdjectiveStarPlacer, DeterministicAdjectiveStarPlacer,
};
use crate::core::placement::natal::decorative::{
DecorativeStarPlacementInput, DecorativeStarPlacer, DeterministicDecorativeStarPlacer,
};
use crate::core::placement::natal::input::NatalChartWithSupportedStarsInput;
use crate::core::placement::natal::major::{
DeterministicMajorStarPlacer, MajorStarPlacementInput, MajorStarPlacer,
};
use crate::core::placement::natal::minor::{
DeterministicMinorStarPlacer, MinorStarPlacementInput, MinorStarPlacer,
};
pub trait NatalStarPlacementStrategy {
fn place_supported_stars(
&self,
chart: Chart,
input: &NatalChartWithSupportedStarsInput,
) -> Result<Chart, ChartError>;
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
pub struct CompositeNatalStarPlacementStrategy<M, N, A, D> {
major: M,
minor: N,
adjective: A,
decorative: D,
}
impl<M, N, A, D> CompositeNatalStarPlacementStrategy<M, N, A, D> {
pub const fn new(major: M, minor: N, adjective: A, decorative: D) -> Self {
Self {
major,
minor,
adjective,
decorative,
}
}
}
impl<M, N, A, D> NatalStarPlacementStrategy for CompositeNatalStarPlacementStrategy<M, N, A, D>
where
M: MajorStarPlacer,
N: MinorStarPlacer,
A: AdjectiveStarPlacer,
D: DecorativeStarPlacer,
{
fn place_supported_stars(
&self,
chart: Chart,
input: &NatalChartWithSupportedStarsInput,
) -> Result<Chart, ChartError> {
let five_element_bureau = chart
.five_element_bureau()
.expect("minimal natal chart should derive a five-element bureau");
let with_major_stars = self.major.place_major_stars(
chart,
MajorStarPlacementInput::new(
input.lunar_day(),
five_element_bureau,
input.birth_year_stem(),
),
)?;
let with_minor_stars = self.minor.place_minor_stars(
with_major_stars,
MinorStarPlacementInput::new_with_birth_time_variant(
input.lunar_month(),
input.birth_context().birth_time_variant(),
input.birth_year_stem(),
input.birth_year_branch(),
),
)?;
let with_adjective_stars = self.adjective.place_adjective_stars(
with_minor_stars,
AdjectiveStarPlacementInput::new_with_daily_star_offset(
input.lunar_month(),
input.lunar_day(),
input.daily_star_offset(),
input.birth_context().birth_time_variant(),
input.birth_year_stem(),
input.birth_year_branch(),
),
)?;
self.decorative.place_decorative_stars(
with_adjective_stars,
DecorativeStarPlacementInput::new(input.birth_year_stem(), input.birth_year_branch()),
)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
pub struct DeterministicNatalStarPlacementStrategy(
CompositeNatalStarPlacementStrategy<
DeterministicMajorStarPlacer,
DeterministicMinorStarPlacer,
DeterministicAdjectiveStarPlacer,
DeterministicDecorativeStarPlacer,
>,
);
impl NatalStarPlacementStrategy for DeterministicNatalStarPlacementStrategy {
fn place_supported_stars(
&self,
chart: Chart,
input: &NatalChartWithSupportedStarsInput,
) -> Result<Chart, ChartError> {
self.0.place_supported_stars(chart, input)
}
}