git_x/adapters/
formatters.rs1use crate::core::output::Format;
2use crate::domain::{
3 BranchCreationResult, BranchSwitchResult, CleanBranchesResult, HealthLevel, HealthStatus,
4 RepositoryInfo,
5};
6
7pub struct BranchFormatter;
9
10impl BranchFormatter {
11 pub fn new() -> Self {
12 Self
13 }
14
15 pub fn format_creation_result(&self, result: &BranchCreationResult) -> String {
17 let mut output = Format::success(&format!(
18 "Created and switched to branch '{}'",
19 result.branch_name
20 ));
21
22 if let Some(ref backup) = result.backup_branch {
23 output.push_str(&format!("\n๐พ Backup created: {backup}"));
24 }
25
26 if let Some(ref base) = result.base_commit {
27 output.push_str(&format!("\n๐ Based on: {base}"));
28 }
29
30 output
31 }
32
33 pub fn format_clean_result(&self, result: &CleanBranchesResult) -> String {
35 if result.candidates.is_empty() {
36 return "No merged branches to delete.".to_string();
37 }
38
39 if result.dry_run {
40 let mut output = format!(
41 "๐งช (dry run) {} branches would be deleted:\n",
42 result.candidates.len()
43 );
44 for branch in &result.candidates {
45 output.push_str(&format!("(dry run) Would delete: {branch}\n"));
46 }
47 output
48 } else {
49 let mut output = format!("๐งน Deleted {} merged branches:\n", result.deleted.len());
50 for branch in &result.deleted {
51 output.push_str(&format!("โ
Deleted: {branch}\n"));
52 }
53
54 if !result.failed.is_empty() {
55 output.push_str(&format!(
56 "\nโ Failed to delete {} branches:\n",
57 result.failed.len()
58 ));
59 for branch in &result.failed {
60 output.push_str(&format!("โ Failed: {branch}\n"));
61 }
62 }
63
64 output
65 }
66 }
67
68 pub fn format_switch_result(&self, result: &BranchSwitchResult) -> String {
70 let mut output = Format::success(&format!("Switched to branch '{}'", result.new_branch));
71
72 if let Some(ref checkpoint) = result.checkpoint {
73 output.push_str(&format!("\n๐พ Checkpoint created: {checkpoint}"));
74 }
75
76 output
77 }
78}
79
80pub struct RepositoryFormatter;
82
83impl RepositoryFormatter {
84 pub fn new() -> Self {
85 Self
86 }
87
88 pub fn format_repository_info(&self, info: &RepositoryInfo, detailed: bool) -> String {
90 let mut output = String::new();
91
92 output.push_str(&format!("๐๏ธ Repository: {}\n", Format::bold(&info.name)));
93 output.push_str(&format!(
94 "๐ Current branch: {}\n",
95 Format::bold(&info.current_branch)
96 ));
97
98 if let Some(ref upstream) = info.upstream_branch {
100 if info.is_in_sync() {
101 output.push_str(&format!("๐ Upstream: {upstream} (up to date)\n"));
102 } else {
103 let mut status_parts = Vec::new();
104 if info.ahead_count > 0 {
105 status_parts.push(format!("{} ahead", info.ahead_count));
106 }
107 if info.behind_count > 0 {
108 status_parts.push(format!("{} behind", info.behind_count));
109 }
110 output.push_str(&format!(
111 "๐ Upstream: {} ({})\n",
112 upstream,
113 status_parts.join(", ")
114 ));
115 }
116 } else {
117 output.push_str("โ No upstream configured\n");
118 }
119
120 if info.is_clean {
122 output.push_str("โ
Working directory: Clean\n");
123 } else {
124 output.push_str("โ ๏ธ Working directory: Has changes\n");
125 }
126
127 if info.staged_files_count == 0 {
129 output.push_str("๐ Staged files: None\n");
130 } else {
131 output.push_str(&format!(
132 "๐ Staged files: {} file(s)\n",
133 info.staged_files_count
134 ));
135 }
136
137 if detailed {
139 output.push_str(&format!("\n๐ Root path: {}\n", info.root_path));
140 output.push_str(&format!("๐ Status: {}\n", info.status_description()));
141 }
142
143 output
144 }
145
146 pub fn format_health_status(&self, health: &HealthStatus) -> String {
148 let mut output = String::new();
149
150 output.push_str("๐ฅ Repository Health Check\n");
151 output.push_str(&"=".repeat(30));
152 output.push('\n');
153
154 match health.level {
156 HealthLevel::Healthy => {
157 output.push_str(&Format::success("Repository is healthy!"));
158 }
159 HealthLevel::Warning => {
160 output.push_str(&Format::warning(&format!(
161 "Repository has {} warning(s)",
162 health.warnings.len()
163 )));
164 }
165 HealthLevel::Unhealthy => {
166 output.push_str(&Format::error(&format!(
167 "Repository has {} issue(s)",
168 health.issues.len()
169 )));
170 }
171 }
172
173 output.push('\n');
174
175 if !health.issues.is_empty() {
177 output.push_str("\n๐จ Issues:\n");
178 for issue in &health.issues {
179 output.push_str(&format!(" โ {issue}\n"));
180 }
181 }
182
183 if !health.warnings.is_empty() {
185 output.push_str("\nโ ๏ธ Warnings:\n");
186 for warning in &health.warnings {
187 output.push_str(&format!(" โ ๏ธ {warning}\n"));
188 }
189 }
190
191 output.push_str(&format!("\n๐ Summary: {}\n", health.summary()));
193
194 output
195 }
196}
197
198pub struct AnalysisFormatter;
200
201impl AnalysisFormatter {
202 pub fn new() -> Self {
203 Self
204 }
205
206 pub fn format_commit_stats(&self, total_commits: u32, period: &str) -> String {
208 Format::info(&format!("๐ {total_commits} commits in {period}"))
209 }
210
211 pub fn format_contributors(&self, contributors: &[(String, u32)]) -> String {
213 let mut output = String::new();
214 output.push_str("๐ฅ Top Contributors:\n");
215
216 for (i, (name, count)) in contributors.iter().enumerate() {
217 let prefix = match i {
218 0 => "๐ฅ",
219 1 => "๐ฅ",
220 2 => "๐ฅ",
221 _ => "๐ค",
222 };
223 output.push_str(&format!(" {prefix} {name} ({count} commits)\n"));
224 }
225
226 output
227 }
228}
229
230pub struct FormatterUtils;
232
233impl FormatterUtils {
234 pub fn section_header(title: &str) -> String {
236 format!("{}\n{}\n", title, "=".repeat(title.len()))
237 }
238
239 pub fn subsection_header(title: &str) -> String {
241 format!("\n{}\n{}\n", title, "-".repeat(title.len()))
242 }
243
244 pub fn bullet_list(items: &[String], bullet: &str) -> String {
246 items
247 .iter()
248 .map(|item| format!("{bullet} {item}"))
249 .collect::<Vec<_>>()
250 .join("\n")
251 }
252
253 pub fn numbered_list(items: &[String]) -> String {
255 items
256 .iter()
257 .enumerate()
258 .map(|(i, item)| format!("{}. {}", i + 1, item))
259 .collect::<Vec<_>>()
260 .join("\n")
261 }
262}
263
264impl Default for BranchFormatter {
266 fn default() -> Self {
267 Self::new()
268 }
269}
270
271impl Default for RepositoryFormatter {
272 fn default() -> Self {
273 Self::new()
274 }
275}
276
277impl Default for AnalysisFormatter {
278 fn default() -> Self {
279 Self::new()
280 }
281}