govctl 0.15.0

Project governance CLI for RFC, ADR, and Work Item management
use super::{OwnedEditAction, OwnedMatchOptions};
use crate::EditActionArgs;
use crate::diagnostic::{Diagnostic, DiagnosticCode, DiagnosticResult};

fn conflicting_edit_flag_error(action: &str, flag: &str) -> Diagnostic {
    Diagnostic::new(
        DiagnosticCode::E0802ConflictingArgs,
        format!("Cannot use {flag} with --{action}"),
        "edit action",
    )
}

fn reject_selector_flags_for_value_action(
    action: &str,
    args: &EditActionArgs,
) -> DiagnosticResult<()> {
    if args.regex {
        return Err(conflicting_edit_flag_error(action, "--regex"));
    }
    if args.all {
        return Err(conflicting_edit_flag_error(action, "--all"));
    }
    Ok(())
}

pub(crate) fn owned_edit_action(args: &EditActionArgs) -> DiagnosticResult<OwnedEditAction> {
    let action_count = usize::from(args.set.is_some())
        + usize::from(args.add.is_some())
        + usize::from(args.tick.is_some())
        + usize::from(args.remove.is_some());

    if action_count == 0 {
        return Err(Diagnostic::new(
            DiagnosticCode::E0801MissingRequiredArg,
            "exactly one edit action is required",
            "edit action",
        ));
    }

    if action_count > 1 {
        return Err(Diagnostic::new(
            DiagnosticCode::E0802ConflictingArgs,
            "Cannot use multiple edit actions at once",
            "edit action",
        ));
    }

    if let Some(value) = &args.set {
        reject_selector_flags_for_value_action("set", args)?;
        return Ok(OwnedEditAction::Set {
            value: Some(value.clone()),
            stdin: args.stdin,
        });
    }
    if let Some(value) = &args.add {
        reject_selector_flags_for_value_action("add", args)?;
        return Ok(OwnedEditAction::Add {
            value: Some(value.clone()),
            stdin: args.stdin,
        });
    }
    if let Some(status) = args.tick {
        if args.stdin {
            return Err(conflicting_edit_flag_error("tick", "--stdin"));
        }
        if args.all {
            return Err(Diagnostic::new(
                DiagnosticCode::E0802ConflictingArgs,
                "Cannot use --all with --tick; tick requires a single target",
                "edit action",
            ));
        }
        if args.regex {
            return Err(conflicting_edit_flag_error("tick", "--regex"));
        }
        return Ok(OwnedEditAction::Tick {
            match_opts: OwnedMatchOptions {
                pattern: None,
                at: None,
                exact: false,
                regex: false,
                all: false,
            },
            status,
        });
    }
    if let Some(remove) = &args.remove {
        if args.stdin {
            return Err(conflicting_edit_flag_error("remove", "--stdin"));
        }
        if args.all && remove.is_some() {
            return Err(Diagnostic::new(
                DiagnosticCode::E0802ConflictingArgs,
                "Cannot combine --all with a remove value",
                "edit action",
            ));
        }
        if args.all && args.regex {
            return Err(Diagnostic::new(
                DiagnosticCode::E0802ConflictingArgs,
                "Cannot combine --all with --regex",
                "edit action",
            ));
        }
        if args.regex && remove.is_none() {
            return Err(Diagnostic::new(
                DiagnosticCode::E0801MissingRequiredArg,
                "--regex requires a remove pattern",
                "edit action",
            ));
        }
        return Ok(OwnedEditAction::Remove {
            match_opts: OwnedMatchOptions {
                pattern: remove.clone(),
                at: None,
                exact: !args.regex,
                regex: args.regex,
                all: args.all,
            },
        });
    }
    unreachable!("action_count guarantees exactly one action branch")
}