haz-domain 0.2.0

Shared domain types for the haz task runner (identifiers, paths, errors).
Documentation
//! Task-set scope selection for `haz run` and `haz check`.
//!
//! The scope flags `--all` (`EXEC-024`) and `--workspace`
//! (`EXEC-025`) are mutually exclusive; absent either, the selection
//! defaults to the bearing project when one applies (`EXEC-022`),
//! otherwise the whole workspace (`EXEC-023` default-target context,
//! `AUX-009`). This module models that tri-state as a single value
//! object so the mutually-exclusive pair of booleans cannot encode
//! the rejected "both set" combination beyond the parser boundary.

/// How broadly a task-set selection ranges over the workspace.
///
/// Produced from the `--all` / `--workspace` scope flags
/// (`EXEC-024`, `EXEC-025`, `AUX-009`) and consumed by `haz run`
/// target resolution and `haz check` validation counting. The two
/// flags are mutually exclusive; representing the choice as one enum
/// keeps the rejected "both flags set" combination unrepresentable
/// in every downstream consumer.
///
/// # Examples
///
/// ```
/// use haz_domain::scope::Scope;
///
/// assert_eq!(Scope::default(), Scope::Default);
/// assert_eq!(Scope::from_flags(false, false), Some(Scope::Default));
/// assert_eq!(Scope::from_flags(true, false), Some(Scope::All));
/// assert_eq!(Scope::from_flags(false, true), Some(Scope::Workspace));
/// assert_eq!(Scope::from_flags(true, true), None);
/// ```
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Scope {
    /// No scope flag: select the bearing project when one applies
    /// (`EXEC-022`), otherwise the whole workspace (`EXEC-023`,
    /// `AUX-009`).
    #[default]
    Default,
    /// `--all`: every task in the current bearing scope (`EXEC-024`),
    /// i.e. the bearing project when one applies, otherwise every
    /// project in the workspace.
    All,
    /// `--workspace`: every task in every project, regardless of cwd
    /// (`EXEC-025`).
    Workspace,
}

impl Scope {
    /// Reduce the mutually-exclusive `--all` / `--workspace` flag
    /// pair to a single [`Scope`].
    ///
    /// Returns [`None`] for the rejected `(true, true)` combination
    /// (the `EXEC-024` / `EXEC-025` / `AUX-009` scope mutex); the
    /// caller maps that to its own diagnostic. The three accepted
    /// combinations map to [`Scope::Default`], [`Scope::All`], and
    /// [`Scope::Workspace`] respectively.
    #[must_use]
    pub fn from_flags(all: bool, workspace: bool) -> Option<Self> {
        match (all, workspace) {
            (false, false) => Some(Self::Default),
            (true, false) => Some(Self::All),
            (false, true) => Some(Self::Workspace),
            (true, true) => None,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Scope;

    #[test]
    fn default_is_the_no_flag_variant() {
        assert_eq!(Scope::default(), Scope::Default);
    }

    #[test]
    fn from_flags_maps_neither_to_default() {
        assert_eq!(Scope::from_flags(false, false), Some(Scope::Default));
    }

    #[test]
    fn from_flags_maps_all_only_to_all() {
        assert_eq!(Scope::from_flags(true, false), Some(Scope::All));
    }

    #[test]
    fn from_flags_maps_workspace_only_to_workspace() {
        assert_eq!(Scope::from_flags(false, true), Some(Scope::Workspace));
    }

    #[test]
    fn from_flags_rejects_both_set() {
        // The EXEC-024 / EXEC-025 / AUX-009 scope mutex: the parser
        // never produces this pair, and the type cannot encode the
        // result.
        assert_eq!(Scope::from_flags(true, true), None);
    }
}