Skip to main content

theater_cli/output/
formatters.rs

1use crate::error::CliResult;
2use crate::output::{OutputFormat, OutputManager};
3
4/// Build result formatter
5#[derive(Debug, serde::Serialize)]
6pub struct BuildResult {
7    pub success: bool,
8    pub project_dir: std::path::PathBuf,
9    pub wasm_path: Option<std::path::PathBuf>,
10    pub manifest_exists: bool,
11    pub manifest_path: Option<std::path::PathBuf>,
12    pub build_type: String,
13    pub package_name: String,
14    pub stdout: String,
15    pub stderr: String,
16}
17
18impl OutputFormat for BuildResult {
19    fn format_compact(&self, output: &OutputManager) -> CliResult<()> {
20        if self.success {
21            output.success("Build completed successfully")?;
22            if let Some(wasm_path) = &self.wasm_path {
23                println!(
24                    "  Package: {}",
25                    output.theme().accent().apply_to(wasm_path.display())
26                );
27            }
28        } else {
29            output.error("Build failed")?;
30            if !self.stderr.is_empty() {
31                println!("{}", output.theme().muted().apply_to(&self.stderr));
32            }
33        }
34        Ok(())
35    }
36
37    fn format_pretty(&self, output: &OutputManager) -> CliResult<()> {
38        if self.success {
39            println!(
40                "{} {}",
41                output.theme().success_icon(),
42                output.theme().highlight().apply_to("Build Successful")
43            );
44            println!();
45            println!(
46                "Package: {}",
47                output.theme().accent().apply_to(&self.package_name)
48            );
49            println!(
50                "Build Type: {}",
51                output.theme().muted().apply_to(&self.build_type)
52            );
53
54            if let Some(wasm_path) = &self.wasm_path {
55                println!(
56                    "WASM: {}",
57                    output.theme().accent().apply_to(wasm_path.display())
58                );
59            }
60
61            if self.manifest_exists {
62                if let Some(manifest_path) = &self.manifest_path {
63                    println!("\nTo run your actor:");
64                    println!(
65                        "  theater spawn {}",
66                        output.theme().muted().apply_to(manifest_path.display())
67                    );
68                }
69            } else {
70                println!(
71                    "\n{} No manifest.toml found.",
72                    output.theme().warning_icon()
73                );
74            }
75
76            if !self.stdout.is_empty() {
77                println!("\nBuild Output:");
78                println!("{}", output.theme().muted().apply_to(&self.stdout));
79            }
80        } else {
81            println!(
82                "{} {}",
83                output.theme().error_icon(),
84                output.theme().error().apply_to("Build Failed")
85            );
86
87            if !self.stderr.is_empty() {
88                println!("\nError Output:");
89                println!("{}", self.stderr);
90            }
91            if !self.stdout.is_empty() {
92                println!("\nBuild Output:");
93                println!("{}", self.stdout);
94            }
95        }
96        Ok(())
97    }
98
99    fn format_table(&self, output: &OutputManager) -> CliResult<()> {
100        let headers = vec!["Property", "Value"];
101        let mut rows = vec![vec![
102            "Status".to_string(),
103            if self.success {
104                "Success".to_string()
105            } else {
106                "Failed".to_string()
107            },
108        ]];
109
110        rows.push(vec!["Package".to_string(), self.package_name.clone()]);
111        rows.push(vec!["Build Type".to_string(), self.build_type.clone()]);
112        rows.push(vec![
113            "Project Dir".to_string(),
114            self.project_dir.display().to_string(),
115        ]);
116
117        if let Some(wasm_path) = &self.wasm_path {
118            rows.push(vec!["WASM".to_string(), wasm_path.display().to_string()]);
119        }
120
121        rows.push(vec![
122            "Manifest Exists".to_string(),
123            self.manifest_exists.to_string(),
124        ]);
125
126        if let Some(manifest_path) = &self.manifest_path {
127            rows.push(vec![
128                "Manifest Path".to_string(),
129                manifest_path.display().to_string(),
130            ]);
131        }
132
133        output.table(&headers, &rows)?;
134        Ok(())
135    }
136
137    fn format_detailed(&self, _output: &OutputManager) -> CliResult<()> {
138        todo!()
139    }
140}
141
142/// Project creation formatter
143#[derive(Debug, serde::Serialize)]
144pub struct ProjectCreated {
145    pub name: String,
146    pub template: String,
147    pub path: std::path::PathBuf,
148    pub build_instructions: Vec<String>,
149}
150
151impl OutputFormat for ProjectCreated {
152    fn format_compact(&self, output: &OutputManager) -> CliResult<()> {
153        println!(
154            "{} Created project: {}",
155            output.theme().success().apply_to("✓"),
156            output.theme().accent().apply_to(&self.name)
157        );
158        println!(
159            "Path: {}",
160            output.theme().muted().apply_to(self.path.display())
161        );
162        Ok(())
163    }
164
165    fn format_pretty(&self, output: &OutputManager) -> CliResult<()> {
166        println!(
167            "{} {}",
168            output.theme().success().apply_to("✓"),
169            output
170                .theme()
171                .highlight()
172                .apply_to(&format!("Created new actor project: {}", self.name))
173        );
174        println!();
175        println!(
176            "Template: {}",
177            output.theme().accent().apply_to(&self.template)
178        );
179        println!(
180            "Location: {}",
181            output.theme().muted().apply_to(self.path.display())
182        );
183        println!();
184        println!("{}", output.theme().highlight().apply_to("Next steps:"));
185        for (i, instruction) in self.build_instructions.iter().enumerate() {
186            println!(
187                "  {}. {}",
188                i + 1,
189                output.theme().muted().apply_to(instruction)
190            );
191        }
192        Ok(())
193    }
194
195    fn format_table(&self, output: &OutputManager) -> CliResult<()> {
196        let headers = vec!["Property", "Value"];
197        let rows = vec![
198            vec!["Name".to_string(), self.name.clone()],
199            vec!["Template".to_string(), self.template.clone()],
200            vec!["Path".to_string(), self.path.display().to_string()],
201        ];
202        output.table(&headers, &rows)?;
203
204        println!();
205        println!(
206            "{}",
207            output.theme().highlight().apply_to("Build Instructions:")
208        );
209        for (i, instruction) in self.build_instructions.iter().enumerate() {
210            println!("  {}. {}", i + 1, instruction);
211        }
212        Ok(())
213    }
214
215    fn format_detailed(&self, _output: &OutputManager) -> CliResult<()> {
216        todo!()
217    }
218}