1use crate::constants::max_bytes_label;
2
3pub struct FormatTextArgs<'a> {
4 pub path: &'a str,
5 pub offset: usize,
6 pub lines: &'a [String],
7 pub total_lines: usize,
8 pub more: bool,
9 pub byte_cap: bool,
10}
11
12pub fn format_text(args: FormatTextArgs<'_>) -> String {
13 let header = format!(
14 "<path>{}</path>\n<type>file</type>\n<content>",
15 args.path
16 );
17
18 if args.lines.is_empty() && args.total_lines == 0 {
19 return format!("{}\n(File exists but is empty)\n</content>", header);
20 }
21
22 let body: String = args
23 .lines
24 .iter()
25 .enumerate()
26 .map(|(i, line)| format!("{}: {}", args.offset + i, line))
27 .collect::<Vec<_>>()
28 .join("\n");
29 let last = args.offset + args.lines.len() - 1;
30 let next = last + 1;
31
32 let hint = if args.byte_cap {
33 let pct = if args.total_lines > 0 {
34 (last as f64 / args.total_lines as f64 * 100.0).round() as i64
35 } else {
36 0
37 };
38 let remaining = args.total_lines.saturating_sub(last);
39 format!(
40 "(Output capped at {}. Showing lines {}-{} of {} · {}% covered · {} lines remaining. Next offset: {}.)",
41 max_bytes_label(),
42 args.offset,
43 last,
44 args.total_lines,
45 pct,
46 remaining,
47 next
48 )
49 } else if args.more {
50 let pct = (last as f64 / args.total_lines as f64 * 100.0).round() as i64;
51 let remaining = args.total_lines.saturating_sub(last);
52 format!(
53 "(Showing lines {}-{} of {} · {}% covered · {} lines remaining. Next offset: {}.)",
54 args.offset, last, args.total_lines, pct, remaining, next
55 )
56 } else {
57 format!("(End of file · {} lines total)", args.total_lines)
58 };
59
60 format!("{}\n{}\n\n{}\n</content>", header, body, hint)
61}
62
63pub struct FormatDirArgs<'a> {
64 pub path: &'a str,
65 pub entries: &'a [String],
66 pub offset: usize,
67 pub total_entries: usize,
68 pub more: bool,
69}
70
71pub fn format_directory(args: FormatDirArgs<'_>) -> String {
72 let header = format!(
73 "<path>{}</path>\n<type>directory</type>\n<entries>",
74 args.path
75 );
76 let body = args.entries.join("\n");
77 let last = args.offset + args.entries.len() - 1;
78 let next = last + 1;
79 let remaining = args.total_entries.saturating_sub(last);
80 let hint = if args.more {
81 format!(
82 "(Showing {} of {} entries · {} remaining. Next offset: {}.)",
83 args.entries.len(),
84 args.total_entries,
85 remaining,
86 next
87 )
88 } else {
89 format!("({} entries)", args.total_entries)
90 };
91 format!("{}\n{}\n\n{}\n</entries>", header, body, hint)
92}
93
94pub fn format_attachment(kind: &str) -> String {
95 format!("{} read successfully", kind)
96}