diff --git a/src/validation.rs b/src/validation.rs index 84e3248..1f3db36 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -26,6 +26,7 @@ use crate::{ datamodel::DataModel, markdown::position::Position, object::{Enumeration, Object}, + xmltype::XMLType, }; use colored::Colorize; use log::error; @@ -417,6 +418,17 @@ impl Validator { for dtype in &attribute.dtypes { self.check_attr_dtype(attribute, types, object, dtype); } + + if let Some(xml_option) = &attribute.xml { + match xml_option { + XMLType::Attribute { name, .. } => { + self.validate_xml_attribute_option(name, &object.name, &attribute.name); + } + XMLType::Element { name, .. } => { + self.validate_xml_element_option(name, &object.name, &attribute.name); + } + } + } } /// Checks the data type of attribute. @@ -502,6 +514,101 @@ impl Validator { } } + /// Validates an XML element option string. + /// + /// # Arguments + /// + /// * `option` - The XML element option string to validate. Can contain multiple comma-separated values. + /// + /// Checks that: + /// - The option string is not empty + /// - Each comma-separated value contains no special characters + fn validate_xml_element_option( + &mut self, + option: &str, + object_name: &str, + attribute_name: &str, + ) { + let option = option.trim(); + if option.is_empty() { + self.add_error(ValidationError { + message: "XML option is not defined.".into(), + object: Some(object_name.to_string()), + attribute: Some(attribute_name.to_string()), + location: "Global".into(), + error_type: ErrorType::GlobalError, + positions: vec![], + }); + } + + let options = option.split(',').map(|s| s.trim()).collect::>(); + for opt in options { + if contains_special_characters(opt.trim()).is_err() { + self.add_error(ValidationError { + message: format!("XML option '{}' contains special characters.", opt), + object: Some(object_name.to_string()), + attribute: Some(attribute_name.to_string()), + location: "Global".into(), + error_type: ErrorType::GlobalError, + positions: vec![], + }); + } + } + } + + /// Validates an XML attribute option string. + /// + /// # Arguments + /// + /// * `option` - The XML attribute option string to validate. Can contain multiple comma-separated values. + /// * `object_name` - The name of the object containing this attribute + /// * `attribute_name` - The name of the attribute being validated + /// + /// Checks that: + /// - The option string is not empty + /// - Each comma-separated value contains no special characters + /// + /// # Errors + /// + /// Adds validation errors to the validator if: + /// - The option string is empty + /// - Any of the comma-separated values contain special characters + fn validate_xml_attribute_option( + &mut self, + option: &str, + object_name: &str, + attribute_name: &str, + ) { + let option = option.trim(); + if option.is_empty() { + self.add_error(ValidationError { + message: "XML attribute option is not defined.".into(), + object: Some(object_name.to_string()), + attribute: Some(attribute_name.to_string()), + location: "Global".into(), + error_type: ErrorType::GlobalError, + positions: vec![], + }); + } + + let options = option.split(',').map(|s| s.trim()).collect::>(); + for opt in options { + if contains_special_characters(opt).is_err() { + self.add_error(ValidationError { + message: format!( + "XML attribute option '{}' contains special characters.", + opt + ), + object: Some(object_name.to_string()), + attribute: Some(attribute_name.to_string()), + location: "Global".into(), + error_type: ErrorType::GlobalError, + positions: vec![], + }); + } + } + } + /// Extracts the type names from the data model. /// /// # Arguments diff --git a/tests/data/model_invalid_xml_option.md b/tests/data/model_invalid_xml_option.md new file mode 100644 index 0000000..0113a5a --- /dev/null +++ b/tests/data/model_invalid_xml_option.md @@ -0,0 +1,37 @@ +--- +id-field: true +repo: "https://www.github.com/my/repo/" +prefix: "tst" +prefixes: + schema: http://schema.org/ +nsmap: + tst: http://example.com/test/ +--- + +### Test + +- __empty_attribute__ + - Type: string + - Term: schema:hello + - Description: The name of the test. + - XML: @ +- __empty_element__ + - Type: string + - Term: schema:hello + - Description: The name of the test. + - XML: +- __special_character_element__ + - Type: string + - Term: schema:hello + - Description: The name of the test. + - XML: schema:hello +- __special_character_attribute__ + - Type: string + - Term: schema:hello + - Description: The name of the test. + - XML: @schema:hello +- __multiple_types__ + - Type: string, float + - Term: schema:hello + - Description: The name of the test. + - XML: fine, bad: diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 3c3f681..dad92f7 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -332,6 +332,23 @@ mod tests { DataModel::from_markdown(path).expect("Could not parse markdown"); } + #[test] + fn test_invalid_xml_option() { + let path = Path::new("tests/data/model_invalid_xml_option.md"); + let result = DataModel::from_markdown(path); + + if let Err(e) = result { + assert_eq!( + e.errors.len(), + 5, + "Expected 5 errors, got {}", + e.errors.len() + ); + } else { + panic!("Expected error, but got success"); + } + } + #[test] fn test_multiple_types() { let path = Path::new("tests/data/model_multiple_types.md");