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