tier 0.1.17

Rust configuration library for layered TOML, env, and CLI settings
Documentation
use std::fmt::{self, Display, Formatter};

use super::super::ValidationCheck;
use super::super::paths::{
    normalize_check_path_group, normalize_metadata_path, try_normalize_metadata_path,
};

impl ValidationCheck {
    /// Returns a stable machine-readable rule identifier.
    #[must_use]
    pub fn code(&self) -> &'static str {
        match self {
            Self::AtLeastOneOf { .. } => "at_least_one_of",
            Self::ExactlyOneOf { .. } => "exactly_one_of",
            Self::MutuallyExclusive { .. } => "mutually_exclusive",
            Self::RequiredWith { .. } => "required_with",
            Self::RequiredIf { .. } => "required_if",
        }
    }

    pub(in crate::metadata) fn normalize(self) -> Option<Self> {
        match self {
            Self::AtLeastOneOf { paths } => {
                normalize_check_path_group(paths).map(|paths| Self::AtLeastOneOf { paths })
            }
            Self::ExactlyOneOf { paths } => {
                normalize_check_path_group(paths).map(|paths| Self::ExactlyOneOf { paths })
            }
            Self::MutuallyExclusive { paths } => {
                normalize_check_path_group(paths).map(|paths| Self::MutuallyExclusive { paths })
            }
            Self::RequiredWith { path, requires } => {
                let path = normalize_metadata_path(&path);
                let requires = normalize_check_path_group(requires)?;
                Some(Self::RequiredWith { path, requires })
            }
            Self::RequiredIf {
                path,
                equals,
                requires,
            } => {
                let path = normalize_metadata_path(&path);
                let requires = normalize_check_path_group(requires)?;
                Some(Self::RequiredIf {
                    path,
                    equals,
                    requires,
                })
            }
        }
    }

    pub(in crate::metadata) fn prefixed(self, prefix: &str) -> Option<Self> {
        let prefix = normalized_prefix(prefix);
        if prefix.is_empty() {
            return self.normalize();
        }

        let join = |path: String| {
            if path.is_empty() {
                prefix.clone()
            } else {
                format!("{prefix}.{path}")
            }
        };

        match self {
            Self::AtLeastOneOf { paths } => Some(Self::AtLeastOneOf {
                paths: paths.into_iter().map(join).collect(),
            })
            .and_then(Self::normalize),
            Self::ExactlyOneOf { paths } => Some(Self::ExactlyOneOf {
                paths: paths.into_iter().map(join).collect(),
            })
            .and_then(Self::normalize),
            Self::MutuallyExclusive { paths } => Some(Self::MutuallyExclusive {
                paths: paths.into_iter().map(join).collect(),
            })
            .and_then(Self::normalize),
            Self::RequiredWith { path, requires } => Some(Self::RequiredWith {
                path: join(path),
                requires: requires.into_iter().map(join).collect(),
            })
            .and_then(Self::normalize),
            Self::RequiredIf {
                path,
                equals,
                requires,
            } => Some(Self::RequiredIf {
                path: join(path),
                equals,
                requires: requires.into_iter().map(join).collect(),
            })
            .and_then(Self::normalize),
        }
    }
}

impl Display for ValidationCheck {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Self::AtLeastOneOf { paths } => {
                write!(f, "at_least_one_of({})", paths.join(", "))
            }
            Self::ExactlyOneOf { paths } => {
                write!(f, "exactly_one_of({})", paths.join(", "))
            }
            Self::MutuallyExclusive { paths } => {
                write!(f, "mutually_exclusive({})", paths.join(", "))
            }
            Self::RequiredWith { path, requires } => {
                write!(f, "required_with({path} -> {})", requires.join(", "))
            }
            Self::RequiredIf {
                path,
                equals,
                requires,
            } => write!(
                f,
                "required_if({path} == {equals} -> {})",
                requires.join(", ")
            ),
        }
    }
}

fn normalized_prefix(prefix: &str) -> String {
    if prefix.is_empty() {
        return String::new();
    }

    try_normalize_metadata_path(prefix)
        .ok()
        .filter(|normalized| !normalized.is_empty())
        .unwrap_or_else(|| prefix.to_owned())
}