Skip to content

Commit b1ec89f

Browse files
committed
fix(branch-options): dedupe GIT_BRANCH manual input entries
1 parent 9509190 commit b1ec89f

File tree

2 files changed

+95
-21
lines changed

2 files changed

+95
-21
lines changed

src/jenkins/client.rs

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ pub struct BuildStatus {
4545
pub in_queue: bool,
4646
}
4747

48+
#[doc(hidden)]
49+
pub struct BranchOptionsInput<'a> {
50+
pub branches: &'a [String],
51+
pub default_branch: Option<&'a str>,
52+
pub current_branch: Option<&'a str>,
53+
pub manual_input: &'a str,
54+
}
55+
4856
/// Represents a Jenkins client.
4957
pub struct JenkinsClient {
5058
pub base_url: String,
@@ -672,6 +680,34 @@ impl JenkinsClient {
672680
Ok(parameters)
673681
}
674682

683+
/// Build branch picker options for GIT_BRANCH-like parameters.
684+
///
685+
/// Order priority is:
686+
/// 1) manual input
687+
/// 2) default branch
688+
/// 3) current local branch
689+
/// 4) remaining remote branches
690+
///
691+
/// Remove duplicate branch names while preserving first-seen order in the prioritized list.
692+
#[doc(hidden)]
693+
pub fn build_branch_options(input: BranchOptionsInput<'_>) -> Vec<String> {
694+
let mut options = Vec::new();
695+
options.push(input.manual_input.to_string());
696+
if let Some(default_branch) = input.default_branch.filter(|value| !value.is_empty()) {
697+
options.push(default_branch.to_string());
698+
}
699+
if let Some(current_branch) = input.current_branch.filter(|value| !value.is_empty()) {
700+
options.push(current_branch.to_string());
701+
}
702+
options.extend(input.branches.iter().cloned());
703+
704+
let mut seen = HashSet::new();
705+
options
706+
.into_iter()
707+
.filter(|branch| seen.insert(branch.clone()))
708+
.collect()
709+
}
710+
675711
/// Prompts the user to enter values for the given parameter definitions.
676712
///
677713
/// # Arguments
@@ -687,7 +723,7 @@ impl JenkinsClient {
687723
use dialoguer::theme::ColorfulTheme; // ColorfulTheme/SimpleTheme
688724
use std::io::{self, Write};
689725
let mut parameters = HashMap::new();
690-
let mut branches = get_git_branches();
726+
let branches = get_git_branches();
691727
let branch_names = ["GIT_BRANCH", "gitBranch"];
692728

693729
// for string, text, password
@@ -852,30 +888,21 @@ impl JenkinsClient {
852888
.iter()
853889
.any(|&b| name.to_lowercase().contains(&b.to_lowercase()))
854890
{
855-
// branches.retain(|branch| branch != &default_value); // Remove branch
856891
// If the parameter name contains GIT_BRANCH
857892
let current_branch = get_current_branch();
858-
// Add `manual input` option at the front
859893
let manual_input = t!("manual-input");
860-
branches.insert(0, manual_input.clone());
861-
// Move current_branch to the front
862-
if let Some(pos) = branches.iter().position(|b| b == &current_branch) {
863-
branches.remove(pos);
864-
branches.insert(1, current_branch.clone());
865-
}
866-
// Move default branch to the front
867-
if !default_value.is_empty() {
868-
if let Some(pos) = branches.iter().position(|b| b == &default_value) {
869-
branches.remove(pos);
870-
}
871-
branches.insert(1, default_value.clone());
872-
}
894+
let branch_options = Self::build_branch_options(BranchOptionsInput {
895+
branches: &branches,
896+
default_branch: Some(&default_value),
897+
current_branch: Some(&current_branch),
898+
manual_input: &manual_input,
899+
});
873900

874901
// Priority: default_value, then current_branch, finally use 0
875-
let default_selection = branches
902+
let default_selection = branch_options
876903
.iter()
877904
.position(|b| b == &default_value)
878-
.or_else(|| branches.iter().position(|b| b == &current_branch))
905+
.or_else(|| branch_options.iter().position(|b| b == &current_branch))
879906
.unwrap_or(0);
880907
let custom_theme = ColorfulTheme {
881908
// active_item_style: console::Style::new(), // Cancel default style
@@ -888,21 +915,21 @@ impl JenkinsClient {
888915
t!("prompt-select-branch", "name" => &fmt_name),
889916
fmt_desc
890917
))
891-
.items(&branches)
918+
.items(&branch_options)
892919
.default(default_selection)
893920
.vim_mode(true) // Esc, j|k
894921
.with_initial_text("")
895922
.interact()
896923
}));
897924

898925
match selected_idx {
899-
Some(idx) if branches[idx] == manual_input => {
926+
Some(idx) if branch_options[idx] == manual_input => {
900927
match prompt_user_input(&fmt_name, &fmt_desc, "", trim) {
901928
Some(v) => (v, ParamType::String),
902929
None => return None, // Ctrl+C in manual input
903930
}
904931
}
905-
Some(idx) => (branches[idx].clone(), ParamType::String),
932+
Some(idx) => (branch_options[idx].clone(), ParamType::String),
906933
None => return None, // Ctrl+C pressed - go back
907934
}
908935
} else {

tests/test_branch_options.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use jenkins::jenkins::client::{BranchOptionsInput, JenkinsClient};
2+
3+
#[test]
4+
fn build_branch_options_keeps_manual_input_unique() {
5+
let manual_input = "[*] Manual input";
6+
let branches = vec![
7+
"main".to_string(),
8+
manual_input.to_string(),
9+
"release".to_string(),
10+
"main".to_string(),
11+
];
12+
13+
let options = JenkinsClient::build_branch_options(BranchOptionsInput {
14+
branches: &branches,
15+
default_branch: Some("main"),
16+
current_branch: Some("main"),
17+
manual_input,
18+
});
19+
20+
assert_eq!(
21+
options,
22+
vec![manual_input.to_string(), "main".to_string(), "release".to_string(),]
23+
);
24+
}
25+
26+
#[test]
27+
fn build_branch_options_prioritizes_default_then_current() {
28+
let manual_input = "[*] Manual input";
29+
let branches = vec!["develop".to_string(), "main".to_string(), "release".to_string()];
30+
31+
let options = JenkinsClient::build_branch_options(BranchOptionsInput {
32+
branches: &branches,
33+
default_branch: Some("release"),
34+
current_branch: Some("develop"),
35+
manual_input,
36+
});
37+
38+
assert_eq!(
39+
options,
40+
vec![
41+
manual_input.to_string(),
42+
"release".to_string(),
43+
"develop".to_string(),
44+
"main".to_string(),
45+
]
46+
);
47+
}

0 commit comments

Comments
 (0)