nstree 1.0.0

construct branched 'namespace strings' for nested subcomponents, often for logging
Documentation
//! Combinators for [`RawNamespacePath`] and [`NamespacePath`]

use empty_fallback_chain::{empty_fallback_chain, maybe_empty_fallback_chain};

use super::{NamespaceComponentsHint, NamespacePath, RawNamespacePath};

/// See [`NamespacePath::cache_components_hint`]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub struct CacheComponentsHint<T: ?Sized> {
    cached_hints: NamespaceComponentsHint,
    path: T,
}

impl<T> CacheComponentsHint<T> {
    /// Create this structure
    /// # SAFETY
    /// Ensure that the given components-hint is actually for the namespace path in question.
    #[inline]
    pub const unsafe fn new_unchecked(nspath: T, components_hint: NamespaceComponentsHint) -> Self {
        Self {
            cached_hints: components_hint,
            path: nspath,
        }
    }

    /// Re-obtain the inner namespace path for further actions, if needed.
    #[inline]
    pub const fn get_path(&self) -> &T {
        &self.path
    }

    /// Extract the original namespace path, removing the cached components.
    #[inline]
    pub fn into_inner(self) -> T {
        self.path
    }
}

impl<T: NamespacePath> CacheComponentsHint<T> {
    #[inline]
    pub fn new(nspath: T) -> Self {
        let components_hint = nspath.components_hint();
        // SAFETY: we extract the components_hint directly from the namespace path
        unsafe { Self::new_unchecked(nspath, components_hint) }
    }
}

impl<T: NamespacePath + ?Sized> NamespacePath for CacheComponentsHint<T> {
    #[inline]
    fn components(&self) -> impl IntoIterator<Item = crate::NamespaceComponent<'_>> {
        self.path.components()
    }

    #[inline]
    fn components_hint(&self) -> NamespaceComponentsHint {
        self.cached_hints
    }
}

impl<T: RawNamespacePath + ?Sized> RawNamespacePath for CacheComponentsHint<T> {
    #[inline]
    fn raw_components(&self) -> impl IntoIterator<Item = impl core::fmt::Display> {
        self.path.raw_components()
    }
}

/// See [`NamespacePath::join`] and [`RawNamespacePath::raw_join`]
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub struct Join<Prefix, Suffix: ?Sized> {
    prefix: Prefix,
    suffix: Suffix,
}

impl<Prefix, Suffix: ?Sized> Join<Prefix, Suffix> {
    #[inline]
    pub const fn new(prefix: Prefix, suffix: Suffix) -> Self
    where
        Suffix: Sized,
    {
        Join { prefix, suffix }
    }

    /// Split the prefix and suffix back out of the chained path.
    #[inline]
    pub fn deconstruct(Join { prefix, suffix }: Self) -> (Prefix, Suffix)
    where
        Suffix: Sized,
    {
        (prefix, suffix)
    }
}

impl<Prefix: NamespacePath, Suffix: NamespacePath + ?Sized> NamespacePath for Join<Prefix, Suffix> {
    #[inline]
    fn components(&self) -> impl IntoIterator<Item = crate::NamespaceComponent<'_>> {
        self.prefix
            .components()
            .into_iter()
            .chain(self.suffix.components())
    }

    #[inline]
    fn components_hint(&self) -> NamespaceComponentsHint {
        self.prefix
            .components_hint()
            .with_appended(self.suffix.components_hint())
    }
}

impl<Prefix: RawNamespacePath, Suffix: RawNamespacePath + ?Sized> RawNamespacePath
    for Join<Prefix, Suffix>
{
    #[inline]
    fn raw_components(&self) -> impl IntoIterator<Item = impl core::fmt::Display> {
        self.prefix
            .raw_components()
            .into_iter()
            .map(either::Left)
            .chain(self.suffix.raw_components().into_iter().map(either::Right))
    }
}

/// See [`NamespacePath::fallback`] and [`RawNamespacePath::raw_fallback`]
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub struct Fallback<P, F: ?Sized> {
    primary: P,
    fallback: F,
}

impl<P, F: ?Sized> Fallback<P, F> {
    #[inline]
    pub const fn new(primary: P, fallback: F) -> Self
    where
        F: Sized,
    {
        Self { primary, fallback }
    }
}

impl<P: NamespacePath, F: NamespacePath + ?Sized> NamespacePath for Fallback<P, F> {
    #[inline]
    fn components(&self) -> impl IntoIterator<Item = crate::NamespaceComponent<'_>> {
        empty_fallback_chain(self.primary.components(), self.fallback.components())
    }

    #[inline]
    fn components_hint(&self) -> NamespaceComponentsHint {
        self.primary
            .components_hint()
            .has_components_or_else(self.fallback.components_hint())
    }
}

impl<P: RawNamespacePath, F: RawNamespacePath + ?Sized> RawNamespacePath for Fallback<P, F> {
    #[inline]
    fn raw_components(&self) -> impl IntoIterator<Item = impl core::fmt::Display> {
        empty_fallback_chain(
            self.primary.raw_components().into_iter().map(either::Left),
            self.fallback
                .raw_components()
                .into_iter()
                .map(either::Right),
        )
    }
}

/// See [`NamespacePath::maybe_fallback`] and [`RawNamespacePath::raw_maybe_fallback`]
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub struct MaybeFallback<P, F> {
    primary: P,
    maybe_fallback: Option<F>,
}

impl<P, F> MaybeFallback<P, F> {
    #[inline]
    pub const fn new(primary: P, maybe_fallback: Option<F>) -> Self {
        Self {
            primary,
            maybe_fallback,
        }
    }

    #[inline]
    pub const fn with_fallback(primary: P, fallback: F) -> Self {
        Self {
            primary,
            maybe_fallback: Some(fallback),
        }
    }

    #[inline]
    pub const fn without_fallback(primary: P) -> Self {
        Self {
            primary,
            maybe_fallback: None,
        }
    }
}

impl<P: NamespacePath, F: NamespacePath> NamespacePath for MaybeFallback<P, F> {
    #[inline]
    fn components(&self) -> impl IntoIterator<Item = crate::NamespaceComponent<'_>> {
        maybe_empty_fallback_chain(
            self.primary.components(),
            self.maybe_fallback.as_ref().map(NamespacePath::components),
        )
    }

    #[inline]
    fn components_hint(&self) -> NamespaceComponentsHint {
        match &self.maybe_fallback {
            Some(fallback) => self
                .primary
                .components_hint()
                .has_components_or_else(fallback.components_hint()),
            None => self.primary.components_hint(),
        }
    }
}

impl<P: RawNamespacePath, F: RawNamespacePath> RawNamespacePath for MaybeFallback<P, F> {
    #[inline]
    fn raw_components(&self) -> impl IntoIterator<Item = impl core::fmt::Display> {
        maybe_empty_fallback_chain(
            self.primary.raw_components().into_iter().map(either::Left),
            self.maybe_fallback
                .as_ref()
                .map(RawNamespacePath::raw_components)
                .map(IntoIterator::into_iter)
                .map(|i| i.map(either::Right)),
        )
    }
}

// nstree - nested namespace string-generating abstraction library
// Copyright (C) 2025  Matti <infomorphic-matti at protonmail dot com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
// ------
// SPDX-License-Identifier: GPL-3.0-or-later