1use crate::branch::{BranchInfo, Node, Topology};
2
3#[must_use]
5pub fn render(topology: &Topology) -> String {
6 let mut lines = Vec::new();
7
8 render_node(&topology.root, &mut lines, "", true);
10
11 lines.join("\n") + "\n"
12}
13
14fn render_node(node: &Node, lines: &mut Vec<String>, prefix: &str, is_root: bool) {
15 let node_text = format_node(node);
17
18 if is_root {
19 lines.push(node_text);
20 } else {
21 unreachable!()
23 }
24
25 render_children(&node.children, lines, prefix);
27}
28
29fn render_children(children: &[Node], lines: &mut Vec<String>, prefix: &str) {
30 let count = children.len();
31 for (i, node) in children.iter().enumerate() {
32 let is_last = i == count - 1;
33 let connector = if is_last { "└─ " } else { "├─ " };
34 let child_prefix = if is_last { " " } else { "│ " };
35
36 lines.push(format!("{prefix}{connector}{}", format_node(node)));
37
38 if !node.children.is_empty() {
39 render_children(&node.children, lines, &format!("{prefix}{child_prefix}"));
40 }
41 }
42}
43
44fn format_branch(info: &BranchInfo) -> String {
46 match info.pr {
47 Some(pr) => format!("{} #{pr}", info.name),
48 None => info.name.clone(),
49 }
50}
51
52fn format_node(node: &Node) -> String {
54 if node.branches.is_empty() {
55 format!("[{}]", node.commit)
57 } else if node.branches.len() == 1 {
58 let info = &node.branches[0];
60 if info.ahead > 0 {
61 format!("{} (+{})", format_branch(info), info.ahead)
62 } else {
63 format_branch(info)
64 }
65 } else {
66 let names: Vec<String> = node.branches.iter().map(format_branch).collect();
68 let ahead = node.branches[0].ahead;
69 if ahead > 0 {
70 format!("{} (+{})", names.join(" "), ahead)
71 } else {
72 names.join(" ")
73 }
74 }
75}