Skip to main content

morphir_core/migration/
diagnostic.rs

1use serde::{Deserialize, Serialize};
2
3use crate::traversal::IrCursor;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6#[serde(rename_all = "lowercase")]
7pub enum Severity {
8    Warning,
9    Error,
10}
11
12#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
13pub enum V4Encoding {
14    #[default]
15    Compact,
16    Expanded,
17}
18
19#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
20pub struct MigrationOptions {
21    pub allow_partial: bool,
22    pub encoding: V4Encoding,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
26#[serde(rename_all = "camelCase")]
27pub struct MigrationDiagnostic {
28    pub severity: Severity,
29    pub code: &'static str,
30    pub path: String,
31    pub message: String,
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub help: Option<String>,
34    #[serde(skip)]
35    pub recoverable: bool,
36    #[serde(skip)]
37    cursor: IrCursor,
38}
39
40impl MigrationDiagnostic {
41    pub fn error(code: &'static str, cursor: IrCursor, message: impl Into<String>) -> Self {
42        let path = cursor.to_string();
43        Self {
44            severity: Severity::Error,
45            code,
46            path,
47            message: message.into(),
48            help: None,
49            recoverable: false,
50            cursor,
51        }
52    }
53
54    pub fn recoverable(code: &'static str, cursor: IrCursor, message: impl Into<String>) -> Self {
55        let path = cursor.to_string();
56        Self {
57            severity: Severity::Error,
58            code,
59            path,
60            message: message.into(),
61            help: None,
62            recoverable: true,
63            cursor,
64        }
65    }
66
67    pub fn warning(code: &'static str, cursor: IrCursor, message: impl Into<String>) -> Self {
68        let path = cursor.to_string();
69        Self {
70            severity: Severity::Warning,
71            code,
72            path,
73            message: message.into(),
74            help: None,
75            recoverable: true,
76            cursor,
77        }
78    }
79
80    pub fn with_help(mut self, help: impl Into<String>) -> Self {
81        self.help = Some(help.into());
82        self
83    }
84
85    /// Return the typed semantic cursor retained by this diagnostic.
86    pub fn cursor(&self) -> &IrCursor {
87        &self.cursor
88    }
89}
90
91#[derive(Debug, Clone)]
92pub struct MigrationReport {
93    options: MigrationOptions,
94    diagnostics: Vec<MigrationDiagnostic>,
95}
96
97impl MigrationReport {
98    pub fn new(options: MigrationOptions) -> Self {
99        Self {
100            options,
101            diagnostics: Vec::new(),
102        }
103    }
104
105    pub fn push(&mut self, diagnostic: MigrationDiagnostic) {
106        self.diagnostics.push(diagnostic);
107    }
108
109    pub fn diagnostics(&self) -> &[MigrationDiagnostic] {
110        &self.diagnostics
111    }
112
113    pub fn can_publish(&self) -> bool {
114        self.diagnostics.iter().all(|diagnostic| {
115            diagnostic.severity != Severity::Error
116                || (self.options.allow_partial && diagnostic.recoverable)
117        })
118    }
119}