fatoora_core/invoice/
validation.rs1use crate::config::Config;
3use libxml::{
4 error::StructuredError,
5 parser::Parser,
6 schemas::{SchemaParserContext, SchemaValidationContext},
7};
8use std::path::PathBuf;
9use thiserror::Error;
10
11pub type ValidationResult = Result<(), XmlValidationError>;
12
13#[derive(Debug, Error)]
15pub enum XmlValidationError {
16 #[error("invalid XSD path: {path}")]
17 InvalidXsdPath { path: String },
18 #[error("schema parser error")]
19 SchemaParse { errors: Vec<StructuredError> },
20 #[error("XML parse error: {message}")]
21 XmlParse { message: String },
22 #[error("schema validation error")]
23 SchemaValidation { errors: Vec<StructuredError> },
24}
25
26fn bundled_xsd_path() -> PathBuf {
27 PathBuf::from(env!("CARGO_MANIFEST_DIR"))
28 .join("assets/schemas/UBL2.1/xsd/maindoc/UBL-Invoice-2.1.xsd")
29}
30
31fn build_validation_context(_config: &Config) -> Result<SchemaValidationContext, XmlValidationError> {
32 let xsd_path_buf = bundled_xsd_path();
33 let xsd_path = xsd_path_buf.to_str().ok_or_else(|| {
34 XmlValidationError::InvalidXsdPath {
35 path: xsd_path_buf.display().to_string(),
36 }
37 })?;
38
39 let mut parser_ctx = SchemaParserContext::from_file(xsd_path);
40 SchemaValidationContext::from_parser(&mut parser_ctx)
41 .map_err(|errors| XmlValidationError::SchemaParse { errors })
42}
43
44pub fn validate_xml_invoice_from_str(xml: &str, config: &Config) -> ValidationResult {
49 let mut validation_ctx = build_validation_context(config)?;
50 let document = Parser::default()
51 .parse_string(xml)
52 .map_err(|e| XmlValidationError::XmlParse {
53 message: format!("{e:?}"),
54 })?;
55
56 validation_ctx
57 .validate_document(&document)
58 .map_err(|errors| XmlValidationError::SchemaValidation { errors })
59}