1use std::borrow::Cow;
2use std::fmt::{Debug, Display, Error, Formatter};
3use std::ops::{Bound, RangeBounds};
4use std::str::FromStr;
5
6use crate::context::ScopeContext;
7use crate::date::Date;
8use crate::everything::Everything;
9#[cfg(feature = "hoi4")]
10use crate::hoi4::variables::validate_variable;
11use crate::item::Item;
12use crate::report::{ErrorKey, Severity, report};
13use crate::scopes::Scopes;
14use crate::token::Token;
15use crate::trigger::validate_target;
16#[cfg(feature = "imperator")]
17use crate::trigger::validate_target_ok_this;
18use crate::validate::validate_identifier;
19
20pub struct ValueValidator<'a> {
29 value: Cow<'a, Token>,
31 data: &'a Everything,
33 validated: bool,
36 max_severity: Severity,
40}
41
42impl Debug for ValueValidator<'_> {
43 fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
45 f.debug_struct("ValueValidator")
46 .field("value", &self.value)
47 .field("validated", &self.validated)
48 .field("max_severity", &self.max_severity)
49 .finish()
50 }
51}
52
53impl<'a> ValueValidator<'a> {
54 pub fn new(value: &'a Token, data: &'a Everything) -> Self {
56 Self { value: Cow::Borrowed(value), data, validated: false, max_severity: Severity::Fatal }
57 }
58
59 #[allow(dead_code)]
61 pub fn new_owned(value: Token, data: &'a Everything) -> Self {
62 Self { value: Cow::Owned(value), data, validated: false, max_severity: Severity::Fatal }
63 }
64
65 #[allow(dead_code)]
66 pub fn data(&self) -> &Everything {
67 self.data
68 }
69
70 pub fn set_max_severity(&mut self, max_severity: Severity) {
74 self.max_severity = max_severity;
75 }
76
77 #[allow(dead_code)]
79 fn value_validator(&self, token: Token) -> Self {
80 let mut vd = ValueValidator::new_owned(token, self.data);
81 vd.set_max_severity(self.max_severity);
82 vd
83 }
84
85 pub fn value(&self) -> &Token {
87 &self.value
88 }
89
90 pub fn accept(&mut self) {
92 self.validated = true;
93 }
94
95 #[allow(dead_code)]
96 pub fn identifier(&mut self, kind: &'static str) {
97 if self.validated {
98 return;
99 }
100 self.validated = true;
101 validate_identifier(&self.value, kind, Severity::Error.at_most(self.max_severity));
102 }
103
104 pub fn item(&mut self, itype: Item) {
107 if self.validated {
108 return;
109 }
110 self.validated = true;
111 self.data.verify_exists_max_sev(itype, &self.value, self.max_severity);
112 }
113
114 #[cfg(feature = "ck3")]
116 #[allow(dead_code)]
117 pub fn icon(&mut self, define: &str, suffix: &str) {
118 if self.validated {
119 return;
120 }
121 self.validated = true;
122 self.data.verify_icon(define, &self.value, suffix);
123 }
124
125 #[cfg(feature = "ck3")] pub fn item_used_with_suffix(&mut self, itype: Item, sfx: &str) {
129 let implied = format!("{}{sfx}", self.value);
130 self.data.mark_used(itype, &implied);
131 }
132
133 #[allow(dead_code)]
135 pub fn implied_localization_sc(&mut self, pfx: &str, sfx: &str, sc: &mut ScopeContext) {
136 let implied = format!("{pfx}{}{sfx}", self.value);
137 self.data.validate_localization_sc(&implied, sc);
138 }
139
140 #[allow(dead_code)]
144 pub fn maybe_item(&mut self, itype: Item) -> bool {
145 if self.data.item_exists(itype, self.value.as_str()) {
146 self.validated = true;
147 true
148 } else {
149 false
150 }
151 }
152
153 #[cfg(feature = "vic3")] pub fn maybe_prefix_item(&mut self, pfx: &str, itype: Item) -> bool {
158 if let Some(value) = self.value.as_str().strip_prefix(pfx) {
159 if self.data.item_exists(itype, value) {
160 self.validated = true;
161 return true;
162 }
163 }
164 false
165 }
166
167 #[cfg(feature = "ck3")] pub fn dir_file(&mut self, path: &str) {
170 if self.validated {
171 return;
172 }
173 self.validated = true;
174 let pathname = format!("{path}/{}", self.value);
175 self.data.verify_exists_implied(Item::File, &pathname, &self.value);
177 }
178
179 #[must_use]
180 #[allow(dead_code)]
181 pub fn split(&mut self, c: char) -> Vec<ValueValidator<'_>> {
182 self.validated = true;
183 self.value.split(c).into_iter().map(|value| self.value_validator(value)).collect()
184 }
185
186 #[allow(dead_code)] pub fn target(&mut self, sc: &mut ScopeContext, outscopes: Scopes) {
194 if self.validated {
195 return;
196 }
197 self.validated = true;
198 validate_target(&self.value, self.data, sc, outscopes);
200 }
201
202 #[cfg(feature = "imperator")]
205 pub fn target_ok_this(&mut self, sc: &mut ScopeContext, outscopes: Scopes) {
206 if self.validated {
207 return;
208 }
209 self.validated = true;
210 validate_target_ok_this(&self.value, self.data, sc, outscopes);
212 }
213
214 #[allow(dead_code)] pub fn item_or_target(&mut self, sc: &mut ScopeContext, itype: Item, outscopes: Scopes) {
218 if self.validated {
219 return;
220 }
221 self.validated = true;
222 if !self.data.item_exists(itype, self.value.as_str()) {
223 validate_target(&self.value, self.data, sc, outscopes);
225 }
226 }
227
228 #[allow(dead_code)]
230 pub fn bool(&mut self) {
231 if self.validated {
232 return;
233 }
234 let sev = Severity::Error.at_most(self.max_severity);
235 self.validated = true;
236 if !self.value.lowercase_is("yes") && !self.value.lowercase_is("no") {
237 report(ErrorKey::Validation, sev).msg("expected yes or no").loc(self).push();
238 }
239 }
240
241 #[allow(dead_code)]
243 pub fn maybe_bool(&mut self) {
244 if self.validated {
245 return;
246 }
247 if self.value.lowercase_is("yes") || self.value.lowercase_is("no") {
248 self.validated = true;
249 }
250 }
251
252 pub fn integer(&mut self) {
254 if self.validated {
255 return;
256 }
257 self.validated = true;
258 self.value.expect_integer();
260 }
261
262 #[allow(dead_code)]
264 pub fn maybe_integer(&mut self) {
265 if self.validated {
266 return;
267 }
268 if self.value.is_integer() {
269 self.validated = true;
270 }
271 }
272
273 #[allow(dead_code)]
275 pub fn integer_range<R: RangeBounds<i64>>(&mut self, range: R) {
276 if self.validated {
277 return;
278 }
279 let sev = Severity::Error.at_most(self.max_severity);
280 self.validated = true;
281 if let Some(i) = self.value.expect_integer() {
283 if !range.contains(&i) {
284 let low = match range.start_bound() {
285 Bound::Unbounded => None,
286 Bound::Included(&n) => Some(n),
287 Bound::Excluded(&n) => Some(n + 1),
288 };
289 let high = match range.end_bound() {
290 Bound::Unbounded => None,
291 Bound::Included(&n) => Some(n),
292 Bound::Excluded(&n) => Some(n - 1),
293 };
294 let msg;
295 if let (Some(low), Some(high)) = (low, high) {
296 msg = format!("should be between {low} and {high} (inclusive)");
297 } else if let Some(low) = low {
298 msg = format!("should be at least {low}");
299 } else if let Some(high) = high {
300 msg = format!("should be at most {high}");
301 } else {
302 unreachable!(); }
304 report(ErrorKey::Range, sev).msg(msg).loc(self).push();
305 }
306 }
307 }
308
309 #[allow(dead_code)] pub fn numeric(&mut self) {
313 if self.validated {
314 return;
315 }
316 self.validated = true;
317 self.value.expect_number();
319 }
320
321 #[cfg(feature = "vic3")]
324 pub fn numeric_range<R: RangeBounds<f64>>(&mut self, range: R) {
325 if self.validated {
326 return;
327 }
328 let sev = Severity::Error.at_most(self.max_severity);
329 self.validated = true;
330 if let Some(f) = self.value.expect_number() {
332 if !range.contains(&f) {
333 let low = match range.start_bound() {
334 Bound::Unbounded => None,
335 Bound::Included(&f) => Some(format!("{f} (inclusive)")),
336 Bound::Excluded(&f) => Some(format!("{f}")),
337 };
338 let high = match range.end_bound() {
339 Bound::Unbounded => None,
340 Bound::Included(&f) => Some(format!("{f} (inclusive)")),
341 Bound::Excluded(&f) => Some(format!("{f}")),
342 };
343 let msg;
344 if let (Some(low), Some(high)) = (low.as_ref(), high.as_ref()) {
345 msg = format!("should be between {low} and {high}");
346 } else if let Some(low) = low {
347 msg = format!("should be at least {low}");
348 } else if let Some(high) = high {
349 msg = format!("should be at most {high}");
350 } else {
351 unreachable!(); }
353 report(ErrorKey::Range, sev).msg(msg).loc(self).push();
354 }
355 }
356 }
357
358 #[allow(dead_code)] pub fn precise_numeric(&mut self) {
361 if self.validated {
362 return;
363 }
364 self.validated = true;
365 self.value.expect_precise_number();
367 }
368
369 #[allow(dead_code)] pub fn date(&mut self) {
374 if self.validated {
375 return;
376 }
377 let sev = Severity::Error.at_most(self.max_severity);
378 self.validated = true;
379 if Date::from_str(self.value.as_str()).is_err() {
380 let msg = "expected date value";
381 report(ErrorKey::Validation, sev).msg(msg).loc(self).push();
382 }
383 }
384
385 #[allow(dead_code)]
387 pub fn choice(&mut self, choices: &[&str]) {
388 if self.validated {
389 return;
390 }
391 self.validated = true;
392 let sev = Severity::Error.at_most(self.max_severity);
393 if !choices.contains(&self.value.as_str()) {
394 let msg = format!("expected one of {}", choices.join(", "));
395 report(ErrorKey::Choice, sev).msg(msg).loc(self).push();
396 }
397 }
398
399 #[cfg(feature = "hoi4")]
401 pub fn variable(&mut self, sc: &mut ScopeContext) {
402 if self.validated {
403 return;
404 }
405 self.validated = true;
406 let sev = Severity::Error.at_most(self.max_severity);
407 validate_variable(&self.value, self.data, sc, sev);
408 }
409
410 pub fn maybe_is(&mut self, s: &str) -> bool {
414 if self.value.is(s) {
415 self.validated = true;
416 true
417 } else {
418 false
419 }
420 }
421
422 pub fn warn_unvalidated(&mut self) {
424 if !self.validated {
425 let sev = Severity::Error.at_most(self.max_severity);
426 let msg = format!("unknown value `{}`", self.value);
427 report(ErrorKey::Validation, sev).msg(msg).loc(self).push();
428 }
429 }
430}
431
432impl Drop for ValueValidator<'_> {
433 fn drop(&mut self) {
434 self.warn_unvalidated();
435 }
436}
437
438impl Display for ValueValidator<'_> {
439 fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
440 Display::fmt(&self.value, f)
441 }
442}