1use anyhow::{anyhow, Result};
2use regex_syntax::escape;
3
4use super::schema::NumberSchema;
5
6#[cfg_attr(test, derive(PartialEq))]
8#[derive(Debug, Clone)]
9pub struct Decimal {
10 pub coef: u32,
11 pub exp: u32,
12}
13
14impl Decimal {
15 fn new(coef: u32, exp: u32) -> Self {
16 if coef == 0 {
17 return Decimal { coef: 0, exp: 0 };
18 }
19 let mut coef = coef;
21 let mut exp = exp;
22 while exp > 0 && coef % 10 == 0 {
23 coef /= 10;
24 exp -= 1;
25 }
26 Decimal { coef, exp }
27 }
28
29 pub fn lcm(&self, other: &Decimal) -> Decimal {
30 if self.coef == 0 || other.coef == 0 {
31 return Decimal::new(0, 0);
32 }
33 let a = self.coef * 10u32.pow(other.exp.saturating_sub(self.exp));
34 let b = other.coef * 10u32.pow(self.exp.saturating_sub(other.exp));
35 let coef = (a * b) / gcd(a, b);
36 Decimal::new(coef, self.exp.max(other.exp))
37 }
38
39 pub fn to_f64(&self) -> f64 {
40 self.coef as f64 / 10.0f64.powi(self.exp as i32)
41 }
42}
43
44impl TryFrom<f64> for Decimal {
45 type Error = anyhow::Error;
46
47 fn try_from(value: f64) -> Result<Self, Self::Error> {
48 if value < 0.0 {
49 return Err(anyhow!("Value for 'multipleOf' must be non-negative"));
50 }
51 let mut value = value;
52 let mut exp = 0;
53 while value.fract() != 0.0 {
54 value *= 10.0;
55 exp += 1;
56 }
57 if value > u32::MAX as f64 {
58 return Err(anyhow!(
59 "Value for 'multipleOf' has too many digits: {}",
60 value
61 ));
62 }
63 Ok(Decimal::new(value as u32, exp))
64 }
65}
66
67fn gcd(a: u32, b: u32) -> u32 {
68 if b == 0 {
69 a
70 } else {
71 gcd(b, a % b)
72 }
73}
74
75fn mk_or(parts: Vec<String>) -> String {
76 if parts.len() == 1 {
77 parts[0].clone()
78 } else {
79 format!("({})", parts.join("|"))
80 }
81}
82
83fn num_digits(n: i64) -> usize {
84 n.abs().to_string().len()
85}
86
87pub fn rx_int_range(left: Option<i64>, right: Option<i64>) -> Result<String> {
88 match (left, right) {
89 (None, None) => Ok("-?(0|[1-9][0-9]*)".to_string()),
90 (Some(left), None) => {
91 if left < 0 {
92 Ok(mk_or(vec![
93 rx_int_range(Some(left), Some(-1))?,
94 rx_int_range(Some(0), None)?,
95 ]))
96 } else {
97 let max_value = "9"
98 .repeat(num_digits(left))
99 .parse::<i64>()
100 .map_err(|e| anyhow!("Failed to parse max value for left {}: {}", left, e))?;
101 Ok(mk_or(vec![
102 rx_int_range(Some(left), Some(max_value))?,
103 format!("[1-9][0-9]{{{},}}", num_digits(left)),
104 ]))
105 }
106 }
107 (None, Some(right)) => {
108 if right >= 0 {
109 Ok(mk_or(vec![
110 rx_int_range(Some(0), Some(right))?,
111 rx_int_range(None, Some(-1))?,
112 ]))
113 } else {
114 Ok(format!("-{}", rx_int_range(Some(-right), None)?))
115 }
116 }
117 (Some(left), Some(right)) => {
118 if left > right {
119 return Err(anyhow!(
120 "Invalid range: left ({}) cannot be greater than right ({})",
121 left,
122 right
123 ));
124 }
125 if left < 0 {
126 if right < 0 {
127 Ok(format!("(-{})", rx_int_range(Some(-right), Some(-left))?))
128 } else {
129 Ok(format!(
130 "(-{}|{})",
131 rx_int_range(Some(0), Some(-left))?,
132 rx_int_range(Some(0), Some(right))?
133 ))
134 }
135 } else if num_digits(left) == num_digits(right) {
136 let l = left.to_string();
137 let r = right.to_string();
138 if left == right {
139 return Ok(format!("({})", l));
140 }
141
142 let lpref = &l[..l.len() - 1];
143 let lx = &l[l.len() - 1..];
144 let rpref = &r[..r.len() - 1];
145 let rx = &r[r.len() - 1..];
146
147 if lpref == rpref {
148 return Ok(format!("({}[{}-{}])", lpref, lx, rx));
149 }
150
151 let mut left_rec = lpref.parse::<i64>().unwrap_or(0);
152 let mut right_rec = rpref.parse::<i64>().unwrap_or(0);
153 if left_rec >= right_rec {
154 return Err(anyhow!(
155 "Invalid recursive range: left_rec ({}) must be less than right_rec ({})",
156 left_rec,
157 right_rec
158 ));
159 }
160
161 let mut parts = Vec::new();
162
163 if lx != "0" {
164 left_rec += 1;
165 parts.push(format!("{}[{}-9]", lpref, lx));
166 }
167
168 if rx != "9" {
169 right_rec -= 1;
170 parts.push(format!("{}[0-{}]", rpref, rx));
171 }
172
173 if left_rec <= right_rec {
174 let inner = rx_int_range(Some(left_rec), Some(right_rec))?;
175 parts.push(format!("{}[0-9]", inner));
176 }
177
178 Ok(mk_or(parts))
179 } else {
180 let break_point = 10_i64
181 .checked_pow(num_digits(left) as u32)
182 .ok_or_else(|| anyhow!("Overflow when calculating break point"))?
183 - 1;
184 Ok(mk_or(vec![
185 rx_int_range(Some(left), Some(break_point))?,
186 rx_int_range(Some(break_point + 1), Some(right))?,
187 ]))
188 }
189 }
190 }
191}
192
193fn lexi_x_to_9(x: &str, incl: bool) -> Result<String> {
194 if incl {
195 if x.is_empty() {
196 Ok("[0-9]*".to_string())
197 } else if x.len() == 1 {
198 Ok(format!("[{}-9][0-9]*", x))
199 } else {
200 let x0 = x
201 .chars()
202 .next()
203 .ok_or_else(|| anyhow!("String x is unexpectedly empty"))?
204 .to_digit(10)
205 .ok_or_else(|| anyhow!("Failed to parse character as digit"))?;
206 let x_rest = &x[1..];
207 let mut parts = vec![format!(
208 "{}{}",
209 x.chars()
210 .next()
211 .ok_or_else(|| anyhow!("String x is unexpectedly empty"))?,
212 lexi_x_to_9(x_rest, incl)?
213 )];
214 if x0 < 9 {
215 parts.push(format!("[{}-9][0-9]*", x0 + 1));
216 }
217 Ok(mk_or(parts))
218 }
219 } else if x.is_empty() {
220 Ok("[0-9]*[1-9]".to_string())
221 } else {
222 let x0 = x
223 .chars()
224 .next()
225 .ok_or_else(|| anyhow!("String x is unexpectedly empty"))?
226 .to_digit(10)
227 .ok_or_else(|| anyhow!("Failed to parse character as digit"))?;
228 let x_rest = &x[1..];
229 let mut parts = vec![format!(
230 "{}{}",
231 x.chars()
232 .next()
233 .ok_or_else(|| anyhow!("String x is unexpectedly empty"))?,
234 lexi_x_to_9(x_rest, incl)?
235 )];
236 if x0 < 9 {
237 parts.push(format!("[{}-9][0-9]*", x0 + 1));
238 }
239 Ok(mk_or(parts))
240 }
241}
242
243fn lexi_0_to_x(x: &str, incl: bool) -> Result<String> {
244 if x.is_empty() {
245 if incl {
246 Ok("".to_string())
247 } else {
248 Err(anyhow!("Inclusive flag must be true for an empty string"))
249 }
250 } else {
251 let x0 = x
252 .chars()
253 .next()
254 .ok_or_else(|| anyhow!("String x is unexpectedly empty"))?
255 .to_digit(10)
256 .ok_or_else(|| anyhow!("Failed to parse character as digit"))?;
257 let x_rest = &x[1..];
258
259 if !incl && x.len() == 1 {
260 if x0 == 0 {
261 return Err(anyhow!(
262 "x0 must be greater than 0 for non-inclusive single character"
263 ));
264 }
265 return Ok(format!("[0-{}][0-9]*", x0 - 1));
266 }
267
268 let mut parts = vec![format!(
269 "{}{}",
270 x.chars()
271 .next()
272 .ok_or_else(|| anyhow!("String x is unexpectedly empty"))?,
273 lexi_0_to_x(x_rest, incl)?
274 )];
275 if x0 > 0 {
276 parts.push(format!("[0-{}][0-9]*", x0 - 1));
277 }
278 Ok(mk_or(parts))
279 }
280}
281
282fn lexi_range(ld: &str, rd: &str, ld_incl: bool, rd_incl: bool) -> Result<String> {
283 if ld.len() != rd.len() {
284 return Err(anyhow!("ld and rd must have the same length"));
285 }
286 if ld == rd {
287 if ld_incl && rd_incl {
288 Ok(ld.to_string())
289 } else {
290 Err(anyhow!(
291 "Empty range when ld equals rd and not both inclusive"
292 ))
293 }
294 } else {
295 let l0 = ld
296 .chars()
297 .next()
298 .ok_or_else(|| anyhow!("ld is unexpectedly empty"))?
299 .to_digit(10)
300 .ok_or_else(|| anyhow!("Failed to parse character as digit"))?;
301 let r0 = rd
302 .chars()
303 .next()
304 .ok_or_else(|| anyhow!("rd is unexpectedly empty"))?
305 .to_digit(10)
306 .ok_or_else(|| anyhow!("Failed to parse character as digit"))?;
307 if l0 == r0 {
308 let ld_rest = &ld[1..];
309 let rd_rest = &rd[1..];
310 Ok(format!(
311 "{}{}",
312 ld.chars()
313 .next()
314 .ok_or_else(|| anyhow!("ld is unexpectedly empty"))?,
315 lexi_range(ld_rest, rd_rest, ld_incl, rd_incl)?
316 ))
317 } else {
318 if l0 >= r0 {
319 return Err(anyhow!("l0 must be less than r0"));
320 }
321 let ld_rest = ld[1..].trim_end_matches('0');
322 let mut parts = vec![format!(
323 "{}{}",
324 ld.chars()
325 .next()
326 .ok_or_else(|| anyhow!("ld is unexpectedly empty"))?,
327 lexi_x_to_9(ld_rest, ld_incl)?
328 )];
329 if l0 + 1 < r0 {
330 parts.push(format!("[{}-{}][0-9]*", l0 + 1, r0 - 1));
331 }
332 let rd_rest = rd[1..].trim_end_matches('0');
333 if !rd_rest.is_empty() || rd_incl {
334 parts.push(format!(
335 "{}{}",
336 rd.chars()
337 .next()
338 .ok_or_else(|| anyhow!("rd is unexpectedly empty"))?,
339 lexi_0_to_x(rd_rest, rd_incl)?
340 ));
341 }
342 Ok(mk_or(parts))
343 }
344 }
345}
346
347fn float_to_str(f: f64) -> String {
348 format!("{}", f)
349}
350
351pub fn rx_float_range(
352 left: Option<f64>,
353 right: Option<f64>,
354 left_inclusive: bool,
355 right_inclusive: bool,
356) -> Result<String> {
357 match (left, right) {
358 (None, None) => Ok("-?(0|[1-9][0-9]*)(\\.[0-9]+)?([eE][+-]?[0-9]+)?".to_string()),
359 (Some(left), None) => {
360 if left < 0.0 {
361 Ok(mk_or(vec![
362 rx_float_range(Some(left), Some(0.0), left_inclusive, false)?,
363 rx_float_range(Some(0.0), None, true, false)?,
364 ]))
365 } else {
366 let left_int_part = left as i64;
367 Ok(mk_or(vec![
368 rx_float_range(
369 Some(left),
370 Some(10f64.powi(num_digits(left_int_part) as i32)),
371 left_inclusive,
372 false,
373 )?,
374 format!("[1-9][0-9]{{{},}}(\\.[0-9]+)?", num_digits(left_int_part)),
375 ]))
376 }
377 }
378 (None, Some(right)) => {
379 if right == 0.0 {
380 let r = format!("-{}", rx_float_range(Some(0.0), None, false, false)?);
381 if right_inclusive {
382 Ok(mk_or(vec![r, "0".to_string()]))
383 } else {
384 Ok(r)
385 }
386 } else if right > 0.0 {
387 Ok(mk_or(vec![
388 format!("-{}", rx_float_range(Some(0.0), None, false, false)?),
389 rx_float_range(Some(0.0), Some(right), true, right_inclusive)?,
390 ]))
391 } else {
392 Ok(format!(
393 "-{}",
394 rx_float_range(Some(-right), None, right_inclusive, false)?
395 ))
396 }
397 }
398 (Some(left), Some(right)) => {
399 if left > right {
400 return Err(anyhow!(
401 "Invalid range: left ({}) cannot be greater than right ({})",
402 left,
403 right
404 ));
405 }
406 if left == right {
407 if left_inclusive && right_inclusive {
408 Ok(format!("({})", escape(&float_to_str(left))))
409 } else {
410 Err(anyhow!(
411 "Empty range when left equals right and not both inclusive"
412 ))
413 }
414 } else if left < 0.0 {
415 if right < 0.0 {
416 Ok(format!(
417 "(-{})",
418 rx_float_range(Some(-right), Some(-left), right_inclusive, left_inclusive)?
419 ))
420 } else {
421 let mut parts = vec![];
422 let neg_part = rx_float_range(Some(0.0), Some(-left), false, left_inclusive)?;
423 parts.push(format!("(-{})", neg_part));
424
425 if right > 0.0 || right_inclusive {
426 let pos_part =
427 rx_float_range(Some(0.0), Some(right), true, right_inclusive)?;
428 parts.push(pos_part);
429 }
430 Ok(mk_or(parts))
431 }
432 } else {
433 let l = float_to_str(left);
434 let r = float_to_str(right);
435 if l == r {
436 return Err(anyhow!(
437 "Unexpected equality of left and right string representations"
438 ));
439 }
440 if !left.is_finite() || !right.is_finite() {
441 return Err(anyhow!("Infinite numbers not supported"));
442 }
443
444 let mut left_rec: i64 = l
445 .split('.')
446 .next()
447 .ok_or_else(|| anyhow!("Failed to split left integer part"))?
448 .parse()
449 .map_err(|e| anyhow!("Failed to parse left integer part: {}", e))?;
450 let right_rec: i64 = r
451 .split('.')
452 .next()
453 .ok_or_else(|| anyhow!("Failed to split right integer part"))?
454 .parse()
455 .map_err(|e| anyhow!("Failed to parse right integer part: {}", e))?;
456
457 let mut ld = l.split('.').nth(1).unwrap_or("").to_string();
458 let mut rd = r.split('.').nth(1).unwrap_or("").to_string();
459
460 if left_rec == right_rec {
461 while ld.len() < rd.len() {
462 ld.push('0');
463 }
464 while rd.len() < ld.len() {
465 rd.push('0');
466 }
467 let suff = format!(
468 "\\.{}",
469 lexi_range(&ld, &rd, left_inclusive, right_inclusive)?
470 );
471 if ld.parse::<i64>().unwrap_or(0) == 0 {
472 Ok(format!("({}({})?)", left_rec, suff))
473 } else {
474 Ok(format!("({}{})", left_rec, suff))
475 }
476 } else {
477 let mut parts = vec![];
478 if !ld.is_empty() || !left_inclusive {
479 parts.push(format!(
480 "({}\\.{})",
481 left_rec,
482 lexi_x_to_9(&ld, left_inclusive)?
483 ));
484 left_rec += 1;
485 }
486
487 if right_rec > left_rec {
488 let inner = rx_int_range(Some(left_rec), Some(right_rec - 1))?;
489 parts.push(format!("({}(\\.[0-9]+)?)", inner));
490 }
491
492 if !rd.is_empty() {
493 parts.push(format!(
494 "({}(\\.{})?)",
495 right_rec,
496 lexi_0_to_x(&rd, right_inclusive)?
497 ));
498 } else if right_inclusive {
499 parts.push(format!("{}(\\.0+)?", right_rec));
500 }
501
502 Ok(mk_or(parts))
503 }
504 }
505 }
506 }
507}
508
509pub fn check_number_bounds(num: &NumberSchema) -> Result<(), String> {
510 let (minimum, exclusive_minimum) = num.get_minimum();
511 let (maximum, exclusive_maximum) = num.get_maximum();
512 if let (Some(min), Some(max)) = (minimum, maximum) {
513 if min > max {
514 return Err(format!(
515 "minimum ({}) is greater than maximum ({})",
516 min, max
517 ));
518 }
519 if min == max && (exclusive_minimum || exclusive_maximum) {
520 let minimum_repr = if exclusive_minimum {
521 "exclusiveMinimum"
522 } else {
523 "minimum"
524 };
525 let maximum_repr = if exclusive_maximum {
526 "exclusiveMaximum"
527 } else {
528 "maximum"
529 };
530 return Err(format!(
531 "{} ({}) is equal to {} ({})",
532 minimum_repr, min, maximum_repr, max
533 ));
534 }
535 }
536 if let Some(d) = num.multiple_of.as_ref() {
537 if d.coef == 0 {
538 if let Some(min) = minimum {
539 if min > 0.0 || (exclusive_minimum && min >= 0.0) {
540 return Err(format!(
541 "minimum ({}) is greater than 0, but multipleOf is 0",
542 min
543 ));
544 }
545 };
546 if let Some(max) = maximum {
547 if max < 0.0 || (exclusive_maximum && max <= 0.0) {
548 return Err(format!(
549 "maximum ({}) is less than 0, but multipleOf is 0",
550 max
551 ));
552 }
553 };
554 return Ok(());
555 }
556 if let (Some(min), Some(max)) = (minimum, maximum) {
558 let step = d.to_f64();
559 let min = {
561 let first_num_ge_min = (min / step).ceil() * step;
562 let adjusted_min = if exclusive_minimum && first_num_ge_min == min {
563 first_num_ge_min + step
564 } else {
565 first_num_ge_min
566 };
567 if num.integer {
568 adjusted_min.ceil()
569 } else {
570 adjusted_min
571 }
572 };
573 let max = {
574 let first_num_le_max = (max / step).floor() * step;
575 let adjusted_max = if exclusive_maximum && first_num_le_max == max {
576 first_num_le_max - step
577 } else {
578 first_num_le_max
579 };
580 if num.integer {
581 adjusted_max.floor()
582 } else {
583 adjusted_max
584 }
585 };
586 if min > max {
587 return Err(format!(
588 "range {}{}, {}{} does not contain a multiple of {}",
589 if exclusive_minimum { "(" } else { "[" },
590 min,
591 max,
592 if exclusive_maximum { ")" } else { "]" },
593 step
594 ));
595 }
596 }
597 }
598 Ok(())
599}
600
601#[cfg(test)]
602mod test_ranges {
603 use super::{rx_float_range, rx_int_range};
604 use regex::Regex;
605
606 fn do_test_int_range(rx: &str, left: Option<i64>, right: Option<i64>) {
607 let re = Regex::new(&format!("^{}$", rx)).unwrap();
608 for n in (left.unwrap_or(0) - 1000)..=(right.unwrap_or(0) + 1000) {
609 let matches = re.is_match(&n.to_string());
610 let expected =
611 (left.is_none() || left.unwrap() <= n) && (right.is_none() || n <= right.unwrap());
612 if expected != matches {
613 let range_str = match (left, right) {
614 (Some(l), Some(r)) => format!("[{}, {}]", l, r),
615 (Some(l), None) => format!("[{}, ∞)", l),
616 (None, Some(r)) => format!("(-∞, {}]", r),
617 (None, None) => "(-∞, ∞)".to_string(),
618 };
619 if matches {
620 panic!("{} not in range {} but matches {:?}", n, range_str, rx);
621 } else {
622 panic!("{} in range {} but does not match {:?}", n, range_str, rx);
623 }
624 }
625 }
626 }
627
628 #[test]
629 fn test_int_range() {
630 let cases = vec![
631 (Some(0), Some(9)),
632 (Some(1), Some(7)),
633 (Some(0), Some(99)),
634 (Some(13), Some(170)),
635 (Some(13), Some(17)),
636 (Some(13), Some(27)),
637 (Some(13), Some(57)),
638 (Some(72), Some(91)),
639 (Some(723), Some(915)),
640 (Some(23), Some(915)),
641 (Some(-1), Some(915)),
642 (Some(-9), Some(9)),
643 (Some(-3), Some(3)),
644 (Some(-3), Some(0)),
645 (Some(-72), Some(13)),
646 (None, Some(0)),
647 (None, Some(7)),
648 (None, Some(23)),
649 (None, Some(725)),
650 (None, Some(-1)),
651 (None, Some(-17)),
652 (None, Some(-283)),
653 (Some(0), None),
654 (Some(2), None),
655 (Some(33), None),
656 (Some(234), None),
657 (Some(-1), None),
658 (Some(-87), None),
659 (Some(-329), None),
660 (None, None),
661 (Some(-13), Some(-13)),
662 (Some(-1), Some(-1)),
663 (Some(0), Some(0)),
664 (Some(1), Some(1)),
665 (Some(13), Some(13)),
666 ];
667
668 for (left, right) in cases {
669 let rx = rx_int_range(left, right).unwrap();
670 do_test_int_range(&rx, left, right);
671 }
672 }
673
674 fn do_test_float_range(
675 rx: &str,
676 left: Option<f64>,
677 right: Option<f64>,
678 left_inclusive: bool,
679 right_inclusive: bool,
680 ) {
681 let re = Regex::new(&format!("^{}$", rx)).unwrap();
682 let left_int = left.map(|x| {
683 let left_int = x.ceil() as i64;
684 if !left_inclusive && x == left_int as f64 {
685 left_int + 1
686 } else {
687 left_int
688 }
689 });
690 let right_int = right.map(|x| {
691 let right_int = x.floor() as i64;
692 if !right_inclusive && x == right_int as f64 {
693 right_int - 1
694 } else {
695 right_int
696 }
697 });
698 do_test_int_range(rx, left_int, right_int);
699
700 let eps1 = 0.0000001;
701 let eps2 = 0.01;
702 let test_cases = vec![
703 left.unwrap_or(-1000.0),
704 right.unwrap_or(1000.0),
705 0.0,
706 left_int.unwrap_or(-1000) as f64,
707 right_int.unwrap_or(1000) as f64,
708 ];
709 for x in test_cases {
710 for offset in [0.0, -eps1, eps1, -eps2, eps2, 1.0, -1.0].iter() {
711 let n = x + offset;
712 let matches = re.is_match(&n.to_string());
713 let left_cond =
714 left.is_none() || left.unwrap() < n || (left.unwrap() == n && left_inclusive);
715 let right_cond = right.is_none()
716 || right.unwrap() > n
717 || (right.unwrap() == n && right_inclusive);
718 let expected = left_cond && right_cond;
719 if expected != matches {
720 let lket = if left_inclusive { "[" } else { "(" };
721 let rket = if right_inclusive { "]" } else { ")" };
722 let range_str = match (left, right) {
723 (Some(l), Some(r)) => format!("{}{}, {}{}", lket, l, r, rket),
724 (Some(l), None) => format!("{}{}, ∞)", lket, l),
725 (None, Some(r)) => format!("(-∞, {}{}", r, rket),
726 (None, None) => "(-∞, ∞)".to_string(),
727 };
728 if matches {
729 panic!("{} not in range {} but matches {:?}", n, range_str, rx);
730 } else {
731 panic!("{} in range {} but does not match {:?}", n, range_str, rx);
732 }
733 }
734 }
735 }
736 }
737
738 #[test]
739 fn test_float_range() {
740 let cases = vec![
741 (Some(0.0), Some(10.0)),
742 (Some(-10.0), Some(0.0)),
743 (Some(0.5), Some(0.72)),
744 (Some(0.5), Some(1.72)),
745 (Some(0.5), Some(1.32)),
746 (Some(0.45), Some(0.5)),
747 (Some(0.3245), Some(0.325)),
748 (Some(0.443245), Some(0.44325)),
749 (Some(1.0), Some(2.34)),
750 (Some(1.33), Some(2.0)),
751 (Some(1.0), Some(10.34)),
752 (Some(1.33), Some(10.0)),
753 (Some(-1.33), Some(10.0)),
754 (Some(-17.23), Some(-1.33)),
755 (Some(-1.23), Some(-1.221)),
756 (Some(-10.2), Some(45293.9)),
757 (None, Some(0.0)),
758 (None, Some(1.0)),
759 (None, Some(1.5)),
760 (None, Some(1.55)),
761 (None, Some(-17.23)),
762 (None, Some(-1.33)),
763 (None, Some(-1.23)),
764 (None, Some(103.74)),
765 (None, Some(100.0)),
766 (Some(0.0), None),
767 (Some(1.0), None),
768 (Some(1.5), None),
769 (Some(1.55), None),
770 (Some(-17.23), None),
771 (Some(-1.33), None),
772 (Some(-1.23), None),
773 (Some(103.74), None),
774 (Some(100.0), None),
775 (None, None),
776 (Some(-103.4), Some(-103.4)),
777 (Some(-27.0), Some(-27.0)),
778 (Some(-1.5), Some(-1.5)),
779 (Some(-1.0), Some(-1.0)),
780 (Some(0.0), Some(0.0)),
781 (Some(1.0), Some(1.0)),
782 (Some(1.5), Some(1.5)),
783 (Some(27.0), Some(27.0)),
784 (Some(103.4), Some(103.4)),
785 ];
786
787 for (left, right) in cases {
788 for left_inclusive in [true, false].iter() {
789 for right_inclusive in [true, false].iter() {
790 match (left, right) {
791 (Some(left), Some(right))
792 if left == right && !(*left_inclusive && *right_inclusive) =>
793 {
794 assert!(rx_float_range(
795 Some(left),
796 Some(right),
797 *left_inclusive,
798 *right_inclusive
799 )
800 .is_err());
801 }
802 _ => {
803 let rx = rx_float_range(left, right, *left_inclusive, *right_inclusive)
804 .unwrap();
805 do_test_float_range(
806 &rx,
807 left,
808 right,
809 *left_inclusive,
810 *right_inclusive,
811 );
812 }
813 }
814 }
815 }
816 }
817 }
818}
819
820#[cfg(test)]
821mod test_decimal {
822 use super::Decimal;
823
824 #[test]
825 fn test_from_f64() {
826 let cases = vec![
827 (0.0, Decimal { coef: 0, exp: 0 }),
828 (1.0, Decimal { coef: 1, exp: 0 }),
829 (10.0, Decimal { coef: 10, exp: 0 }),
830 (100.0, Decimal { coef: 100, exp: 0 }),
831 (0.1, Decimal { coef: 1, exp: 1 }),
832 (0.01, Decimal { coef: 1, exp: 2 }),
833 (1.1, Decimal { coef: 11, exp: 1 }),
834 (1.01, Decimal { coef: 101, exp: 2 }),
835 (10.1, Decimal { coef: 101, exp: 1 }),
836 (10.01, Decimal { coef: 1001, exp: 2 }),
837 (100.1, Decimal { coef: 1001, exp: 1 }),
838 (
839 100.01,
840 Decimal {
841 coef: 10001,
842 exp: 2,
843 },
844 ),
845 ];
846 for (f, d) in cases {
847 assert_eq!(Decimal::try_from(f).unwrap(), d);
848 }
849 }
850
851 #[test]
852 fn test_simplified() {
853 let cases = vec![
854 (Decimal::new(10, 1), Decimal { coef: 1, exp: 0 }),
855 (Decimal::new(100, 2), Decimal { coef: 1, exp: 0 }),
856 (Decimal::new(100, 1), Decimal { coef: 10, exp: 0 }),
857 (Decimal::new(1000, 3), Decimal { coef: 1, exp: 0 }),
858 (Decimal::new(1000, 2), Decimal { coef: 10, exp: 0 }),
859 (Decimal::new(1000, 1), Decimal { coef: 100, exp: 0 }),
860 (Decimal::new(10000, 4), Decimal { coef: 1, exp: 0 }),
861 (Decimal::new(10000, 3), Decimal { coef: 10, exp: 0 }),
862 (Decimal::new(10000, 2), Decimal { coef: 100, exp: 0 }),
863 (Decimal::new(10000, 1), Decimal { coef: 1000, exp: 0 }),
864 ];
865 for (d, s) in cases {
866 assert_eq!(d, s);
867 }
868 }
869
870 #[test]
871 fn test_lcm() {
872 let cases = vec![
873 (2.0, 3.0, 6.0),
874 (0.5, 1.5, 1.5),
875 (0.5, 0.5, 0.5),
876 (0.5, 0.25, 0.5),
877 (0.3, 0.2, 0.6),
878 (0.3, 0.4, 1.2),
879 (0.05, 0.36, 1.8),
880 (0.3, 14.0, 42.0),
881 ];
882 for (a, b, c) in cases {
883 let a = Decimal::try_from(a).unwrap();
884 let b = Decimal::try_from(b).unwrap();
885 let c = Decimal::try_from(c).unwrap();
886 assert_eq!(a.lcm(&b), c);
887 }
888 }
889}
890
891#[cfg(test)]
892mod test_number_bounds {
893 use crate::json::schema::NumberSchema;
894
895 use super::{check_number_bounds, Decimal};
896
897 #[derive(Debug)]
898 struct Case {
899 minimum: Option<f64>,
900 maximum: Option<f64>,
901 exclusive_minimum: bool,
902 exclusive_maximum: bool,
903 integer: bool,
904 multiple_of: Option<Decimal>,
905 ok: bool,
906 }
907
908 impl Case {
909 fn to_number_schema(&self) -> NumberSchema {
910 NumberSchema {
911 minimum: if self.exclusive_minimum {
912 None
913 } else {
914 self.minimum
915 },
916 maximum: if self.exclusive_maximum {
917 None
918 } else {
919 self.maximum
920 },
921 exclusive_minimum: if self.exclusive_minimum {
922 self.minimum
923 } else {
924 None
925 },
926 exclusive_maximum: if self.exclusive_maximum {
927 self.maximum
928 } else {
929 None
930 },
931 integer: self.integer,
932 multiple_of: self.multiple_of.clone(),
933 }
934 }
935 }
936
937 #[test]
938 fn test_check_number_bounds() {
939 let cases = vec![
940 Case {
941 minimum: Some(5.5),
942 maximum: Some(6.0),
943 exclusive_minimum: false,
944 exclusive_maximum: false,
945 integer: false,
946 multiple_of: Decimal::try_from(0.5).ok(),
947 ok: true,
948 },
949 Case {
950 minimum: Some(5.5),
951 maximum: Some(6.0),
952 exclusive_minimum: true,
953 exclusive_maximum: false,
954 integer: false,
955 multiple_of: Decimal::try_from(0.5).ok(),
956 ok: true,
957 },
958 Case {
959 minimum: Some(5.5),
960 maximum: Some(6.0),
961 exclusive_minimum: false,
962 exclusive_maximum: true,
963 integer: false,
964 multiple_of: Decimal::try_from(0.5).ok(),
965 ok: true,
966 },
967 Case {
968 minimum: Some(5.5),
969 maximum: Some(6.0),
970 exclusive_minimum: true,
971 exclusive_maximum: true,
972 integer: false,
973 multiple_of: Decimal::try_from(0.5).ok(),
974 ok: false,
975 },
976 Case {
977 minimum: Some(5.5),
978 maximum: Some(6.0),
979 exclusive_minimum: false,
980 exclusive_maximum: false,
981 integer: true,
982 multiple_of: Decimal::try_from(0.5).ok(),
983 ok: true,
984 },
985 Case {
986 minimum: Some(5.5),
987 maximum: Some(6.0),
988 exclusive_minimum: true,
989 exclusive_maximum: false,
990 integer: true,
991 multiple_of: Decimal::try_from(0.5).ok(),
992 ok: true,
993 },
994 Case {
995 minimum: Some(5.5),
996 maximum: Some(6.0),
997 exclusive_minimum: false,
998 exclusive_maximum: true,
999 integer: true,
1000 multiple_of: Decimal::try_from(0.5).ok(),
1001 ok: false,
1002 },
1003 Case {
1004 minimum: Some(5.5),
1005 maximum: Some(6.0),
1006 exclusive_minimum: true,
1007 exclusive_maximum: true,
1008 integer: true,
1009 multiple_of: Decimal::try_from(0.5).ok(),
1010 ok: false,
1011 },
1012 Case {
1014 minimum: Some(0.0),
1015 maximum: Some(10.0),
1016 exclusive_minimum: false,
1017 exclusive_maximum: false,
1018 integer: true,
1019 multiple_of: Decimal::try_from(2.0).ok(),
1020 ok: true,
1021 },
1022 Case {
1023 minimum: Some(0.0),
1024 maximum: Some(10.0),
1025 exclusive_minimum: true,
1026 exclusive_maximum: false,
1027 integer: true,
1028 multiple_of: Decimal::try_from(2.0).ok(),
1029 ok: true,
1030 },
1031 Case {
1032 minimum: Some(0.0),
1033 maximum: Some(10.0),
1034 exclusive_minimum: true,
1035 exclusive_maximum: true,
1036 integer: true,
1037 multiple_of: Decimal::try_from(2.0).ok(),
1038 ok: true,
1039 },
1040 Case {
1042 minimum: Some(-10.0),
1043 maximum: Some(-5.0),
1044 exclusive_minimum: false,
1045 exclusive_maximum: false,
1046 integer: true,
1047 multiple_of: Decimal::try_from(1.0).ok(),
1048 ok: true,
1049 },
1050 Case {
1051 minimum: Some(-10.0),
1052 maximum: Some(-5.0),
1053 exclusive_minimum: false,
1054 exclusive_maximum: false,
1055 integer: false,
1056 multiple_of: Decimal::try_from(0.1).ok(),
1057 ok: true,
1058 },
1059 Case {
1061 minimum: Some(1.0),
1062 maximum: Some(1.01),
1063 exclusive_minimum: false,
1064 exclusive_maximum: false,
1065 integer: false,
1066 multiple_of: Decimal::try_from(0.005).ok(),
1067 ok: true,
1068 },
1069 Case {
1070 minimum: Some(1.0),
1071 maximum: Some(1.01),
1072 exclusive_minimum: true,
1073 exclusive_maximum: true,
1074 integer: false,
1075 multiple_of: Decimal::try_from(0.005).ok(),
1076 ok: true,
1077 },
1078 Case {
1079 minimum: Some(1.0),
1080 maximum: Some(1.01),
1081 exclusive_minimum: false,
1082 exclusive_maximum: false,
1083 integer: false,
1084 multiple_of: Decimal::try_from(0.01).ok(),
1085 ok: true,
1086 },
1087 Case {
1088 minimum: Some(1.0),
1089 maximum: Some(1.01),
1090 exclusive_minimum: true,
1091 exclusive_maximum: true,
1092 integer: false,
1093 multiple_of: Decimal::try_from(0.01).ok(),
1094 ok: false,
1095 },
1096 Case {
1098 minimum: Some(1.0),
1099 maximum: Some(1e9),
1100 exclusive_minimum: false,
1101 exclusive_maximum: false,
1102 integer: true,
1103 multiple_of: Decimal::try_from(100000.0).ok(),
1104 ok: true,
1105 },
1106 Case {
1108 minimum: Some(f64::NEG_INFINITY),
1109 maximum: Some(10.0),
1110 exclusive_minimum: false,
1111 exclusive_maximum: false,
1112 integer: true,
1113 multiple_of: Decimal::try_from(1.0).ok(),
1114 ok: true,
1115 },
1116 Case {
1117 minimum: Some(0.0),
1118 maximum: Some(f64::INFINITY),
1119 exclusive_minimum: false,
1120 exclusive_maximum: false,
1121 integer: true,
1122 multiple_of: Decimal::try_from(1.0).ok(),
1123 ok: true,
1124 },
1125 Case {
1127 minimum: Some(1.0),
1128 maximum: Some(10.0),
1129 exclusive_minimum: false,
1130 exclusive_maximum: false,
1131 integer: false,
1132 multiple_of: Decimal::try_from(1.0).ok(),
1133 ok: true,
1134 },
1135 Case {
1136 minimum: Some(1.0),
1137 maximum: Some(10.0),
1138 exclusive_minimum: false,
1139 exclusive_maximum: false,
1140 integer: false,
1141 multiple_of: Decimal::try_from(0.3).ok(),
1142 ok: true,
1143 },
1144 Case {
1145 minimum: Some(1.0),
1146 maximum: Some(10.0),
1147 exclusive_minimum: false,
1148 exclusive_maximum: false,
1149 integer: false,
1150 multiple_of: Decimal::try_from(0.0).ok(),
1151 ok: false,
1152 },
1153 Case {
1154 minimum: Some(0.0),
1155 maximum: Some(10.0),
1156 exclusive_minimum: true,
1157 exclusive_maximum: false,
1158 integer: false,
1159 multiple_of: Decimal::try_from(0.0).ok(),
1160 ok: false,
1161 },
1162 Case {
1163 minimum: Some(0.0),
1164 maximum: Some(10.0),
1165 exclusive_minimum: false,
1166 exclusive_maximum: false,
1167 integer: false,
1168 multiple_of: Decimal::try_from(0.0).ok(),
1169 ok: true,
1170 },
1171 ];
1172 for case in cases {
1173 let result = check_number_bounds(&case.to_number_schema());
1174 assert_eq!(
1175 result.is_ok(),
1176 case.ok,
1177 "Failed for case {:?} with result {:?}",
1178 case,
1179 result
1180 );
1181 }
1182 }
1183}