story_tracker_cli/subcommands/
generate.rs

1use 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}