Skip to main content

mx20022_validate/
typed.rs

1//! Typed validation bridge.
2//!
3//! Converts [`ConstraintViolation`]s from the model crate's [`Validatable`]
4//! trait into the validate crate's [`ValidationResult`].
5//!
6//! # Example
7//!
8//! ```no_run
9//! use mx20022_validate::typed::validate_constraints;
10//!
11//! // Given a type that implements Validatable:
12//! // let result = validate_constraints(&my_message, "/Document");
13//! // assert!(result.is_valid());
14//! ```
15
16use mx20022_model::common::validate::{
17    ConstraintKind, ConstraintViolation, IsoMessage, Validatable,
18};
19
20use crate::error::{Severity, ValidationError, ValidationResult};
21
22/// Validate XSD constraints on a typed value, producing a [`ValidationResult`].
23///
24/// Calls [`Validatable::validate_constraints`] on `msg` and maps each
25/// [`ConstraintViolation`] to a [`ValidationError`] with `Severity::Error`.
26pub fn validate_constraints<T: Validatable>(msg: &T, base_path: &str) -> ValidationResult {
27    let mut violations = Vec::new();
28    msg.validate_constraints(base_path, &mut violations);
29    ValidationResult::new(violations.into_iter().map(violation_to_error).collect())
30}
31
32/// Validate a complete ISO 20022 message using its own root path.
33///
34/// Convenience wrapper that extracts the root path from [`IsoMessage`] and
35/// calls [`validate_constraints`].
36pub fn validate_message<T: IsoMessage>(msg: &T) -> ValidationResult {
37    validate_constraints(msg, msg.root_path())
38}
39
40/// Map a [`ConstraintViolation`] to a [`ValidationError`].
41fn violation_to_error(v: ConstraintViolation) -> ValidationError {
42    ValidationError::new(
43        v.path,
44        Severity::Error,
45        constraint_rule_id(v.kind),
46        v.message,
47    )
48}
49
50/// Map a [`ConstraintKind`] to a stable rule ID string.
51fn constraint_rule_id(kind: ConstraintKind) -> &'static str {
52    match kind {
53        ConstraintKind::MinLength => "XSD_MIN_LENGTH",
54        ConstraintKind::MaxLength => "XSD_MAX_LENGTH",
55        ConstraintKind::Pattern => "XSD_PATTERN",
56        ConstraintKind::MinInclusive => "XSD_MIN_INCLUSIVE",
57        ConstraintKind::MaxInclusive => "XSD_MAX_INCLUSIVE",
58        ConstraintKind::TotalDigits => "XSD_TOTAL_DIGITS",
59        ConstraintKind::FractionDigits => "XSD_FRACTION_DIGITS",
60    }
61}