Files
SIA/lib/xml_schema_validator/src/token/text_content.rs
2025-04-13 22:02:29 +02:00

222 lines
6.7 KiB
Rust

use crate::*;
use std::sync::Arc;
/// Represents the text content within an XML element
#[derive(Clone, Debug)]
pub struct TextContent {
element: Arc<schema::XsElement>,
}
impl TextContent {
pub fn new(element: Arc<schema::XsElement>) -> Self {
Self { element }
}
pub fn append(self, c: char) -> Vec<Token> {
if c == '<' {
let is_mixed = self
.element
.xs_complex_type
.mixed
.as_ref()
.map(|val| val == "true")
.unwrap_or(false);
let mut tokens = vec![Token::ElementCloseStart(ElementCloseStart::new(
Arc::clone(&self.element),
))];
// If mixed content is allowed, also add Comment and CData as possible transitions
if is_mixed {
tokens.push(Token::Comment(Comment::new(Arc::clone(&self.element))));
tokens.push(Token::CData(CData::new(Arc::clone(&self.element))));
}
tokens
} else {
vec![Token::TextContent(TextContent::new(Arc::clone(
&self.element,
)))]
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_text_content() {
let values = [
"<reasoning>text",
"<reasoning>Hello world",
"<reasoning>Special chars: !@#$%^&*()",
"<reasoning>Numbers: 12345",
"<reasoning>Mixed content with spaces",
"<write_stdout>Output message",
"<single>ls -la /tmp",
];
for value in values {
let mut validator = crate::tests::example_validator();
validator.append(value).expect(value);
assert!(!validator.current_tokens.is_empty(), "{value}");
assert!(
validator
.current_tokens
.iter()
.any(|token| matches!(token, Token::TextContent(_))),
"{value}"
);
}
}
#[test]
fn test_whitespace_text_content() {
let values = [
"<reasoning> ",
"<reasoning> ",
"<reasoning>\t",
"<reasoning>\n",
"<reasoning> \t\n ",
"<write_stdout> ",
"<single> ",
];
for value in values {
let mut validator = crate::tests::example_validator();
validator.append(value).expect(value);
assert!(!validator.current_tokens.is_empty(), "{value}");
assert!(
validator
.current_tokens
.iter()
.any(|token| matches!(token, Token::TextContent(_))),
"{value}"
);
}
}
#[test]
fn test_valid_element_close_start_after_text() {
let values = [
"<reasoning>text<",
"<reasoning>Hello world<",
"<write_stdout>message<",
"<single>command<",
];
for value in values {
let mut validator = crate::tests::example_validator();
validator.append(value).expect(value);
assert!(!validator.current_tokens.is_empty(), "{value}");
assert!(
validator
.current_tokens
.iter()
.any(|token| matches!(token, Token::ElementCloseStart(_))),
"{value}"
);
}
}
#[test]
fn test_complete_element_with_text() {
let values = [
"<reasoning>text</reasoning>",
"<reasoning>Hello world</reasoning>",
"<write_stdout>message</write_stdout>",
"<single>command</single>",
];
for value in values {
let mut validator = crate::tests::example_validator();
validator.append(value).expect(value);
assert!(!validator.current_tokens.is_empty(), "{value}");
assert!(
validator
.current_tokens
.iter()
.any(|token| matches!(token, Token::EndOfFile(_))),
"{value}"
);
}
}
// New tests for transitions to comment and CDATA
#[test]
fn test_text_to_comment_transition() {
let values = [
"<reasoning>text<",
"<reasoning>text<!",
"<reasoning>text<!--",
"<reasoning>text<!-- comment",
];
for value in values {
let mut validator = crate::tests::example_validator();
validator.append(value).expect(value);
assert!(!validator.current_tokens.is_empty(), "{value}");
let is_valid = validator
.current_tokens
.iter()
.any(|token| matches!(token, Token::Comment(_)));
assert!(is_valid, "{value}");
}
}
#[test]
fn test_text_to_cdata_transition() {
let values = [
"<reasoning>text<",
"<reasoning>text<!",
"<reasoning>text<![",
"<reasoning>text<![C",
"<reasoning>text<![CD",
"<reasoning>text<![CDA",
"<reasoning>text<![CDAT",
"<reasoning>text<![CDATA",
"<reasoning>text<![CDATA[",
];
for value in values {
let mut validator = crate::tests::example_validator();
validator.append(value).expect(value);
assert!(!validator.current_tokens.is_empty(), "{value}");
// Should transition to CDATA state or remain in TextContent for partial prefix
let is_valid = validator
.current_tokens
.iter()
.any(|token| matches!(token, Token::CData(_)));
assert!(is_valid, "{value}");
}
}
#[test]
fn test_text_with_mixed_content() {
let values = [
"<reasoning>text<!-- comment -->more text</reasoning>",
"<reasoning>text<![CDATA[cdata content]]>more text</reasoning>",
"<reasoning>text<!-- comment --><![CDATA[cdata]]>more</reasoning>",
"<reasoning><!-- c1 -->text<![CDATA[cd]]><!-- c2 --></reasoning>",
];
for value in values {
let mut validator = crate::tests::example_validator();
validator.append(value).expect(value);
assert!(!validator.current_tokens.is_empty(), "{value}");
assert!(
validator
.current_tokens
.iter()
.any(|token| matches!(token, Token::EndOfFile(_))),
"{value}"
);
}
}
}