1pub mod diagnostic;
7
8use crate::output::OutputContext;
9
10#[derive(Debug)]
26pub enum CliError {
27 Config {
29 detail: String,
31 file: Option<String>,
33 suggestion: String,
35 },
36 Contract {
38 detail: String,
40 diagnostics: Vec<String>,
42 },
43 Link {
45 detail: String,
47 diagnostics: Vec<String>,
49 },
50 Runtime {
52 detail: String,
54 context: Option<String>,
56 },
57 Packaging {
59 detail: String,
61 suggestion: String,
63 },
64 Security {
66 detail: String,
68 suggestion: String,
70 },
71 #[allow(dead_code)]
73 Environment {
74 detail: String,
76 fix: String,
78 },
79 Io {
81 detail: String,
83 path: Option<String>,
85 },
86 #[allow(dead_code)]
88 Internal {
89 detail: String,
91 },
92 #[allow(dead_code)]
94 NotImplemented {
95 command: String,
97 },
98}
99
100impl CliError {
101 pub fn exit_code(&self) -> i32 {
107 match self {
108 Self::Config { .. } => 2,
109 Self::Contract { .. } => 1,
110 Self::Link { .. } => 1,
111 Self::Runtime { .. } => 1,
112 Self::Packaging { .. } => 1,
113 Self::Security { .. } => 1,
114 Self::Environment { .. } => 3,
115 Self::Io { .. } => 2,
116 Self::Internal { .. } => 1,
117 Self::NotImplemented { .. } => 1,
118 }
119 }
120
121 pub fn render(&self, ctx: &OutputContext) {
125 use crate::cli::OutputFormat;
126 match ctx.format {
127 OutputFormat::Json => {
128 let err_obj = self.to_json_value();
129 crate::output::json::print_json(&err_obj);
130 }
131 OutputFormat::Human => {
132 diagnostic::render_cli_error(ctx, self);
133 }
134 }
135 }
136
137 fn to_json_value(&self) -> serde_json::Value {
139 match self {
140 Self::Config {
141 detail,
142 file,
143 suggestion,
144 } => serde_json::json!({
145 "error": true,
146 "category": "config",
147 "detail": detail,
148 "file": file,
149 "suggestion": suggestion,
150 }),
151 Self::Contract {
152 detail,
153 diagnostics,
154 } => serde_json::json!({
155 "error": true,
156 "category": "contract",
157 "detail": detail,
158 "diagnostics": diagnostics,
159 }),
160 Self::Link {
161 detail,
162 diagnostics,
163 } => serde_json::json!({
164 "error": true,
165 "category": "link",
166 "detail": detail,
167 "diagnostics": diagnostics,
168 }),
169 Self::Runtime { detail, context } => serde_json::json!({
170 "error": true,
171 "category": "runtime",
172 "detail": detail,
173 "context": context,
174 }),
175 Self::Packaging { detail, suggestion } => serde_json::json!({
176 "error": true,
177 "category": "packaging",
178 "detail": detail,
179 "suggestion": suggestion,
180 }),
181 Self::Security { detail, suggestion } => serde_json::json!({
182 "error": true,
183 "category": "security",
184 "detail": detail,
185 "suggestion": suggestion,
186 }),
187 Self::Environment { detail, fix } => serde_json::json!({
188 "error": true,
189 "category": "environment",
190 "detail": detail,
191 "fix": fix,
192 }),
193 Self::Io { detail, path } => serde_json::json!({
194 "error": true,
195 "category": "io",
196 "detail": detail,
197 "path": path,
198 }),
199 Self::Internal { detail } => serde_json::json!({
200 "error": true,
201 "category": "internal",
202 "detail": detail,
203 }),
204 Self::NotImplemented { command } => serde_json::json!({
205 "error": true,
206 "category": "not_implemented",
207 "detail": format!("Command '{command}' is not yet implemented (Part B)"),
208 }),
209 }
210 }
211}
212
213impl From<torvyn_types::TorvynError> for CliError {
214 fn from(err: torvyn_types::TorvynError) -> Self {
215 match err {
216 torvyn_types::TorvynError::Config(e) => CliError::Config {
217 detail: e.to_string(),
218 file: None,
219 suggestion: "Check your Torvyn.toml for errors.".into(),
220 },
221 torvyn_types::TorvynError::Contract(e) => CliError::Contract {
222 detail: e.to_string(),
223 diagnostics: vec![],
224 },
225 torvyn_types::TorvynError::Link(e) => CliError::Link {
226 detail: e.to_string(),
227 diagnostics: vec![],
228 },
229 torvyn_types::TorvynError::Resource(e) => CliError::Runtime {
230 detail: e.to_string(),
231 context: Some("resource error".into()),
232 },
233 torvyn_types::TorvynError::Reactor(e) => CliError::Runtime {
234 detail: e.to_string(),
235 context: Some("reactor error".into()),
236 },
237 torvyn_types::TorvynError::Security(e) => CliError::Security {
238 detail: e.to_string(),
239 suggestion: "Check capability grants in your security configuration.".into(),
240 },
241 torvyn_types::TorvynError::Packaging(e) => CliError::Packaging {
242 detail: e.to_string(),
243 suggestion: "Check your packaging configuration.".into(),
244 },
245 torvyn_types::TorvynError::Process(e) => CliError::Runtime {
246 detail: e.to_string(),
247 context: Some("component process error".into()),
248 },
249 torvyn_types::TorvynError::Engine(e) => CliError::Runtime {
250 detail: e.to_string(),
251 context: Some("engine error".into()),
252 },
253 torvyn_types::TorvynError::Io(e) => CliError::Io {
254 detail: e.to_string(),
255 path: None,
256 },
257 }
258 }
259}
260
261impl From<std::io::Error> for CliError {
262 fn from(err: std::io::Error) -> Self {
263 CliError::Io {
264 detail: err.to_string(),
265 path: None,
266 }
267 }
268}