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