use crate::client::http_client::projects::PatchProjectArgs;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConfigField {
DefaultBranch,
Description,
StackHint,
GhUser,
Worktree,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ClearableField {
Description,
StackHint,
GhUser,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConfigEdit {
Set(ConfigField, String),
Unset(ClearableField),
Tags {
add: Vec<String>,
remove: Vec<String>,
},
}
fn apply_edit(args: &mut PatchProjectArgs, edit: &ConfigEdit) {
match edit {
ConfigEdit::Set(ConfigField::DefaultBranch, v) => args.default_branch = Some(v.clone()),
ConfigEdit::Set(ConfigField::Description, v) => args.description = Some(Some(v.clone())),
ConfigEdit::Set(ConfigField::StackHint, v) => args.stack_hint = Some(Some(v.clone())),
ConfigEdit::Set(ConfigField::GhUser, v) => args.gh_user = Some(Some(v.clone())),
ConfigEdit::Set(ConfigField::Worktree, v) => {
args.worktree = Some(v.eq_ignore_ascii_case("true"))
}
ConfigEdit::Unset(ClearableField::Description) => args.description = Some(None),
ConfigEdit::Unset(ClearableField::StackHint) => args.stack_hint = Some(None),
ConfigEdit::Unset(ClearableField::GhUser) => args.gh_user = Some(None),
ConfigEdit::Tags { add, remove } => {
if !add.is_empty() {
args.tags_add = Some(add.clone());
}
if !remove.is_empty() {
args.tags_remove = Some(remove.clone());
}
}
}
}
pub fn build_patch_args(edit: &ConfigEdit) -> PatchProjectArgs {
let mut args = PatchProjectArgs::default();
apply_edit(&mut args, edit);
args
}
pub fn merge_patch_args(edits: &[ConfigEdit]) -> PatchProjectArgs {
let mut args = PatchProjectArgs::default();
for edit in edits {
apply_edit(&mut args, edit);
}
args
}
pub struct ConfigEditCase {
pub label: &'static str,
pub edit: ConfigEdit,
pub expect_default_branch: Option<&'static str>,
pub expect_description: Option<Option<&'static str>>,
pub expect_stack_hint: Option<Option<&'static str>>,
pub expect_gh_user: Option<Option<&'static str>>,
pub expect_tags_add: &'static [&'static str],
pub expect_tags_remove: &'static [&'static str],
}
pub fn config_edit_cases() -> Vec<ConfigEditCase> {
vec![
ConfigEditCase {
label: "set default_branch",
edit: ConfigEdit::Set(ConfigField::DefaultBranch, "develop".to_string()),
expect_default_branch: Some("develop"),
expect_description: None,
expect_stack_hint: None,
expect_gh_user: None,
expect_tags_add: &[],
expect_tags_remove: &[],
},
ConfigEditCase {
label: "set description",
edit: ConfigEdit::Set(ConfigField::Description, "new desc".to_string()),
expect_default_branch: None,
expect_description: Some(Some("new desc")),
expect_stack_hint: None,
expect_gh_user: None,
expect_tags_add: &[],
expect_tags_remove: &[],
},
ConfigEditCase {
label: "unset description",
edit: ConfigEdit::Unset(ClearableField::Description),
expect_default_branch: None,
expect_description: Some(None),
expect_stack_hint: None,
expect_gh_user: None,
expect_tags_add: &[],
expect_tags_remove: &[],
},
ConfigEditCase {
label: "set stack_hint",
edit: ConfigEdit::Set(ConfigField::StackHint, "rust".to_string()),
expect_default_branch: None,
expect_description: None,
expect_stack_hint: Some(Some("rust")),
expect_gh_user: None,
expect_tags_add: &[],
expect_tags_remove: &[],
},
ConfigEditCase {
label: "unset stack_hint",
edit: ConfigEdit::Unset(ClearableField::StackHint),
expect_default_branch: None,
expect_description: None,
expect_stack_hint: Some(None),
expect_gh_user: None,
expect_tags_add: &[],
expect_tags_remove: &[],
},
ConfigEditCase {
label: "set gh_user",
edit: ConfigEdit::Set(ConfigField::GhUser, "acme-bot".to_string()),
expect_default_branch: None,
expect_description: None,
expect_stack_hint: None,
expect_gh_user: Some(Some("acme-bot")),
expect_tags_add: &[],
expect_tags_remove: &[],
},
ConfigEditCase {
label: "unset gh_user",
edit: ConfigEdit::Unset(ClearableField::GhUser),
expect_default_branch: None,
expect_description: None,
expect_stack_hint: None,
expect_gh_user: Some(None),
expect_tags_add: &[],
expect_tags_remove: &[],
},
ConfigEditCase {
label: "tags add and remove together",
edit: ConfigEdit::Tags {
add: vec!["ml".to_string()],
remove: vec!["oss".to_string()],
},
expect_default_branch: None,
expect_description: None,
expect_stack_hint: None,
expect_gh_user: None,
expect_tags_add: &["ml"],
expect_tags_remove: &["oss"],
},
]
}
pub fn assert_matches_case(args: &PatchProjectArgs, case: &ConfigEditCase) {
assert_eq!(
args.default_branch.as_deref(),
case.expect_default_branch,
"{}: default_branch",
case.label
);
assert_eq!(
args.description.as_ref().map(|o| o.as_deref()),
case.expect_description,
"{}: description",
case.label
);
assert_eq!(
args.stack_hint.as_ref().map(|o| o.as_deref()),
case.expect_stack_hint,
"{}: stack_hint",
case.label
);
assert_eq!(
args.gh_user.as_ref().map(|o| o.as_deref()),
case.expect_gh_user,
"{}: gh_user",
case.label
);
let tags_add: Vec<&str> = args
.tags_add
.as_ref()
.map(|v| v.iter().map(String::as_str).collect())
.unwrap_or_default();
assert_eq!(
tags_add,
case.expect_tags_add.to_vec(),
"{}: tags_add",
case.label
);
let tags_remove: Vec<&str> = args
.tags_remove
.as_ref()
.map(|v| v.iter().map(String::as_str).collect())
.unwrap_or_default();
assert_eq!(
tags_remove,
case.expect_tags_remove.to_vec(),
"{}: tags_remove",
case.label
);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn edit_cases_build_expected_patch_args() {
for case in config_edit_cases() {
let args = build_patch_args(&case.edit);
assert_matches_case(&args, &case);
}
}
#[test]
fn merge_patch_args_combines_independent_field_edits() {
let edits = vec![
ConfigEdit::Set(ConfigField::StackHint, "rust".to_string()),
ConfigEdit::Unset(ClearableField::Description),
];
let args = merge_patch_args(&edits);
assert_eq!(args.stack_hint, Some(Some("rust".to_string())));
assert_eq!(args.description, Some(None));
assert!(args.default_branch.is_none());
}
#[test]
fn build_patch_args_worktree_set_true_false() {
let off = build_patch_args(&ConfigEdit::Set(ConfigField::Worktree, "false".to_string()));
assert_eq!(off.worktree, Some(false));
let on = build_patch_args(&ConfigEdit::Set(ConfigField::Worktree, "true".to_string()));
assert_eq!(on.worktree, Some(true));
}
#[test]
fn build_patch_args_omits_empty_tags_lists() {
let args = build_patch_args(&ConfigEdit::Tags {
add: vec![],
remove: vec![],
});
assert!(args.tags_add.is_none());
assert!(args.tags_remove.is_none());
}
}