use crate::*; use std::sync::Arc; /// Represents the text content within an XML element #[derive(Clone, Debug)] pub struct TextContent { element: Arc, } impl TextContent { pub fn new(element: Arc) -> Self { Self { element } } pub fn append(self, c: char) -> Vec { 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 = [ "text", "Hello world", "Special chars: !@#$%^&*()", "Numbers: 12345", "Mixed content with spaces", "Output message", "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 = [ " ", " ", "\t", "\n", " \t\n ", " ", " ", ]; 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 = [ "text<", "Hello world<", "message<", "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 = [ "text", "Hello world", "message", "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::EndOfFile(_))), "{value}" ); } } // New tests for transitions to comment and CDATA #[test] fn test_text_to_comment_transition() { let values = [ "text<", "texttextmore text", "textmore text", "textmore", "text", ]; 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}" ); } } }