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("internal error, please report to KittyCAD team: {details:?}")]
134 Internal { details: KclErrorDetails },
135}
136
137impl From<KclErrorWithOutputs> for KclError {
138 fn from(error: KclErrorWithOutputs) -> Self {
139 error.error
140 }
141}
142
143#[derive(Error, Debug, Serialize, ts_rs::TS, Clone, PartialEq)]
144#[error("{error}")]
145#[ts(export)]
146#[serde(rename_all = "camelCase")]
147pub struct KclErrorWithOutputs {
148 pub error: KclError,
149 pub non_fatal: Vec<CompilationError>,
150 pub variables: IndexMap<String, KclValue>,
153 #[cfg(feature = "artifact-graph")]
154 pub operations: Vec<Operation>,
155 #[cfg(feature = "artifact-graph")]
158 pub _artifact_commands: Vec<ArtifactCommand>,
159 #[cfg(feature = "artifact-graph")]
160 pub artifact_graph: ArtifactGraph,
161 #[cfg(feature = "artifact-graph")]
162 #[serde(skip)]
163 pub scene_objects: Vec<Object>,
164 #[cfg(feature = "artifact-graph")]
165 #[serde(skip)]
166 pub source_range_to_object: BTreeMap<SourceRange, ObjectId>,
167 #[cfg(feature = "artifact-graph")]
168 #[serde(skip)]
169 pub var_solutions: Vec<(SourceRange, Number)>,
170 pub scene_graph: Option<crate::front::SceneGraph>,
171 pub filenames: IndexMap<ModuleId, ModulePath>,
172 pub source_files: IndexMap<ModuleId, ModuleSource>,
173 pub default_planes: Option<DefaultPlanes>,
174}
175
176impl KclErrorWithOutputs {
177 #[allow(clippy::too_many_arguments)]
178 pub fn new(
179 error: KclError,
180 non_fatal: Vec<CompilationError>,
181 variables: IndexMap<String, KclValue>,
182 #[cfg(feature = "artifact-graph")] operations: Vec<Operation>,
183 #[cfg(feature = "artifact-graph")] artifact_commands: Vec<ArtifactCommand>,
184 #[cfg(feature = "artifact-graph")] artifact_graph: ArtifactGraph,
185 #[cfg(feature = "artifact-graph")] scene_objects: Vec<Object>,
186 #[cfg(feature = "artifact-graph")] source_range_to_object: BTreeMap<SourceRange, ObjectId>,
187 #[cfg(feature = "artifact-graph")] var_solutions: Vec<(SourceRange, Number)>,
188 filenames: IndexMap<ModuleId, ModulePath>,
189 source_files: IndexMap<ModuleId, ModuleSource>,
190 default_planes: Option<DefaultPlanes>,
191 ) -> Self {
192 Self {
193 error,
194 non_fatal,
195 variables,
196 #[cfg(feature = "artifact-graph")]
197 operations,
198 #[cfg(feature = "artifact-graph")]
199 _artifact_commands: artifact_commands,
200 #[cfg(feature = "artifact-graph")]
201 artifact_graph,
202 #[cfg(feature = "artifact-graph")]
203 scene_objects,
204 #[cfg(feature = "artifact-graph")]
205 source_range_to_object,
206 #[cfg(feature = "artifact-graph")]
207 var_solutions,
208 scene_graph: Default::default(),
209 filenames,
210 source_files,
211 default_planes,
212 }
213 }
214 pub fn no_outputs(error: KclError) -> Self {
215 Self {
216 error,
217 non_fatal: Default::default(),
218 variables: Default::default(),
219 #[cfg(feature = "artifact-graph")]
220 operations: Default::default(),
221 #[cfg(feature = "artifact-graph")]
222 _artifact_commands: Default::default(),
223 #[cfg(feature = "artifact-graph")]
224 artifact_graph: Default::default(),
225 #[cfg(feature = "artifact-graph")]
226 scene_objects: Default::default(),
227 #[cfg(feature = "artifact-graph")]
228 source_range_to_object: Default::default(),
229 #[cfg(feature = "artifact-graph")]
230 var_solutions: Default::default(),
231 scene_graph: Default::default(),
232 filenames: Default::default(),
233 source_files: Default::default(),
234 default_planes: Default::default(),
235 }
236 }
237 pub fn into_miette_report_with_outputs(self, code: &str) -> anyhow::Result<ReportWithOutputs> {
238 let mut source_ranges = self.error.source_ranges();
239
240 let first_source_range = source_ranges
242 .pop()
243 .ok_or_else(|| anyhow::anyhow!("No source ranges found"))?;
244
245 let source = self
246 .source_files
247 .get(&first_source_range.module_id())
248 .cloned()
249 .unwrap_or(ModuleSource {
250 source: code.to_string(),
251 path: self
252 .filenames
253 .get(&first_source_range.module_id())
254 .cloned()
255 .unwrap_or(ModulePath::Main),
256 });
257 let filename = source.path.to_string();
258 let kcl_source = source.source;
259
260 let mut related = Vec::new();
261 for source_range in source_ranges {
262 let module_id = source_range.module_id();
263 let source = self.source_files.get(&module_id).cloned().unwrap_or(ModuleSource {
264 source: code.to_string(),
265 path: self.filenames.get(&module_id).cloned().unwrap_or(ModulePath::Main),
266 });
267 let error = self.error.override_source_ranges(vec![source_range]);
268 let report = Report {
269 error,
270 kcl_source: source.source.to_string(),
271 filename: source.path.to_string(),
272 };
273 related.push(report);
274 }
275
276 Ok(ReportWithOutputs {
277 error: self,
278 kcl_source,
279 filename,
280 related,
281 })
282 }
283}
284
285impl IntoDiagnostic for KclErrorWithOutputs {
286 fn to_lsp_diagnostics(&self, code: &str) -> Vec<Diagnostic> {
287 let message = self.error.get_message();
288 let source_ranges = self.error.source_ranges();
289
290 source_ranges
291 .into_iter()
292 .map(|source_range| {
293 let source = self
294 .source_files
295 .get(&source_range.module_id())
296 .cloned()
297 .unwrap_or(ModuleSource {
298 source: code.to_string(),
299 path: self.filenames.get(&source_range.module_id()).unwrap().clone(),
300 });
301 let mut filename = source.path.to_string();
302 if !filename.starts_with("file://") {
303 filename = format!("file:///{}", filename.trim_start_matches("/"));
304 }
305
306 let related_information = if let Ok(uri) = url::Url::parse(&filename) {
307 Some(vec![tower_lsp::lsp_types::DiagnosticRelatedInformation {
308 location: tower_lsp::lsp_types::Location {
309 uri,
310 range: source_range.to_lsp_range(&source.source),
311 },
312 message: message.to_string(),
313 }])
314 } else {
315 None
316 };
317
318 Diagnostic {
319 range: source_range.to_lsp_range(code),
320 severity: Some(self.severity()),
321 code: None,
322 code_description: None,
324 source: Some("kcl".to_string()),
325 related_information,
326 message: message.clone(),
327 tags: None,
328 data: None,
329 }
330 })
331 .collect()
332 }
333
334 fn severity(&self) -> DiagnosticSeverity {
335 DiagnosticSeverity::ERROR
336 }
337}
338
339#[derive(thiserror::Error, Debug)]
340#[error("{}", self.error.error.get_message())]
341pub struct ReportWithOutputs {
342 pub error: KclErrorWithOutputs,
343 pub kcl_source: String,
344 pub filename: String,
345 pub related: Vec<Report>,
346}
347
348impl miette::Diagnostic for ReportWithOutputs {
349 fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
350 let family = match self.error.error {
351 KclError::Lexical { .. } => "Lexical",
352 KclError::Syntax { .. } => "Syntax",
353 KclError::Semantic { .. } => "Semantic",
354 KclError::ImportCycle { .. } => "ImportCycle",
355 KclError::Argument { .. } => "Argument",
356 KclError::Type { .. } => "Type",
357 KclError::Io { .. } => "I/O",
358 KclError::Unexpected { .. } => "Unexpected",
359 KclError::ValueAlreadyDefined { .. } => "ValueAlreadyDefined",
360 KclError::UndefinedValue { .. } => "UndefinedValue",
361 KclError::InvalidExpression { .. } => "InvalidExpression",
362 KclError::MaxCallStack { .. } => "MaxCallStack",
363 KclError::Engine { .. } => "Engine",
364 KclError::Internal { .. } => "Internal",
365 };
366 let error_string = format!("KCL {family} error");
367 Some(Box::new(error_string))
368 }
369
370 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
371 Some(&self.kcl_source)
372 }
373
374 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
375 let iter = self
376 .error
377 .error
378 .source_ranges()
379 .into_iter()
380 .map(miette::SourceSpan::from)
381 .map(|span| miette::LabeledSpan::new_with_span(Some(self.filename.to_string()), span));
382 Some(Box::new(iter))
383 }
384
385 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> {
386 let iter = self.related.iter().map(|r| r as &dyn miette::Diagnostic);
387 Some(Box::new(iter))
388 }
389}
390
391#[derive(thiserror::Error, Debug)]
392#[error("{}", self.error.get_message())]
393pub struct Report {
394 pub error: KclError,
395 pub kcl_source: String,
396 pub filename: String,
397}
398
399impl miette::Diagnostic for Report {
400 fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
401 let family = match self.error {
402 KclError::Lexical { .. } => "Lexical",
403 KclError::Syntax { .. } => "Syntax",
404 KclError::Semantic { .. } => "Semantic",
405 KclError::ImportCycle { .. } => "ImportCycle",
406 KclError::Argument { .. } => "Argument",
407 KclError::Type { .. } => "Type",
408 KclError::Io { .. } => "I/O",
409 KclError::Unexpected { .. } => "Unexpected",
410 KclError::ValueAlreadyDefined { .. } => "ValueAlreadyDefined",
411 KclError::UndefinedValue { .. } => "UndefinedValue",
412 KclError::InvalidExpression { .. } => "InvalidExpression",
413 KclError::MaxCallStack { .. } => "MaxCallStack",
414 KclError::Engine { .. } => "Engine",
415 KclError::Internal { .. } => "Internal",
416 };
417 let error_string = format!("KCL {family} error");
418 Some(Box::new(error_string))
419 }
420
421 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
422 Some(&self.kcl_source)
423 }
424
425 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
426 let iter = self
427 .error
428 .source_ranges()
429 .into_iter()
430 .map(miette::SourceSpan::from)
431 .map(|span| miette::LabeledSpan::new_with_span(Some(self.filename.to_string()), span));
432 Some(Box::new(iter))
433 }
434}
435
436impl KclErrorDetails {
437 pub fn new(message: String, source_ranges: Vec<SourceRange>) -> KclErrorDetails {
438 let backtrace = source_ranges
439 .iter()
440 .map(|s| BacktraceItem {
441 source_range: *s,
442 fn_name: None,
443 })
444 .collect();
445 KclErrorDetails {
446 source_ranges,
447 backtrace,
448 message,
449 }
450 }
451}
452
453impl KclError {
454 pub fn internal(message: String) -> KclError {
455 KclError::Internal {
456 details: KclErrorDetails {
457 source_ranges: Default::default(),
458 backtrace: Default::default(),
459 message,
460 },
461 }
462 }
463
464 pub fn new_internal(details: KclErrorDetails) -> KclError {
465 KclError::Internal { details }
466 }
467
468 pub fn new_import_cycle(details: KclErrorDetails) -> KclError {
469 KclError::ImportCycle { details }
470 }
471
472 pub fn new_argument(details: KclErrorDetails) -> KclError {
473 KclError::Argument { details }
474 }
475
476 pub fn new_semantic(details: KclErrorDetails) -> KclError {
477 KclError::Semantic { details }
478 }
479
480 pub fn new_value_already_defined(details: KclErrorDetails) -> KclError {
481 KclError::ValueAlreadyDefined { details }
482 }
483
484 pub fn new_syntax(details: KclErrorDetails) -> KclError {
485 KclError::Syntax { details }
486 }
487
488 pub fn new_io(details: KclErrorDetails) -> KclError {
489 KclError::Io { details }
490 }
491
492 pub fn new_invalid_expression(details: KclErrorDetails) -> KclError {
493 KclError::InvalidExpression { details }
494 }
495
496 pub fn new_engine(details: KclErrorDetails) -> KclError {
497 KclError::Engine { details }
498 }
499
500 pub fn new_lexical(details: KclErrorDetails) -> KclError {
501 KclError::Lexical { details }
502 }
503
504 pub fn new_undefined_value(details: KclErrorDetails, name: Option<String>) -> KclError {
505 KclError::UndefinedValue { details, name }
506 }
507
508 pub fn new_type(details: KclErrorDetails) -> KclError {
509 KclError::Type { details }
510 }
511
512 pub fn get_message(&self) -> String {
514 format!("{}: {}", self.error_type(), self.message())
515 }
516
517 pub fn error_type(&self) -> &'static str {
518 match self {
519 KclError::Lexical { .. } => "lexical",
520 KclError::Syntax { .. } => "syntax",
521 KclError::Semantic { .. } => "semantic",
522 KclError::ImportCycle { .. } => "import cycle",
523 KclError::Argument { .. } => "argument",
524 KclError::Type { .. } => "type",
525 KclError::Io { .. } => "i/o",
526 KclError::Unexpected { .. } => "unexpected",
527 KclError::ValueAlreadyDefined { .. } => "value already defined",
528 KclError::UndefinedValue { .. } => "undefined value",
529 KclError::InvalidExpression { .. } => "invalid expression",
530 KclError::MaxCallStack { .. } => "max call stack",
531 KclError::Engine { .. } => "engine",
532 KclError::Internal { .. } => "internal",
533 }
534 }
535
536 pub fn source_ranges(&self) -> Vec<SourceRange> {
537 match &self {
538 KclError::Lexical { details: e } => e.source_ranges.clone(),
539 KclError::Syntax { details: e } => e.source_ranges.clone(),
540 KclError::Semantic { details: e } => e.source_ranges.clone(),
541 KclError::ImportCycle { details: e } => e.source_ranges.clone(),
542 KclError::Argument { details: e } => e.source_ranges.clone(),
543 KclError::Type { details: e } => e.source_ranges.clone(),
544 KclError::Io { details: e } => e.source_ranges.clone(),
545 KclError::Unexpected { details: e } => e.source_ranges.clone(),
546 KclError::ValueAlreadyDefined { details: e } => e.source_ranges.clone(),
547 KclError::UndefinedValue { details: e, .. } => e.source_ranges.clone(),
548 KclError::InvalidExpression { details: e } => e.source_ranges.clone(),
549 KclError::MaxCallStack { details: e } => e.source_ranges.clone(),
550 KclError::Engine { details: e } => e.source_ranges.clone(),
551 KclError::Internal { details: e } => e.source_ranges.clone(),
552 }
553 }
554
555 pub fn message(&self) -> &str {
557 match &self {
558 KclError::Lexical { details: e } => &e.message,
559 KclError::Syntax { details: e } => &e.message,
560 KclError::Semantic { details: e } => &e.message,
561 KclError::ImportCycle { details: e } => &e.message,
562 KclError::Argument { details: e } => &e.message,
563 KclError::Type { details: e } => &e.message,
564 KclError::Io { details: e } => &e.message,
565 KclError::Unexpected { details: e } => &e.message,
566 KclError::ValueAlreadyDefined { details: e } => &e.message,
567 KclError::UndefinedValue { details: e, .. } => &e.message,
568 KclError::InvalidExpression { details: e } => &e.message,
569 KclError::MaxCallStack { details: e } => &e.message,
570 KclError::Engine { details: e } => &e.message,
571 KclError::Internal { details: e } => &e.message,
572 }
573 }
574
575 pub fn backtrace(&self) -> Vec<BacktraceItem> {
576 match self {
577 KclError::Lexical { details: e }
578 | KclError::Syntax { details: e }
579 | KclError::Semantic { details: e }
580 | KclError::ImportCycle { details: e }
581 | KclError::Argument { details: e }
582 | KclError::Type { details: e }
583 | KclError::Io { details: e }
584 | KclError::Unexpected { details: e }
585 | KclError::ValueAlreadyDefined { details: e }
586 | KclError::UndefinedValue { details: e, .. }
587 | KclError::InvalidExpression { details: e }
588 | KclError::MaxCallStack { details: e }
589 | KclError::Engine { details: e }
590 | KclError::Internal { details: e } => e.backtrace.clone(),
591 }
592 }
593
594 pub(crate) fn override_source_ranges(&self, source_ranges: Vec<SourceRange>) -> Self {
595 let mut new = self.clone();
596 match &mut new {
597 KclError::Lexical { details: e }
598 | KclError::Syntax { details: e }
599 | KclError::Semantic { details: e }
600 | KclError::ImportCycle { details: e }
601 | KclError::Argument { details: e }
602 | KclError::Type { details: e }
603 | KclError::Io { details: e }
604 | KclError::Unexpected { details: e }
605 | KclError::ValueAlreadyDefined { details: e }
606 | KclError::UndefinedValue { details: e, .. }
607 | KclError::InvalidExpression { details: e }
608 | KclError::MaxCallStack { details: e }
609 | KclError::Engine { details: e }
610 | KclError::Internal { details: e } => {
611 e.backtrace = source_ranges
612 .iter()
613 .map(|s| BacktraceItem {
614 source_range: *s,
615 fn_name: None,
616 })
617 .collect();
618 e.source_ranges = source_ranges;
619 }
620 }
621
622 new
623 }
624
625 pub(crate) fn add_unwind_location(&self, last_fn_name: Option<String>, source_range: SourceRange) -> Self {
626 let mut new = self.clone();
627 match &mut new {
628 KclError::Lexical { details: e }
629 | KclError::Syntax { details: e }
630 | KclError::Semantic { details: e }
631 | KclError::ImportCycle { details: e }
632 | KclError::Argument { details: e }
633 | KclError::Type { details: e }
634 | KclError::Io { details: e }
635 | KclError::Unexpected { details: e }
636 | KclError::ValueAlreadyDefined { details: e }
637 | KclError::UndefinedValue { details: e, .. }
638 | KclError::InvalidExpression { details: e }
639 | KclError::MaxCallStack { details: e }
640 | KclError::Engine { details: e }
641 | KclError::Internal { details: e } => {
642 if let Some(item) = e.backtrace.last_mut() {
643 item.fn_name = last_fn_name;
644 }
645 e.backtrace.push(BacktraceItem {
646 source_range,
647 fn_name: None,
648 });
649 e.source_ranges.push(source_range);
650 }
651 }
652
653 new
654 }
655}
656
657#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ts_rs::TS, thiserror::Error, miette::Diagnostic)]
658#[serde(rename_all = "camelCase")]
659#[ts(export)]
660pub struct BacktraceItem {
661 pub source_range: SourceRange,
662 pub fn_name: Option<String>,
663}
664
665impl std::fmt::Display for BacktraceItem {
666 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
667 if let Some(fn_name) = &self.fn_name {
668 write!(f, "{fn_name}: {:?}", self.source_range)
669 } else {
670 write!(f, "(fn): {:?}", self.source_range)
671 }
672 }
673}
674
675impl IntoDiagnostic for KclError {
676 fn to_lsp_diagnostics(&self, code: &str) -> Vec<Diagnostic> {
677 let message = self.get_message();
678 let source_ranges = self.source_ranges();
679
680 let module_id = ModuleId::default();
682 let source_ranges = source_ranges
683 .iter()
684 .filter(|r| r.module_id() == module_id)
685 .collect::<Vec<_>>();
686
687 let mut diagnostics = Vec::new();
688 for source_range in &source_ranges {
689 diagnostics.push(Diagnostic {
690 range: source_range.to_lsp_range(code),
691 severity: Some(self.severity()),
692 code: None,
693 code_description: None,
695 source: Some("kcl".to_string()),
696 related_information: None,
697 message: message.clone(),
698 tags: None,
699 data: None,
700 });
701 }
702
703 diagnostics
704 }
705
706 fn severity(&self) -> DiagnosticSeverity {
707 DiagnosticSeverity::ERROR
708 }
709}
710
711impl From<KclError> for String {
714 fn from(error: KclError) -> Self {
715 serde_json::to_string(&error).unwrap()
716 }
717}
718
719impl From<String> for KclError {
720 fn from(error: String) -> Self {
721 serde_json::from_str(&error).unwrap()
722 }
723}
724
725#[cfg(feature = "pyo3")]
726impl From<pyo3::PyErr> for KclError {
727 fn from(error: pyo3::PyErr) -> Self {
728 KclError::new_internal(KclErrorDetails {
729 source_ranges: vec![],
730 backtrace: Default::default(),
731 message: error.to_string(),
732 })
733 }
734}
735
736#[cfg(feature = "pyo3")]
737impl From<KclError> for pyo3::PyErr {
738 fn from(error: KclError) -> Self {
739 pyo3::exceptions::PyException::new_err(error.to_string())
740 }
741}
742
743impl From<CompilationError> for KclErrorDetails {
744 fn from(err: CompilationError) -> Self {
745 let backtrace = vec![BacktraceItem {
746 source_range: err.source_range,
747 fn_name: None,
748 }];
749 KclErrorDetails {
750 source_ranges: vec![err.source_range],
751 backtrace,
752 message: err.message,
753 }
754 }
755}