Handle attributes and self-closing tags
This commit is contained in:
150
lib/xml_schema_validator/src/token/attribute_name.rs
Normal file
150
lib/xml_schema_validator/src/token/attribute_name.rs
Normal file
@@ -0,0 +1,150 @@
|
||||
use std::sync::Arc;
|
||||
use crate::*;
|
||||
|
||||
/// Represents an attribute name of an XML element
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AttributeName {
|
||||
/// The element that owns this attribute
|
||||
element: Arc<schema::XsElement>,
|
||||
/// Buffer containing the attribute name characters processed so far
|
||||
buffer: String,
|
||||
}
|
||||
|
||||
impl AttributeName {
|
||||
/// Create a new AttributeName with an empty buffer
|
||||
pub fn new(element: Arc<schema::XsElement>) -> Self {
|
||||
Self {
|
||||
element,
|
||||
buffer: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new AttributeName with the given buffer
|
||||
pub fn with_buffer(element: Arc<schema::XsElement>, buffer: String) -> Self {
|
||||
Self {
|
||||
element,
|
||||
buffer,
|
||||
}
|
||||
}
|
||||
|
||||
/// Append a character to the attribute name and return possible continuations
|
||||
pub fn append(self, c: char) -> Vec<Token> {
|
||||
if c.is_whitespace() && self.buffer.is_empty() {
|
||||
return vec![Token::AttributeName(self)];
|
||||
} else if c == '=' {
|
||||
// Found the equals sign, transition to attribute value
|
||||
// We need to verify that this attribute name is valid for this element
|
||||
if let Some(attributes) = &self.element.xs_complex_type.xs_attribute {
|
||||
for attr in attributes {
|
||||
if attr.name == self.buffer {
|
||||
// Valid attribute, transition to attribute value
|
||||
return vec![Token::AttributeValue(AttributeValue::new(
|
||||
Arc::clone(&self.element),
|
||||
self.buffer,
|
||||
))];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Invalid attribute name
|
||||
vec![]
|
||||
} else {
|
||||
// Continue building the attribute name
|
||||
let new_buffer = self.buffer + &c.to_string();
|
||||
|
||||
// Check if this could be a valid attribute for this element
|
||||
if let Some(attributes) = &self.element.xs_complex_type.xs_attribute {
|
||||
for attr in attributes {
|
||||
if attr.name.starts_with(&new_buffer) {
|
||||
// This could be a valid attribute, continue parsing
|
||||
return vec![Token::AttributeName(AttributeName::with_buffer(
|
||||
Arc::clone(&self.element),
|
||||
new_buffer,
|
||||
))];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No matching attribute found
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod attribute_name_tests {
|
||||
use super::*;
|
||||
use std::sync::Arc;
|
||||
use crate::schema;
|
||||
|
||||
fn create_element_with_attributes() -> Arc<schema::XsElement> {
|
||||
let element = schema::XsElement {
|
||||
name: "delete".to_string(),
|
||||
text: None,
|
||||
xs_complex_type: schema::XsComplexType {
|
||||
mixed: None,
|
||||
text: None,
|
||||
xs_attribute: Some(vec![
|
||||
schema::XsAttribute {
|
||||
name: "id".to_string(),
|
||||
xs_attribute_type: "xs:string".to_string(),
|
||||
xs_attribute_use: "required".to_string(),
|
||||
}
|
||||
]),
|
||||
xs_sequence: None,
|
||||
},
|
||||
};
|
||||
Arc::new(element)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attribute_name_append() {
|
||||
let element = create_element_with_attributes();
|
||||
let token = AttributeName::new(Arc::clone(&element));
|
||||
|
||||
// Build attribute name character by character
|
||||
let mut current_token = token;
|
||||
for c in "id".chars() {
|
||||
let next_tokens = current_token.append(c);
|
||||
assert!(!next_tokens.is_empty(), "Should accept attribute name character '{}'", c);
|
||||
|
||||
match &next_tokens[0] {
|
||||
Token::AttributeName(next) => current_token = next.clone(),
|
||||
_ => panic!("Expected AttributeName, got {:?}", next_tokens[0]),
|
||||
}
|
||||
}
|
||||
|
||||
// Test equals sign
|
||||
let next_tokens = current_token.append('=');
|
||||
assert!(!next_tokens.is_empty(), "Should accept '=' after attribute name");
|
||||
|
||||
match &next_tokens[0] {
|
||||
Token::AttributeValue(_) => (),
|
||||
_ => panic!("Expected AttributeValue, got {:?}", next_tokens[0]),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attribute_name_whitespace() {
|
||||
let element = create_element_with_attributes();
|
||||
let token = AttributeName::new(Arc::clone(&element));
|
||||
|
||||
// Test with whitespace
|
||||
let next_tokens = token.append(' ');
|
||||
assert!(!next_tokens.is_empty(), "Should accept whitespace in attribute name");
|
||||
|
||||
match &next_tokens[0] {
|
||||
Token::AttributeName(_) => (),
|
||||
_ => panic!("Expected AttributeName, got {:?}", next_tokens[0]),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_attribute_name() {
|
||||
let element = create_element_with_attributes();
|
||||
let token = AttributeName::new(Arc::clone(&element));
|
||||
|
||||
// Test with invalid attribute name
|
||||
let next_tokens = token.append('x'); // 'x' doesn't start any valid attribute
|
||||
assert!(next_tokens.is_empty(), "Should reject invalid attribute name");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user