enneagram 1.3.0

A digital enneagram
use crate::{
    config::{TriadsConfig, Fallback, Property},
    triad::{Fault, Frame, Means, Need, Triad}
};

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Personality
{
    pub frame: Frame,
    pub strategy: Means,
    pub fault: Fault,
    pub need: Need
}

impl Personality
{
    pub fn affirmation(&self, f: &mut core::fmt::Formatter, config: &dyn Property<TriadsConfig>, fallback: &Fallback) -> core::fmt::Result
    {
        let frame = self.frame.config(config, fallback);
        let frame = frame.affirmation.as_ref();

        let fault = self.fault.config(config, fallback);
        let fault = fault.affirmation.as_ref();

        let strategy = self.strategy.config(config, fallback);
        let strategy = strategy.affirmation.as_ref();

        write!(f, "i will {frame}, {fault}, and {strategy}.")
    }

    pub fn from_triads(triads: [&dyn Triad; 4]) -> Self
    {
        let (frame, strategy, fault, need) = triads
            .into_iter()
            .map(|triad| {
                let any = triad.as_any();
                any.downcast_ref()
                    .map(|frame| (Some(frame), None, None, None))
                    .or_else(|| any.downcast_ref().map(|strategy| (None, Some(strategy), None, None)))
                    .or_else(|| any.downcast_ref().map(|fault| (None, None, Some(fault), None)))
                    .or_else(|| any.downcast_ref().map(|need| (None, None, None, Some(need))))
                    .expect("The triad type is not r{ecognized. Something must have gone horribly wrong!")
            })
            .reduce(|lhs, rhs| {
                (
                    lhs.0
                        .map(Some)
                        .xor(rhs.0.map(Some))
                        .map(|inner| inner.expect("Unsound personality. Conflicting personality frame")),
                    lhs.1
                        .map(Some)
                        .xor(rhs.1.map(Some))
                        .map(|inner| inner.expect("Unsound personality. Conflicting personality strategy")),
                    lhs.2
                        .map(Some)
                        .xor(rhs.2.map(Some))
                        .map(|inner| inner.expect("Unsound personality. Conflicting personality fault")),
                    lhs.3
                        .map(Some)
                        .xor(rhs.3.map(Some))
                        .map(|inner| inner.expect("Unsound personality. Conflicting personality need"))
                )
            })
            .expect("Unsound personality, No triads given. Impossible!");
        Personality {
            frame: *frame.expect("Unsound personality. Undefined personality frame"),
            strategy: *strategy.expect("Unsound personality. Undefined personality strategy"),
            fault: *fault.expect("Unsound personality. Undefined personality fault"),
            need: *need.expect("Unsound personality. Undefined personality need")
        }
    }
}