cargo-mend 0.12.2

Opinionated visibility auditing for Rust crates and workspaces
use std::collections::BTreeSet;

use super::cli::FixCli;
use super::cli::FixExecution;
use super::cli::FixRequest;

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum FixKind {
    ShortenImport,
    PreferModuleImport,
    InlinePathQualifiedType,
    NarrowToPubCrate,
    FieldVisibility,
    PubUse,
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct FixSelection {
    fix_kinds: BTreeSet<FixKind>,
}

impl From<&FixCli> for FixSelection {
    fn from(fix_cli: &FixCli) -> Self {
        match fix_cli.execution {
            FixExecution::ApplyAll | FixExecution::PreviewAll => Self::all_fix_kinds(),
            FixExecution::ReadOnly => Self::default(),
            FixExecution::ApplyRequested | FixExecution::PreviewRequested => {
                let mut fix_kinds = BTreeSet::new();
                if fix_cli.includes(FixRequest::Mend) {
                    fix_kinds.insert(FixKind::ShortenImport);
                    fix_kinds.insert(FixKind::PreferModuleImport);
                    fix_kinds.insert(FixKind::InlinePathQualifiedType);
                    fix_kinds.insert(FixKind::NarrowToPubCrate);
                    fix_kinds.insert(FixKind::FieldVisibility);
                }
                if fix_cli.includes(FixRequest::PubUse) {
                    fix_kinds.insert(FixKind::PubUse);
                }
                Self { fix_kinds }
            },
        }
    }
}

impl FixSelection {
    fn all_fix_kinds() -> Self {
        let mut fix_kinds = BTreeSet::new();
        fix_kinds.insert(FixKind::ShortenImport);
        fix_kinds.insert(FixKind::PreferModuleImport);
        fix_kinds.insert(FixKind::InlinePathQualifiedType);
        fix_kinds.insert(FixKind::NarrowToPubCrate);
        fix_kinds.insert(FixKind::FieldVisibility);
        fix_kinds.insert(FixKind::PubUse);
        Self { fix_kinds }
    }

    pub(crate) fn contains(&self, kind: FixKind) -> bool { self.fix_kinds.contains(&kind) }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum OperationIntent {
    ReadOnly,
    DryRun,
    Apply,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct OperationMode {
    pub intent: OperationIntent,
    pub fixes:  FixSelection,
}

impl From<&FixCli> for OperationMode {
    fn from(fix_cli: &FixCli) -> Self {
        let fixes = FixSelection::from(fix_cli);
        match fix_cli.execution {
            FixExecution::PreviewRequested | FixExecution::PreviewAll => Self {
                intent: OperationIntent::DryRun,
                fixes,
            },
            FixExecution::ReadOnly => Self {
                intent: OperationIntent::ReadOnly,
                fixes,
            },
            FixExecution::ApplyRequested | FixExecution::ApplyAll => Self {
                intent: OperationIntent::Apply,
                fixes,
            },
        }
    }
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeSet;

    use super::FixKind;
    use super::OperationIntent;
    use super::OperationMode;
    use crate::cli::FixCli;
    use crate::cli::FixExecution;
    use crate::cli::FixRequest;

    #[test]
    fn operation_mode_is_read_only_without_fix_flags() {
        let cli = FixCli::default();
        let operation_mode = OperationMode::from(&cli);
        assert_eq!(operation_mode.intent, OperationIntent::ReadOnly);
    }

    #[test]
    fn operation_mode_dry_run_alone_implies_all_fix_kinds() {
        let cli = FixCli {
            execution: FixExecution::PreviewAll,
            ..FixCli::default()
        };
        let operation_mode = OperationMode::from(&cli);
        assert_eq!(operation_mode.intent, OperationIntent::DryRun);
        assert!(operation_mode.fixes.contains(FixKind::ShortenImport));
        assert!(operation_mode.fixes.contains(FixKind::PreferModuleImport));
        assert!(
            operation_mode
                .fixes
                .contains(FixKind::InlinePathQualifiedType)
        );
        assert!(operation_mode.fixes.contains(FixKind::NarrowToPubCrate));
        assert!(operation_mode.fixes.contains(FixKind::PubUse));
    }

    #[test]
    fn operation_mode_allows_previewing_multiple_fix_kinds() {
        let cli = FixCli {
            execution:       FixExecution::PreviewRequested,
            requested_fixes: BTreeSet::from([FixRequest::Mend, FixRequest::PubUse]),
        };
        let operation_mode = OperationMode::from(&cli);
        assert_eq!(operation_mode.intent, OperationIntent::DryRun);
        assert!(operation_mode.fixes.contains(FixKind::ShortenImport));
        assert!(operation_mode.fixes.contains(FixKind::NarrowToPubCrate));
        assert!(operation_mode.fixes.contains(FixKind::PubUse));
    }

    #[test]
    fn operation_mode_allows_applying_multiple_fix_kinds() {
        let cli = FixCli {
            execution:       FixExecution::ApplyRequested,
            requested_fixes: BTreeSet::from([FixRequest::Mend, FixRequest::PubUse]),
        };
        let operation_mode = OperationMode::from(&cli);
        assert_eq!(operation_mode.intent, OperationIntent::Apply);
        assert!(operation_mode.fixes.contains(FixKind::ShortenImport));
        assert!(operation_mode.fixes.contains(FixKind::NarrowToPubCrate));
        assert!(operation_mode.fixes.contains(FixKind::PubUse));
    }
}