mxmlextrema_as3parser/diagnostics/
diagnostics.rs1use std::any::Any;
2
3use realhydroper_path::FlexPath;
4use maplit::hashmap;
5use crate::ns::*;
6
7use lazy_regex::*;
8
9#[path = "diagnostics_english_resources.rs"]
10mod diagnostics_english_resources;
11
12#[derive(Clone)]
16pub struct Diagnostic {
17 pub(crate) location: Location,
18 pub(crate) kind: DiagnosticKind,
19 pub(crate) is_warning: bool,
20 pub(crate) is_verify_error: bool,
21 pub(crate) arguments: Vec<Rc<dyn DiagnosticArgument>>,
22 pub(crate) custom_kind: RefCell<Option<Rc<dyn Any>>>,
23}
24
25impl Eq for Diagnostic {}
26
27impl PartialEq for Diagnostic {
28 fn eq(&self, other: &Self) -> bool {
29 self.location == other.location &&
30 self.kind == other.kind
31 }
32}
33
34impl Ord for Diagnostic {
35 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
36 self.location.cmp(&other.location)
37 }
38}
39
40impl PartialOrd for Diagnostic {
41 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
42 self.location.partial_cmp(&other.location)
43 }
44}
45
46impl Diagnostic {
47 pub fn new_syntax_error(location: &Location, kind: DiagnosticKind, arguments: Vec<Rc<dyn DiagnosticArgument>>) -> Self {
48 Self {
49 location: location.clone(),
50 kind,
51 is_verify_error: false,
52 is_warning: false,
53 arguments,
54 custom_kind: RefCell::new(None),
55 }
56 }
57
58 pub fn new_verify_error(location: &Location, kind: DiagnosticKind, arguments: Vec<Rc<dyn DiagnosticArgument>>) -> Self {
59 Self {
60 location: location.clone(),
61 kind,
62 is_verify_error: true,
63 is_warning: false,
64 arguments,
65 custom_kind: RefCell::new(None),
66 }
67 }
68
69 pub fn new_warning(location: &Location, kind: DiagnosticKind, arguments: Vec<Rc<dyn DiagnosticArgument>>) -> Self {
70 Self {
71 location: location.clone(),
72 kind,
73 is_verify_error: false,
74 is_warning: true,
75 arguments,
76 custom_kind: RefCell::new(None),
77 }
78 }
79
80 pub fn location(&self) -> Location {
81 self.location.clone()
82 }
83
84 pub fn kind(&self) -> DiagnosticKind {
85 self.kind.clone()
86 }
87
88 pub fn is_warning(&self) -> bool {
89 self.is_warning
90 }
91
92 pub fn is_error(&self) -> bool {
93 !self.is_warning
94 }
95
96 pub fn is_syntax_error(&self) -> bool {
97 !self.is_verify_error && !self.is_warning
98 }
99
100 pub fn is_verify_error(&self) -> bool {
101 self.is_verify_error
102 }
103
104 pub fn arguments(&self) -> Vec<Rc<dyn DiagnosticArgument>> {
105 self.arguments.clone()
106 }
107
108 pub fn id(&self) -> i32 {
109 self.kind.id()
110 }
111
112 pub fn custom_kind(&self) -> Option<Rc<dyn Any>> {
113 self.custom_kind.borrow().clone()
114 }
115
116 pub fn set_custom_kind(&self, id: Option<Rc<dyn Any>>) {
117 self.custom_kind.replace(id);
118 }
119
120 pub fn format_with_message(&self, message: &str, id: Option<i32>) -> String {
122 self.format_with_message_and_base_path(message, id, None)
123 }
124
125 pub fn format_with_message_and_base_path(&self, message: &str, id: Option<i32>, base_path: Option<&str>) -> String {
128 let category = (if self.is_verify_error {
129 "Verify error"
130 } else if self.is_warning {
131 "Warning"
132 } else {
133 "Syntax error"
134 }).to_owned();
135
136 let mut file_path = self.location.compilation_unit.file_path.clone().map_or("".to_owned(), |s| format!("{s}:"));
137 if let Some(base_path) = base_path {
138 file_path = FlexPath::new_native(base_path).relative(&file_path).to_owned();
139 }
140 if regex_is_match!(r"^\\\\\?\\[uU][nN][cC][\\/]", &file_path) {
141 file_path = r"\\".to_owned() + &file_path[8..].to_owned();
142 } else if file_path.starts_with(r"\\?\") {
143 file_path = file_path[4..].to_owned();
144 }
145 let line = self.location.first_line_number();
146 let column = self.location.first_column() + 1;
147 if let Some(id) = id {
148 format!("{file_path}{line}:{column}: {category} #{}: {message}", id.to_string())
149 } else {
150 format!("{file_path}{line}:{column}: {category}: {message}")
151 }
152 }
153
154 pub fn format_english(&self) -> String {
156 self.format_with_message(&self.format_message_english(), Some(self.id()))
157 }
158
159 pub fn format_english_with_base_path(&self, base_path: &str) -> String {
161 self.format_with_message_and_base_path(&self.format_message_english(), Some(self.id()), Some(base_path))
162 }
163
164 pub fn format_message_english(&self) -> String {
165 self.format_message(&diagnostics_english_resources::DATA)
166 }
167
168 pub fn format_message(&self, messages: &HashMap<i32, String>) -> String {
169 let mut string_arguments: HashMap<String, String> = hashmap!{};
170 let mut i = 1;
171 for argument in &self.arguments {
172 string_arguments.insert(i.to_string(), argument.to_string());
173 i += 1;
174 }
175 use realhydroper_lateformat::LateFormat;
176 let Some(msg) = messages.get(&self.id()) else {
177 let id = self.id();
178 panic!("Message resource is missing for ID {id}");
179 };
180 msg.realhydroper_lateformat(string_arguments)
181 }
182}
183
184pub macro diagarg {
189 ($($value:expr),*) => { vec![ $(Rc::new($value)),* ] },
190}
191
192pub trait DiagnosticArgument: Any + ToString + 'static {
193}
194
195impl DiagnosticArgument for String {}
196
197impl DiagnosticArgument for Token {}