systemprompt_cli/shared/command_result/
render.rs1use systemprompt_logging::CliService;
10use systemprompt_models::artifacts::{
11 ChartArtifact, CliArtifact, ListArtifact, PresentationCardArtifact, TableArtifact,
12};
13
14use super::CommandOutput;
15use crate::cli_settings::{CliConfig, OutputFormat};
16
17pub fn render_result(result: &CommandOutput, config: &CliConfig) {
18 if result.should_skip_render() {
19 return;
20 }
21
22 match config.output_format() {
23 OutputFormat::Json => CliService::json(result.artifact()),
24 OutputFormat::Yaml => CliService::yaml(result.artifact()),
25 OutputFormat::Table => render_terminal(result),
26 }
27}
28
29fn render_terminal(result: &CommandOutput) {
30 if let Some(title) = result.title() {
31 CliService::section(title);
32 }
33
34 match result.artifact() {
35 CliArtifact::Text { artifact } => {
36 if result.title().is_none()
37 && let Some(title) = &artifact.title
38 {
39 CliService::section(title);
40 }
41 CliService::output(&artifact.content);
42 },
43 CliArtifact::CopyPasteText { artifact } => {
44 if result.title().is_none()
45 && let Some(title) = &artifact.title
46 {
47 CliService::section(title);
48 }
49 CliService::output(&artifact.content);
50 },
51 CliArtifact::Table { artifact } => render_table(artifact),
52 CliArtifact::List { artifact } => render_list(artifact),
53 CliArtifact::PresentationCard { artifact } => render_card(artifact),
54 CliArtifact::Dashboard { artifact } => {
55 if result.title().is_none() {
56 CliService::section(&artifact.title);
57 }
58 if let Some(description) = &artifact.description {
59 CliService::output(description);
60 }
61 },
62 CliArtifact::Chart { artifact } => render_chart(artifact),
63 CliArtifact::Audio { artifact } => CliService::output(&artifact.src),
64 CliArtifact::Image { artifact } => CliService::output(&artifact.src),
65 CliArtifact::Video { artifact } => CliService::output(&artifact.src),
66 CliArtifact::Message { artifact } => {
67 for line in &artifact.messages {
68 match line.level.as_str() {
69 "success" => CliService::success(&line.text),
70 "warning" => CliService::warning(&line.text),
71 "error" => CliService::error(&line.text),
72 _ => CliService::info(&line.text),
73 }
74 }
75 },
76 }
77}
78
79fn render_table(artifact: &TableArtifact) {
80 let headers: Vec<&str> = artifact
81 .columns
82 .iter()
83 .map(|c| c.label.as_deref().unwrap_or(&c.name))
84 .collect();
85
86 let rows: Vec<Vec<String>> = artifact
87 .items
88 .iter()
89 .map(|item| {
90 artifact
91 .columns
92 .iter()
93 .map(|col| item.get(&col.name).map_or_else(String::new, cell_display))
94 .collect()
95 })
96 .collect();
97
98 CliService::table(&headers, &rows);
99}
100
101fn render_list(artifact: &ListArtifact) {
102 for item in &artifact.items {
103 CliService::subsection(&item.title);
104 if !item.summary.is_empty() {
105 CliService::output(&item.summary);
106 }
107 if !item.link.is_empty() {
108 CliService::output(&item.link);
109 }
110 }
111}
112
113fn render_card(artifact: &PresentationCardArtifact) {
114 CliService::section(&artifact.title);
115 if let Some(subtitle) = &artifact.subtitle {
116 CliService::output(subtitle);
117 }
118 for section in &artifact.sections {
119 CliService::subsection(§ion.heading);
120 CliService::output(§ion.content_display());
121 }
122}
123
124fn render_chart(artifact: &ChartArtifact) {
125 let mut headers: Vec<&str> = vec!["label"];
126 for dataset in &artifact.datasets {
127 headers.push(&dataset.label);
128 }
129
130 let rows: Vec<Vec<String>> = artifact
131 .labels
132 .iter()
133 .enumerate()
134 .map(|(row, label)| {
135 let mut cells = vec![label.clone()];
136 for dataset in &artifact.datasets {
137 cells.push(
138 dataset
139 .data
140 .get(row)
141 .map_or_else(String::new, ToString::to_string),
142 );
143 }
144 cells
145 })
146 .collect();
147
148 CliService::table(&headers, &rows);
149}
150
151fn cell_display(value: &serde_json::Value) -> String {
152 match value {
153 serde_json::Value::String(s) => s.clone(),
154 serde_json::Value::Null => String::new(),
155 other => other.to_string(),
156 }
157}