froodi 1.0.0-beta.18

An ergonomic Rust IoC container
Documentation
use core::fmt::{self, Display, Formatter};

use alloc::vec::Vec;

pub trait Scope: Ord + Into<ScopeData> {
    #[must_use]
    fn name(&self) -> &'static str;

    #[must_use]
    fn priority(&self) -> u8;

    #[must_use]
    fn is_skipped_by_default(&self) -> bool {
        false
    }
}

pub trait Scopes<const N: usize> {
    type Scope;

    fn all() -> (Self::Scope, [Self::Scope; N]);
}

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
pub enum DefaultScope {
    Runtime,
    App,
    Session,
    Request,
    Action,
    Step,
}

impl From<DefaultScope> for ScopeData {
    fn from(scope: DefaultScope) -> Self {
        Self {
            priority: scope.priority(),
            name: scope.name(),
            is_skipped_by_default: scope.is_skipped_by_default(),
        }
    }
}

impl Scope for DefaultScope {
    #[inline]
    fn name(&self) -> &'static str {
        match self {
            DefaultScope::Runtime => "runtime",
            DefaultScope::App => "app",
            DefaultScope::Session => "session",
            DefaultScope::Request => "request",
            DefaultScope::Action => "action",
            DefaultScope::Step => "step",
        }
    }

    #[inline]
    fn priority(&self) -> u8 {
        *self as u8
    }

    #[inline]
    fn is_skipped_by_default(&self) -> bool {
        matches!(self, DefaultScope::Runtime | DefaultScope::Session)
    }
}

impl Scopes<5> for DefaultScope {
    type Scope = Self;

    #[inline]
    fn all() -> (Self::Scope, [Self::Scope; 5]) {
        use DefaultScope::{Action, App, Request, Runtime, Session, Step};

        (Runtime, [App, Session, Request, Action, Step])
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct ScopeData {
    pub priority: u8,
    pub name: &'static str,
    pub is_skipped_by_default: bool,
}

impl Display for ScopeData {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{} ({}, is_skipped_by_default = {})",
            self.name, self.priority, self.is_skipped_by_default
        )
    }
}

pub(crate) struct ScopeDataWithChildScopesData {
    pub scope_data: Option<ScopeData>,
    pub child_scopes_data: Vec<ScopeData>,
}

impl ScopeDataWithChildScopesData {
    #[inline]
    #[must_use]
    pub(crate) const fn new(scope_data: ScopeData, child_scopes_data: Vec<ScopeData>) -> Self {
        Self {
            scope_data: Some(scope_data),
            child_scopes_data,
        }
    }

    #[must_use]
    pub(crate) fn new_with_sort(mut scopes: Vec<ScopeData>) -> Self {
        scopes.sort_by_key(|scope| scope.priority);
        let mut iter = scopes.into_iter();
        match iter.next() {
            Some(scope_data) => Self {
                scope_data: Some(scope_data),
                child_scopes_data: iter.collect(),
            },
            None => Self {
                scope_data: None,
                child_scopes_data: Vec::new(),
            },
        }
    }

    #[inline]
    #[must_use]
    pub(crate) fn child(self) -> Self {
        let mut iter = self.child_scopes_data.into_iter();
        match iter.next() {
            Some(scope_data) => Self {
                scope_data: Some(scope_data),
                child_scopes_data: iter.collect(),
            },
            None => Self {
                scope_data: None,
                child_scopes_data: Vec::new(),
            },
        }
    }
}