systemprompt_logging/services/cli/
service.rs1use std::io::Write;
12use std::time::Duration;
13
14use indicatif::{ProgressBar, ProgressStyle};
15use serde::Serialize;
16
17use super::display;
18use super::output::mark_structured_emitted;
19use super::theme::{EmphasisType, ItemStatus, MessageLevel, Theme};
20
21#[derive(Copy, Clone, Debug)]
22pub struct CliService;
23
24impl CliService {
25 pub fn success(message: &str) {
26 display::message(MessageLevel::Success, message);
27 }
28
29 pub fn warning(message: &str) {
30 display::message(MessageLevel::Warning, message);
31 }
32
33 pub fn error(message: &str) {
34 display::message(MessageLevel::Error, message);
35 }
36
37 pub fn info(message: &str) {
38 display::message(MessageLevel::Info, message);
39 }
40
41 pub fn debug(message: &str) {
42 let debug_msg = format!("DEBUG: {message}");
43 display::message(MessageLevel::Info, &debug_msg);
44 }
45
46 pub fn verbose(message: &str) {
47 display::message(MessageLevel::Info, message);
48 }
49
50 pub fn fatal(message: &str, exit_code: i32) -> ! {
51 let fatal_msg = format!("FATAL: {message}");
52 display::message(MessageLevel::Error, &fatal_msg);
53 std::process::exit(exit_code);
54 }
55
56 pub fn section(title: &str) {
57 display::section_header(title);
58 }
59
60 pub fn subsection(title: &str) {
61 display::subsection_header(title);
62 }
63
64 pub fn clear_screen() {
65 let mut stderr = std::io::stderr();
66 if let Err(e) = write!(stderr, "\x1B[2J\x1B[1;1H") {
67 tracing::debug!(error = %e, "cli clear_screen write failed");
68 }
69 }
70
71 pub fn output(content: &str) {
72 let mut stdout = std::io::stdout();
73 if let Err(e) = writeln!(stdout, "{content}") {
74 tracing::debug!(error = %e, "cli output write failed");
75 }
76 }
77
78 pub fn json<T: Serialize>(value: &T) {
79 mark_structured_emitted();
80 match serde_json::to_string_pretty(value) {
81 Ok(json) => {
82 let mut stdout = std::io::stdout();
83 if let Err(e) = writeln!(stdout, "{json}") {
84 tracing::debug!(error = %e, "cli json write failed");
85 }
86 },
87 Err(e) => Self::error(&format!("Failed to format log entry: {e}")),
88 }
89 }
90
91 pub fn json_compact<T: Serialize>(value: &T) {
92 mark_structured_emitted();
93 match serde_json::to_string(value) {
94 Ok(json) => {
95 let mut stdout = std::io::stdout();
96 if let Err(e) = writeln!(stdout, "{json}") {
97 tracing::debug!(error = %e, "cli json_compact write failed");
98 }
99 },
100 Err(e) => Self::error(&format!("Failed to format log entry: {e}")),
101 }
102 }
103
104 pub fn yaml<T: Serialize>(value: &T) {
105 mark_structured_emitted();
106 match serde_yaml::to_string(value) {
107 Ok(yaml) => {
108 let mut stdout = std::io::stdout();
109 if let Err(e) = write!(stdout, "{yaml}") {
110 tracing::debug!(error = %e, "cli yaml write failed");
111 }
112 },
113 Err(e) => Self::error(&format!("Failed to format log entry: {e}")),
114 }
115 }
116
117 pub fn key_value(label: &str, value: &str) {
118 let mut stderr = std::io::stderr();
119 if let Err(e) = writeln!(
120 stderr,
121 "{}: {}",
122 Theme::color(label, EmphasisType::Bold),
123 Theme::color(value, EmphasisType::Highlight)
124 ) {
125 tracing::debug!(error = %e, "cli key_value write failed");
126 }
127 }
128
129 pub fn status_line(label: &str, value: &str, status: ItemStatus) {
130 let mut stderr = std::io::stderr();
131 if let Err(e) = writeln!(
132 stderr,
133 "{} {}: {}",
134 Theme::icon(status),
135 Theme::color(label, EmphasisType::Bold),
136 Theme::color(value, status)
137 ) {
138 tracing::debug!(error = %e, "cli status_line write failed");
139 }
140 }
141
142 pub fn spinner(message: &str) -> ProgressBar {
143 let pb = ProgressBar::new_spinner();
144 pb.set_style(
145 ProgressStyle::default_spinner()
146 .template("{spinner:.cyan} {msg}")
147 .unwrap_or_else(|_| ProgressStyle::default_spinner()),
148 );
149 pb.set_message(message.to_owned());
150 pb.enable_steady_tick(Duration::from_millis(100));
151 pb
152 }
153
154 pub fn progress_bar(total: u64) -> ProgressBar {
155 let pb = ProgressBar::new(total);
156 pb.set_style(
157 ProgressStyle::default_bar()
158 .template(
159 "{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} {msg}",
160 )
161 .unwrap_or_else(|_| ProgressStyle::default_bar())
162 .progress_chars("#>-"),
163 );
164 pb
165 }
166
167 pub fn table(headers: &[&str], rows: &[Vec<String>]) {
168 super::table::render_table(headers, rows);
169 }
170}