1#[cfg(feature = "artifact-graph")]
2use std::collections::BTreeMap;
3
4use indexmap::IndexMap;
5pub use kcl_error::{CompilationError, Severity, Suggestion, Tag};
6use serde::{Deserialize, Serialize};
7use thiserror::Error;
8use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity};
9
10use crate::{
11 ModuleId, SourceRange,
12 exec::KclValue,
13 execution::DefaultPlanes,
14 lsp::{IntoDiagnostic, ToLspRange},
15 modules::{ModulePath, ModuleSource},
16};
17#[cfg(feature = "artifact-graph")]
18use crate::{
19 execution::{ArtifactCommand, ArtifactGraph, Operation},
20 front::{Number, Object, ObjectId},
21};
22
23mod details;
24
25pub use details::KclErrorDetails;
26
27#[derive(thiserror::Error, Debug)]
29pub enum ExecError {
30 #[error("{0}")]
31 Kcl(#[from] Box<crate::KclErrorWithOutputs>),
32 #[error("Could not connect to engine: {0}")]
33 Connection(#[from] ConnectionError),
34 #[error("PNG snapshot could not be decoded: {0}")]
35 BadPng(String),
36 #[error("Bad export: {0}")]
37 BadExport(String),
38}
39
40impl From<KclErrorWithOutputs> for ExecError {
41 fn from(error: KclErrorWithOutputs) -> Self {
42 ExecError::Kcl(Box::new(error))
43 }
44}
45
46#[cfg_attr(target_arch = "wasm32", expect(dead_code))]
48#[derive(Debug)]
49pub struct ExecErrorWithState {
50 pub error: ExecError,
51 pub exec_state: Option<crate::execution::ExecState>,
52}
53
54impl ExecErrorWithState {
55 #[cfg_attr(target_arch = "wasm32", expect(dead_code))]
56 pub fn new(error: ExecError, exec_state: crate::execution::ExecState) -> Self {
57 Self {
58 error,
59 exec_state: Some(exec_state),
60 }
61 }
62}
63
64impl ExecError {
65 pub fn as_kcl_error(&self) -> Option<&crate::KclError> {
66 let ExecError::Kcl(k) = &self else {
67 return None;
68 };
69 Some(&k.error)
70 }
71}
72
73impl From<ExecError> for ExecErrorWithState {
74 fn from(error: ExecError) -> Self {
75 Self {
76 error,
77 exec_state: None,
78 }
79 }
80}
81
82impl From<ConnectionError> for ExecErrorWithState {
83 fn from(error: ConnectionError) -> Self {
84 Self {
85 error: error.into(),
86 exec_state: None,
87 }
88 }
89}
90
91#[derive(thiserror::Error, Debug)]
93pub enum ConnectionError {
94 #[error("Could not create a Zoo client: {0}")]
95 CouldNotMakeClient(anyhow::Error),
96 #[error("Could not establish connection to engine: {0}")]
97 Establishing(anyhow::Error),
98}
99
100#[derive(Error, Debug, Serialize, Deserialize, ts_rs::TS, Clone, PartialEq, Eq)]
101#[ts(export)]
102#[serde(tag = "kind", rename_all = "snake_case")]
103pub enum KclError {
104 #[error("lexical: {details:?}")]
105 Lexical { details: KclErrorDetails },
106 #[error("syntax: {details:?}")]
107 Syntax { details: KclErrorDetails },
108 #[error("semantic: {details:?}")]
109 Semantic { details: KclErrorDetails },
110 #[error("import cycle: {details:?}")]
111 ImportCycle { details: KclErrorDetails },
112 #[error("argument: {details:?}")]
113 Argument { details: KclErrorDetails },
114 #[error("type: {details:?}")]
115 Type { details: KclErrorDetails },
116 #[error("i/o: {details:?}")]
117 Io { details: KclErrorDetails },
118 #[error("unexpected: {details:?}")]
119 Unexpected { details: KclErrorDetails },
120 #[error("value already defined: {details:?}")]
121 ValueAlreadyDefined { details: KclErrorDetails },
122 #[error("undefined value: {details:?}")]
123 UndefinedValue {
124 details: KclErrorDetails,
125 name: Option<String>,
126 },
127 #[error("invalid expression: {details:?}")]
128 InvalidExpression { details: KclErrorDetails },
129 #[error("max call stack size exceeded: {details:?}")]
130 MaxCallStack { details: KclErrorDetails },
131 #[error("engine: {details:?}")]
132 Engine { details: KclErrorDetails },
133 #[error("engine hangup: {details:?}")]
134 EngineHangup { details: KclErrorDetails },
135 #[error("internal error, please report to KittyCAD team: {details:?}")]
136 Internal { details: KclErrorDetails },
137}
138
139impl From<KclErrorWithOutputs> for KclError {
140 fn from(error: KclErrorWithOutputs) -> Self {
141 error.error
142 }
143}
144
145#[derive(Error, Debug, Serialize, ts_rs::TS, Clone, PartialEq)]
146#[error("{error}")]
147#[ts(export)]
148#[serde(rename_all = "camelCase")]
149pub struct KclErrorWithOutputs {
150 pub error: KclError,
151 pub non_fatal: Vec<CompilationError>,
152 pub variables: IndexMap<String, KclValue>,
155 #[cfg(feature = "artifact-graph")]
156 pub operations: Vec<Operation>,
157 #[cfg(feature = "artifact-graph")]
160 pub _artifact_commands: Vec<ArtifactCommand>,
161 #[cfg(feature = "artifact-graph")]
162 pub artifact_graph: ArtifactGraph,
163 #[cfg(feature = "artifact-graph")]
164 #[serde(skip)]
165 pub scene_objects: Vec<Object>,
166 #[cfg(feature = "artifact-graph")]
167 #[serde(skip)]
168 pub source_range_to_object: BTreeMap<SourceRange, ObjectId>,
169 #[cfg(feature = "artifact-graph")]
170 #[serde(skip)]
171 pub var_solutions: Vec<(SourceRange, Number)>,
172 pub scene_graph: Option<crate::front::SceneGraph>,
173 pub filenames: IndexMap<ModuleId, ModulePath>,
174 pub source_files: IndexMap<ModuleId, ModuleSource>,
175 pub default_planes: Option<DefaultPlanes>,
176}
177
178impl KclErrorWithOutputs {
179 #[allow(clippy::too_many_arguments)]
180 pub fn new(
181 error: KclError,
182 non_fatal: Vec<CompilationError>,
183 variables: IndexMap<String, KclValue>,
184 #[cfg(feature = "artifact-graph")] operations: Vec<Operation>,
185 #[cfg(feature = "artifact-graph")] artifact_commands: Vec<ArtifactCommand>,
186 #[cfg(feature = "artifact-graph")] artifact_graph: ArtifactGraph,
187 #[cfg(feature = "artifact-graph")] scene_objects: Vec<Object>,
188 #[cfg(feature = "artifact-graph")] source_range_to_object: BTreeMap<SourceRange, ObjectId>,
189 #[cfg(feature = "artifact-graph")] var_solutions: Vec<(SourceRange, Number)>,
190 filenames: IndexMap<ModuleId, ModulePath>,
191 source_files: IndexMap<ModuleId, ModuleSource>,
192 default_planes: Option<DefaultPlanes>,
193 ) -> Self {
194 Self {
195 error,
196 non_fatal,
197 variables,
198 #[cfg(feature = "artifact-graph")]
199 operations,
200 #[cfg(feature = "artifact-graph")]
201 _artifact_commands: artifact_commands,
202 #[cfg(feature = "artifact-graph")]
203 artifact_graph,
204 #[cfg(feature = "artifact-graph")]
205 scene_objects,
206 #[cfg(feature = "artifact-graph")]
207 source_range_to_object,
208 #[cfg(feature = "artifact-graph")]
209 var_solutions,
210 scene_graph: Default::default(),
211 filenames,
212 source_files,
213 default_planes,
214 }
215 }
216 pub fn no_outputs(error: KclError) -> Self {
217 Self {
218 error,
219 non_fatal: Default::default(),
220 variables: Default::default(),
221 #[cfg(feature = "artifact-graph")]
222 operations: Default::default(),
223 #[cfg(feature = "artifact-graph")]
224 _artifact_commands: Default::default(),
225 #[cfg(feature = "artifact-graph")]
226 artifact_graph: Default::default(),
227 #[cfg(feature = "artifact-graph")]
228 scene_objects: Default::default(),
229 #[cfg(feature = "artifact-graph")]
230 source_range_to_object: Default::default(),
231 #[cfg(feature = "artifact-graph")]
232 var_solutions: Default::default(),
233 scene_graph: Default::default(),
234 filenames: Default::default(),
235 source_files: Default::default(),
236 default_planes: Default::default(),
237 }
238 }
239 pub fn into_miette_report_with_outputs(self, code: &str) -> anyhow::Result<ReportWithOutputs> {
240 let mut source_ranges = self.error.source_ranges();
241
242 let first_source_range = source_ranges
244 .pop()
245 .ok_or_else(|| anyhow::anyhow!("No source ranges found"))?;
246
247 let source = self
248 .source_files
249 .get(&first_source_range.module_id())
250 .cloned()
251 .unwrap_or(ModuleSource {
252 source: code.to_string(),
253 path: self
254 .filenames
255 .get(&first_source_range.module_id())
256 .cloned()
257 .unwrap_or(ModulePath::Main),
258 });
259 let filename = source.path.to_string();
260 let kcl_source = source.source;
261
262 let mut related = Vec::new();
263 for source_range in source_ranges {
264 let module_id = source_range.module_id();
265 let source = self.source_files.get(&module_id).cloned().unwrap_or(ModuleSource {
266 source: code.to_string(),
267 path: self.filenames.get(&module_id).cloned().unwrap_or(ModulePath::Main),
268 });
269 let error = self.error.override_source_ranges(vec![source_range]);
270 let report = Report {
271 error,
272 kcl_source: source.source.to_string(),
273 filename: source.path.to_string(),
274 };
275 related.push(report);
276 }
277
278 Ok(ReportWithOutputs {
279 error: self,
280 kcl_source,
281 filename,
282 related,
283 })
284 }
285}
286
287impl IntoDiagnostic for KclErrorWithOutputs {
288 fn to_lsp_diagnostics(&self, code: &str) -> Vec<Diagnostic> {
289 let message = self.error.get_message();
290 let source_ranges = self.error.source_ranges();
291
292 source_ranges
293 .into_iter()
294 .map(|source_range| {
295 let source = self
296 .source_files
297 .get(&source_range.module_id())
298 .cloned()
299 .unwrap_or(ModuleSource {
300 source: code.to_string(),
301 path: self.filenames.get(&source_range.module_id()).unwrap().clone(),
302 });
303 let mut filename = source.path.to_string();
304 if !filename.starts_with("file://") {
305 filename = format!("file:///{}", filename.trim_start_matches("/"));
306 }
307
308 let related_information = if let Ok(uri) = url::Url::parse(&filename) {
309 Some(vec![tower_lsp::lsp_types::DiagnosticRelatedInformation {
310 location: tower_lsp::lsp_types::Location {
311 uri,
312 range: source_range.to_lsp_range(&source.source),
313 },
314 message: message.to_string(),
315 }])
316 } else {
317 None
318 };
319
320 Diagnostic {
321 range: source_range.to_lsp_range(code),
322 severity: Some(self.severity()),
323 code: None,
324 code_description: None,
326 source: Some("kcl".to_string()),
327 related_information,
328 message: message.clone(),
329 tags: None,
330 data: None,
331 }
332 })
333 .collect()
334 }
335
336 fn severity(&self) -> DiagnosticSeverity {
337 DiagnosticSeverity::ERROR
338 }
339}
340
341#[derive(thiserror::Error, Debug)]
342#[error("{}", self.error.error.get_message())]
343pub struct ReportWithOutputs {
344 pub error: KclErrorWithOutputs,
345 pub kcl_source: String,
346 pub filename: String,
347 pub related: Vec<Report>,
348}
349
350impl miette::Diagnostic for ReportWithOutputs {
351 fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
352 let family = match self.error.error {
353 KclError::Lexical { .. } => "Lexical",
354 KclError::Syntax { .. } => "Syntax",
355 KclError::Semantic { .. } => "Semantic",
356 KclError::ImportCycle { .. } => "ImportCycle",
357 KclError::Argument { .. } => "Argument",
358 KclError::Type { .. } => "Type",
359 KclError::Io { .. } => "I/O",
360 KclError::Unexpected { .. } => "Unexpected",
361 KclError::ValueAlreadyDefined { .. } => "ValueAlreadyDefined",
362 KclError::UndefinedValue { .. } => "UndefinedValue",
363 KclError::InvalidExpression { .. } => "InvalidExpression",
364 KclError::MaxCallStack { .. } => "MaxCallStack",
365 KclError::Engine { .. } => "Engine",
366 KclError::EngineHangup { .. } => "EngineHangup",
367 KclError::Internal { .. } => "Internal",
368 };
369 let error_string = format!("KCL {family} error");
370 Some(Box::new(error_string))
371 }
372
373 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
374 Some(&self.kcl_source)
375 }
376
377 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
378 let iter = self
379 .error
380 .error
381 .source_ranges()
382 .into_iter()
383 .map(miette::SourceSpan::from)
384 .map(|span| miette::LabeledSpan::new_with_span(Some(self.filename.to_string()), span));
385 Some(Box::new(iter))
386 }
387
388 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> {
389 let iter = self.related.iter().map(|r| r as &dyn miette::Diagnostic);
390 Some(Box::new(iter))
391 }
392}
393
394#[derive(thiserror::Error, Debug)]
395#[error("{}", self.error.get_message())]
396pub struct Report {
397 pub error: KclError,
398 pub kcl_source: String,
399 pub filename: String,
400}
401
402impl miette::Diagnostic for Report {
403 fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
404 let family = match self.error {
405 KclError::Lexical { .. } => "Lexical",
406 KclError::Syntax { .. } => "Syntax",
407 KclError::Semantic { .. } => "Semantic",
408 KclError::ImportCycle { .. } => "ImportCycle",
409 KclError::Argument { .. } => "Argument",
410 KclError::Type { .. } => "Type",
411 KclError::Io { .. } => "I/O",
412 KclError::Unexpected { .. } => "Unexpected",
413 KclError::ValueAlreadyDefined { .. } => "ValueAlreadyDefined",
414 KclError::UndefinedValue { .. } => "UndefinedValue",
415 KclError::InvalidExpression { .. } => "InvalidExpression",
416 KclError::MaxCallStack { .. } => "MaxCallStack",
417 KclError::Engine { .. } => "Engine",
418 KclError::EngineHangup { .. } => "EngineHangup",
419 KclError::Internal { .. } => "Internal",
420 };
421 let error_string = format!("KCL {family} error");
422 Some(Box::new(error_string))
423 }
424
425 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
426 Some(&self.kcl_source)
427 }
428
429 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
430 let iter = self
431 .error
432 .source_ranges()
433 .into_iter()
434 .map(miette::SourceSpan::from)
435 .map(|span| miette::LabeledSpan::new_with_span(Some(self.filename.to_string()), span));
436 Some(Box::new(iter))
437 }
438}
439
440impl KclErrorDetails {
441 pub fn new(message: String, source_ranges: Vec<SourceRange>) -> KclErrorDetails {
442 let backtrace = source_ranges
443 .iter()
444 .map(|s| BacktraceItem {
445 source_range: *s,
446 fn_name: None,
447 })
448 .collect();
449 KclErrorDetails {
450 source_ranges,
451 backtrace,
452 message,
453 }
454 }
455}
456
457impl KclError {
458 pub fn internal(message: String) -> KclError {
459 KclError::Internal {
460 details: KclErrorDetails {
461 source_ranges: Default::default(),
462 backtrace: Default::default(),
463 message,
464 },
465 }
466 }
467
468 pub fn new_internal(details: KclErrorDetails) -> KclError {
469 KclError::Internal { details }
470 }
471
472 pub fn new_import_cycle(details: KclErrorDetails) -> KclError {
473 KclError::ImportCycle { details }
474 }
475
476 pub fn new_argument(details: KclErrorDetails) -> KclError {
477 KclError::Argument { details }
478 }
479
480 pub fn new_semantic(details: KclErrorDetails) -> KclError {
481 KclError::Semantic { details }
482 }
483
484 pub fn new_value_already_defined(details: KclErrorDetails) -> KclError {
485 KclError::ValueAlreadyDefined { details }
486 }
487
488 pub fn new_syntax(details: KclErrorDetails) -> KclError {
489 KclError::Syntax { details }
490 }
491
492 pub fn new_io(details: KclErrorDetails) -> KclError {
493 KclError::Io { details }
494 }
495
496 pub fn new_invalid_expression(details: KclErrorDetails) -> KclError {
497 KclError::InvalidExpression { details }
498 }
499
500 pub fn new_engine(details: KclErrorDetails) -> KclError {
501 KclError::Engine { details }
502 }
503
504 pub fn new_engine_hangup(details: KclErrorDetails) -> KclError {
505 KclError::EngineHangup { details }
506 }
507
508 pub fn new_lexical(details: KclErrorDetails) -> KclError {
509 KclError::Lexical { details }
510 }
511
512 pub fn new_undefined_value(details: KclErrorDetails, name: Option<String>) -> KclError {
513 KclError::UndefinedValue { details, name }
514 }
515
516 pub fn new_type(details: KclErrorDetails) -> KclError {
517 KclError::Type { details }
518 }
519
520 pub fn get_message(&self) -> String {
522 format!("{}: {}", self.error_type(), self.message())
523 }
524
525 pub fn error_type(&self) -> &'static str {
526 match self {
527 KclError::Lexical { .. } => "lexical",
528 KclError::Syntax { .. } => "syntax",
529 KclError::Semantic { .. } => "semantic",
530 KclError::ImportCycle { .. } => "import cycle",
531 KclError::Argument { .. } => "argument",
532 KclError::Type { .. } => "type",
533 KclError::Io { .. } => "i/o",
534 KclError::Unexpected { .. } => "unexpected",
535 KclError::ValueAlreadyDefined { .. } => "value already defined",
536 KclError::UndefinedValue { .. } => "undefined value",
537 KclError::InvalidExpression { .. } => "invalid expression",
538 KclError::MaxCallStack { .. } => "max call stack",
539 KclError::Engine { .. } => "engine",
540 KclError::EngineHangup { .. } => "engine hangup",
541 KclError::Internal { .. } => "internal",
542 }
543 }
544
545 pub fn source_ranges(&self) -> Vec<SourceRange> {
546 match &self {
547 KclError::Lexical { details: e } => e.source_ranges.clone(),
548 KclError::Syntax { details: e } => e.source_ranges.clone(),
549 KclError::Semantic { details: e } => e.source_ranges.clone(),
550 KclError::ImportCycle { details: e } => e.source_ranges.clone(),
551 KclError::Argument { details: e } => e.source_ranges.clone(),
552 KclError::Type { details: e } => e.source_ranges.clone(),
553 KclError::Io { details: e } => e.source_ranges.clone(),
554 KclError::Unexpected { details: e } => e.source_ranges.clone(),
555 KclError::ValueAlreadyDefined { details: e } => e.source_ranges.clone(),
556 KclError::UndefinedValue { details: e, .. } => e.source_ranges.clone(),
557 KclError::InvalidExpression { details: e } => e.source_ranges.clone(),
558 KclError::MaxCallStack { details: e } => e.source_ranges.clone(),
559 KclError::Engine { details: e } => e.source_ranges.clone(),
560 KclError::EngineHangup { details: e } => e.source_ranges.clone(),
561 KclError::Internal { details: e } => e.source_ranges.clone(),
562 }
563 }
564
565 pub fn message(&self) -> &str {
567 match &self {
568 KclError::Lexical { details: e } => &e.message,
569 KclError::Syntax { details: e } => &e.message,
570 KclError::Semantic { details: e } => &e.message,
571 KclError::ImportCycle { details: e } => &e.message,
572 KclError::Argument { details: e } => &e.message,
573 KclError::Type { details: e } => &e.message,
574 KclError::Io { details: e } => &e.message,
575 KclError::Unexpected { details: e } => &e.message,
576 KclError::ValueAlreadyDefined { details: e } => &e.message,
577 KclError::UndefinedValue { details: e, .. } => &e.message,
578 KclError::InvalidExpression { details: e } => &e.message,
579 KclError::MaxCallStack { details: e } => &e.message,
580 KclError::Engine { details: e } => &e.message,
581 KclError::EngineHangup { details: e } => &e.message,
582 KclError::Internal { details: e } => &e.message,
583 }
584 }
585
586 pub fn backtrace(&self) -> Vec<BacktraceItem> {
587 match self {
588 KclError::Lexical { details: e }
589 | KclError::Syntax { details: e }
590 | KclError::Semantic { details: e }
591 | KclError::ImportCycle { details: e }
592 | KclError::Argument { details: e }
593 | KclError::Type { details: e }
594 | KclError::Io { details: e }
595 | KclError::Unexpected { details: e }
596 | KclError::ValueAlreadyDefined { details: e }
597 | KclError::UndefinedValue { details: e, .. }
598 | KclError::InvalidExpression { details: e }
599 | KclError::MaxCallStack { details: e }
600 | KclError::Engine { details: e }
601 | KclError::EngineHangup { details: e }
602 | KclError::Internal { details: e } => e.backtrace.clone(),
603 }
604 }
605
606 pub(crate) fn override_source_ranges(&self, source_ranges: Vec<SourceRange>) -> Self {
607 let mut new = self.clone();
608 match &mut new {
609 KclError::Lexical { details: e }
610 | KclError::Syntax { details: e }
611 | KclError::Semantic { details: e }
612 | KclError::ImportCycle { details: e }
613 | KclError::Argument { details: e }
614 | KclError::Type { details: e }
615 | KclError::Io { details: e }
616 | KclError::Unexpected { details: e }
617 | KclError::ValueAlreadyDefined { details: e }
618 | KclError::UndefinedValue { details: e, .. }
619 | KclError::InvalidExpression { details: e }
620 | KclError::MaxCallStack { details: e }
621 | KclError::Engine { details: e }
622 | KclError::EngineHangup { details: e }
623 | KclError::Internal { details: e } => {
624 e.backtrace = source_ranges
625 .iter()
626 .map(|s| BacktraceItem {
627 source_range: *s,
628 fn_name: None,
629 })
630 .collect();
631 e.source_ranges = source_ranges;
632 }
633 }
634
635 new
636 }
637
638 pub(crate) fn add_unwind_location(&self, last_fn_name: Option<String>, source_range: SourceRange) -> Self {
639 let mut new = self.clone();
640 match &mut new {
641 KclError::Lexical { details: e }
642 | KclError::Syntax { details: e }
643 | KclError::Semantic { details: e }
644 | KclError::ImportCycle { details: e }
645 | KclError::Argument { details: e }
646 | KclError::Type { details: e }
647 | KclError::Io { details: e }
648 | KclError::Unexpected { details: e }
649 | KclError::ValueAlreadyDefined { details: e }
650 | KclError::UndefinedValue { details: e, .. }
651 | KclError::InvalidExpression { details: e }
652 | KclError::MaxCallStack { details: e }
653 | KclError::Engine { details: e }
654 | KclError::EngineHangup { details: e }
655 | KclError::Internal { details: e } => {
656 if let Some(item) = e.backtrace.last_mut() {
657 item.fn_name = last_fn_name;
658 }
659 e.backtrace.push(BacktraceItem {
660 source_range,
661 fn_name: None,
662 });
663 e.source_ranges.push(source_range);
664 }
665 }
666
667 new
668 }
669}
670
671#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ts_rs::TS, thiserror::Error, miette::Diagnostic)]
672#[serde(rename_all = "camelCase")]
673#[ts(export)]
674pub struct BacktraceItem {
675 pub source_range: SourceRange,
676 pub fn_name: Option<String>,
677}
678
679impl std::fmt::Display for BacktraceItem {
680 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
681 if let Some(fn_name) = &self.fn_name {
682 write!(f, "{fn_name}: {:?}", self.source_range)
683 } else {
684 write!(f, "(fn): {:?}", self.source_range)
685 }
686 }
687}
688
689impl IntoDiagnostic for KclError {
690 fn to_lsp_diagnostics(&self, code: &str) -> Vec<Diagnostic> {
691 let message = self.get_message();
692 let source_ranges = self.source_ranges();
693
694 let module_id = ModuleId::default();
696 let source_ranges = source_ranges
697 .iter()
698 .filter(|r| r.module_id() == module_id)
699 .collect::<Vec<_>>();
700
701 let mut diagnostics = Vec::new();
702 for source_range in &source_ranges {
703 diagnostics.push(Diagnostic {
704 range: source_range.to_lsp_range(code),
705 severity: Some(self.severity()),
706 code: None,
707 code_description: None,
709 source: Some("kcl".to_string()),
710 related_information: None,
711 message: message.clone(),
712 tags: None,
713 data: None,
714 });
715 }
716
717 diagnostics
718 }
719
720 fn severity(&self) -> DiagnosticSeverity {
721 DiagnosticSeverity::ERROR
722 }
723}
724
725impl From<KclError> for String {
728 fn from(error: KclError) -> Self {
729 serde_json::to_string(&error).unwrap()
730 }
731}
732
733impl From<String> for KclError {
734 fn from(error: String) -> Self {
735 serde_json::from_str(&error).unwrap()
736 }
737}
738
739#[cfg(feature = "pyo3")]
740impl From<pyo3::PyErr> for KclError {
741 fn from(error: pyo3::PyErr) -> Self {
742 KclError::new_internal(KclErrorDetails {
743 source_ranges: vec![],
744 backtrace: Default::default(),
745 message: error.to_string(),
746 })
747 }
748}
749
750#[cfg(feature = "pyo3")]
751impl From<KclError> for pyo3::PyErr {
752 fn from(error: KclError) -> Self {
753 pyo3::exceptions::PyException::new_err(error.to_string())
754 }
755}
756
757impl From<CompilationError> for KclErrorDetails {
758 fn from(err: CompilationError) -> Self {
759 let backtrace = vec![BacktraceItem {
760 source_range: err.source_range,
761 fn_name: None,
762 }];
763 KclErrorDetails {
764 source_ranges: vec![err.source_range],
765 backtrace,
766 message: err.message,
767 }
768 }
769}