Skip to main content

agentic_server/
agentic_output.rs

1const BLUE: &str = "\u{1b}[38;5;75m";
2const GOLD: &str = "\u{1b}[38;5;214m";
3const BRIGHT_WHITE: &str = "\u{1b}[97m";
4const MAGENTA: &str = "\u{1b}[38;5;207m";
5const BOLD: &str = "\u{1b}[1m";
6const RESET: &str = "\u{1b}[0m";
7
8const CODEX_EXAMPLE: &str = "agentic run codex --model Qwen/...";
9const CLAUDE_EXAMPLE: &str = "agentic run claude --upstream http://127.0.0.1:8000";
10
11#[must_use]
12pub fn render_banner(color: bool) -> String {
13    render_box(
14        &["⚡  Agentic API", "    Local agent gateway"],
15        None,
16        BLUE,
17        color,
18        |row| match row {
19            "⚡  Agentic API" if color => {
20                format!("{GOLD}⚡{RESET}  {BLUE}{BOLD}Agentic{RESET} {GOLD}{BOLD}API{RESET}")
21            }
22            "⚡  Agentic API" => row.to_owned(),
23            _ if color => format!("{BLUE}{row}{RESET}"),
24            _ => row.to_owned(),
25        },
26    )
27}
28
29#[must_use]
30pub fn render_help(help: &str, color: bool) -> String {
31    let mut rendered = String::with_capacity(help.len() + 512);
32    rendered.push_str(&render_banner(color));
33    rendered.push_str("\n\n");
34    rendered.push_str(help.trim());
35    if strip_ansi_codes(help).contains("Usage: agentic <COMMAND>") {
36        rendered.push_str("\n\n");
37        rendered.push_str(&render_examples(color));
38    }
39    rendered
40}
41
42fn strip_ansi_codes(value: &str) -> String {
43    let mut plain = String::with_capacity(value.len());
44    let mut in_escape = false;
45    for character in value.chars() {
46        if in_escape {
47            if character.is_ascii_alphabetic() {
48                in_escape = false;
49            }
50        } else if character == '\u{1b}' {
51            in_escape = true;
52        } else {
53            plain.push(character);
54        }
55    }
56    plain
57}
58
59#[must_use]
60pub fn colorize_help(help: &str, color: bool) -> String {
61    if !color {
62        return help.to_owned();
63    }
64
65    help.lines()
66        .map(|line| {
67            let line = line.replace("Usage:", &format!("{BLUE}{BOLD}Usage:{RESET}"));
68            let line = line.replace("<COMMAND>", &format!("{MAGENTA}{BOLD}<COMMAND>{RESET}"));
69            if line.trim_end().ends_with(':') {
70                format!("{BLUE}{BOLD}{line}{RESET}")
71            } else {
72                line
73            }
74        })
75        .collect::<Vec<_>>()
76        .join("\n")
77}
78
79#[must_use]
80pub fn render_examples(color: bool) -> String {
81    render_box(&[CODEX_EXAMPLE, CLAUDE_EXAMPLE], Some("Examples"), GOLD, color, |row| {
82        if color {
83            format!("{BRIGHT_WHITE}{row}{RESET}")
84        } else {
85            row.to_owned()
86        }
87    })
88}
89
90fn render_box<F>(rows: &[&str], title: Option<&str>, border_color: &str, color: bool, style_row: F) -> String
91where
92    F: Fn(&str) -> String,
93{
94    let content_width = rows.iter().map(|row| display_width(row) + 2).max().unwrap_or(2);
95    let inner_width = title.map_or(content_width, |title| content_width.max(display_width(title) + 3));
96    let border = |text: String| {
97        if color {
98            format!("{border_color}{text}{RESET}")
99        } else {
100            text
101        }
102    };
103    let top = match title {
104        Some(title) => format!(
105            "╭─ {title} {}╮",
106            "─".repeat(inner_width.saturating_sub(display_width(title) + 3))
107        ),
108        None => format!("┌{}┐", "─".repeat(inner_width)),
109    };
110    let mut lines = vec![border(top)];
111    for row in rows {
112        let padding = inner_width.saturating_sub(display_width(row) + 2);
113        let content = style_row(row);
114        lines.push(if color {
115            format!(
116                "{border_color}│{RESET} {content}{}{border_color} │{RESET}",
117                " ".repeat(padding)
118            )
119        } else {
120            format!("│ {content}{} │", " ".repeat(padding))
121        });
122    }
123    lines.push(border(format!("╰{}╯", "─".repeat(inner_width))));
124    lines.join("\n")
125}
126
127#[must_use]
128pub fn redact_url(url: &str) -> String {
129    let Some((scheme, rest)) = url.split_once("://") else {
130        return url.to_owned();
131    };
132    let suffix_start = rest.find(['/', '?', '#']).unwrap_or(rest.len());
133    let (authority, suffix) = rest.split_at(suffix_start);
134    let Some((userinfo, host)) = authority.split_once('@') else {
135        return url.to_owned();
136    };
137    let Some((username, _password)) = userinfo.split_once(':') else {
138        return url.to_owned();
139    };
140    format!("{scheme}://{username}:[REDACTED]@{host}{suffix}")
141}
142
143fn display_width(value: &str) -> usize {
144    value
145        .chars()
146        .map(|character| usize::from(character != '\u{fe0f}'))
147        .sum()
148}
149
150#[cfg(test)]
151mod tests {
152    use super::{colorize_help, redact_url, render_banner, render_examples, render_help};
153
154    #[test]
155    fn banner_rows_have_equal_display_width() {
156        let banner = render_banner(false);
157        let widths: Vec<_> = banner.lines().map(display_width).collect();
158        assert!(widths.windows(2).all(|pair| pair[0] == pair[1]));
159    }
160
161    #[test]
162    fn examples_box_rows_have_equal_display_width() {
163        let examples = render_examples(false);
164        let widths: Vec<_> = examples.lines().map(display_width).collect();
165        assert!(widths.windows(2).all(|pair| pair[0] == pair[1]));
166    }
167
168    #[test]
169    fn help_has_logo_and_examples_box() {
170        let help = render_help("Usage: agentic <COMMAND>\n\nCommands:", false);
171        assert!(help.contains("⚡  Agentic API"));
172        assert!(help.contains("╭─ Examples"));
173    }
174
175    #[test]
176    fn styled_root_usage_keeps_examples_box() {
177        let help = render_help("Usage: agentic \u{1b}[35m<COMMAND>\u{1b}[0m", false);
178
179        assert!(help.contains("╭─ Examples"));
180    }
181
182    #[test]
183    fn help_colors_command_placeholder() {
184        let help = colorize_help("Usage: agentic <COMMAND>", true);
185        assert!(help.contains("\u{1b}[38;5;207m\u{1b}[1m<COMMAND>"));
186        assert!(!colorize_help("Usage: agentic <COMMAND>", false).contains('\u{1b}'));
187    }
188
189    #[test]
190    fn banner_can_be_colored() {
191        let banner = render_banner(true);
192        assert!(banner.contains("\u{1b}[38;5;75m"));
193        assert!(banner.contains("\u{1b}[38;5;214m"));
194        assert!(!render_banner(false).contains('\u{1b}'));
195    }
196
197    #[test]
198    fn examples_box_can_be_colored() {
199        let examples = render_examples(true);
200        assert!(examples.contains("\u{1b}[38;5;214m"));
201        assert!(examples.contains("\u{1b}[97magentic run codex"));
202        assert!(!render_examples(false).contains('\u{1b}'));
203    }
204
205    #[test]
206    fn redact_url_hides_password() {
207        assert_eq!(
208            redact_url("postgresql://alice:secret@db.example/agentic"),
209            "postgresql://alice:[REDACTED]@db.example/agentic"
210        );
211        assert_eq!(
212            redact_url("postgresql://alice:secret@db.example"),
213            "postgresql://alice:[REDACTED]@db.example"
214        );
215        assert_eq!(
216            redact_url("postgresql://alice:secret@db.example?sslmode=require"),
217            "postgresql://alice:[REDACTED]@db.example?sslmode=require"
218        );
219    }
220
221    fn display_width(value: &str) -> usize {
222        value.chars().count()
223    }
224}