1use owo_colors::OwoColorize;
4use rootcause::prelude::ResultExt;
5use rootcause::report;
6
7pub fn run() -> rootcause::Result<()> {
9 let Some(branch) = ytil_tui::git_branch::select()? else {
10 return Ok(());
11 };
12
13 let title = pr_title_from_branch_name(branch.name_no_origin())?;
14 let pr_url = ytil_gh::pr::create(&title)?;
15 println!("{} title={title:?} pr_url={pr_url:?}", "PR created".green().bold());
16
17 Ok(())
18}
19
20fn pr_title_from_branch_name(branch_name: &str) -> rootcause::Result<String> {
22 let mut parts = branch_name.split('-');
23
24 let x = parts
25 .next()
26 .ok_or_else(|| report!("error malformed branch_name"))
27 .attach_with(|| format!("branch_name={branch_name:?}"))?;
28 let issue_number: usize = x
29 .parse()
30 .context("error parsing issue number")
31 .attach_with(|| format!("branch_name={branch_name:?} issue_number={x:?}"))?;
32
33 let mut title = String::with_capacity(branch_name.len());
34 for (i, word) in parts.enumerate() {
35 if i > 0 {
36 title.push(' ');
37 }
38 if i == 0 {
39 let mut chars = word.chars();
40 if let Some(first) = chars.next() {
41 for c in first.to_uppercase() {
42 title.push(c);
43 }
44 title.push_str(chars.as_str());
45 }
46 } else {
47 title.push_str(word);
48 }
49 }
50
51 if title.is_empty() {
52 Err(report!("error empty title")).attach_with(|| format!("branch_name={branch_name:?}"))?;
53 }
54
55 Ok(format!("[{issue_number}]: {title}"))
56}
57
58#[cfg(test)]
59mod tests {
60 use rstest::rstest;
61 use test_that::prelude::*;
62
63 use super::*;
64
65 #[rstest]
66 #[case("43-foo-bar-baz", "[43]: Foo bar baz")]
67 #[case("1-hello", "[1]: Hello")]
68 #[case("123-long-branch-name-here", "[123]: Long branch name here")]
69 fn test_pr_title_from_branch_name_when_valid_input_formats_correctly(#[case] input: &str, #[case] expected: &str) {
70 assert_that!(pr_title_from_branch_name(input).unwrap(), eq(expected));
71 }
72
73 #[rstest]
74 #[case("abc-foo", "error parsing issue number")]
75 #[case("42", "error empty title")]
76 #[case("", "error parsing issue number")]
77 fn test_pr_title_from_branch_name_when_invalid_input_returns_error(
78 #[case] input: &str,
79 #[case] expected_ctx: &str,
80 ) {
81 assert_that!(
82 pr_title_from_branch_name(input),
83 err(result_of!(
84 |err: &rootcause::Report| err.format_current_context().to_string(),
85 eq(expected_ctx)
86 ))
87 );
88 }
89}