story_tracker_cli/subcommands/
generate.rs1use pivotal_tracker::{
2 client::Client,
3 story::{GetStoryOptions, Story, StoryID},
4};
5use slug::slugify;
6use std::error::Error;
7
8pub struct RunOptions<'a> {
9 pub client: &'a Client,
10 pub story_id: &'a String,
11}
12
13pub async fn run(options: RunOptions<'_>) -> Result<(), Box<dyn Error>> {
14 let RunOptions { story_id, client } = options;
15 let story_id = story_id
16 .parse::<StoryID>()
17 .expect(&format!("Invalid story ID in {story_id}"));
18
19 eprintln!("Getting story...");
20
21 let story = client
22 .get_story(GetStoryOptions { id: story_id })
23 .await
24 .expect("Failed to get story");
25
26 eprintln!("Generating branch name...");
27
28 let branch_name = branch_name(&story);
29
30 println!("{branch_name}");
31
32 Ok(())
33}
34
35pub fn branch_name(story: &Story) -> String {
36 let story_slug = slugify(story.name.trim());
37 let mut story_name_words = story_slug.split('-').collect::<Vec<_>>();
38 let mut branch_name_parts = vec![
39 format!("{}/{}", story.story_type, story_name_words.remove(0)),
40 format!("#{}", story.id),
41 ];
42
43 loop {
44 if story_name_words.is_empty() {
45 break;
46 }
47
48 let branch_length =
49 branch_name_parts.iter().fold(0, |acc, x| acc + x.len())
50 + branch_name_parts.len()
51 - 1;
52
53 if branch_length + story_name_words[0].len() + 1 > 50 {
54 break;
55 }
56
57 branch_name_parts.insert(
58 branch_name_parts.len() - 1,
59 story_name_words.remove(0).into(),
60 );
61 }
62
63 branch_name_parts.join("-")
64}