1#![warn(missing_docs)]
127#![warn(rustdoc::missing_crate_level_docs)]
128#![allow(clippy::collapsible_if)]
130#![allow(clippy::collapsible_else_if)]
131#![allow(clippy::collapsible_match)]
132#![allow(clippy::large_enum_variant)]
133
134pub mod document;
135pub mod error;
136pub mod event;
137pub mod generator;
138pub mod namespace;
139pub mod namespace_error;
140pub mod node;
141pub mod node_error;
142pub mod parse_error;
143pub mod parser;
144pub mod position;
145pub mod profile;
146pub mod schema;
147pub mod serialize;
148pub mod transform;
149pub mod xpath;
150
151pub use error::{Error, ErrorLevel, ErrorLocation, Result, StructuredError, ValidationErrorType};
153
154pub use document::{DocumentBuilder, XmlDocument};
156
157pub use node::{NodeId, NodeType, XmlNode, XmlRoNode};
159
160pub use namespace::{Namespace, NamespaceResolver};
162
163pub use compact_str::CompactString;
165
166pub use parser::{
168 ParserOptions, parse, parse_from_bufread, parse_schema_locations, parse_with_options,
169};
170
171pub use position::PositionTrackingReader;
173
174pub use xpath::context::{XmlContext, XmlSafeContext};
176
177pub fn evaluate<T: AsRef<str>>(
185 document: &XmlDocument,
186 xpath_expr: T,
187) -> Result<xpath::XPathResult> {
188 xpath::evaluate(document, xpath_expr.as_ref())
189}
190
191pub fn create_context(document: &XmlDocument) -> Result<XmlContext> {
195 xpath::create_context(document)
196}
197
198pub fn create_safe_context(document: &XmlDocument) -> Result<XmlSafeContext> {
200 xpath::create_safe_context(document)
201}
202
203pub fn find_nodes_by_xpath(
205 ctx: &XmlContext,
206 xpath_expr: &str,
207 node: &XmlNode,
208) -> Result<Vec<XmlNode>> {
209 xpath::find_nodes_by_xpath(ctx, xpath_expr, node)
210}
211
212pub fn find_readonly_nodes_by_xpath(
214 ctx: &XmlContext,
215 xpath_expr: &str,
216 node: &XmlRoNode,
217) -> Result<Vec<XmlRoNode>> {
218 xpath::find_readonly_nodes_by_xpath(ctx, xpath_expr, node)
219}
220
221pub fn find_safe_readonly_nodes_by_xpath(
223 ctx: &XmlSafeContext,
224 xpath_expr: &str,
225 node: &XmlRoNode,
226) -> Result<Vec<XmlRoNode>> {
227 xpath::find_safe_readonly_nodes_by_xpath(ctx, xpath_expr, node)
228}
229
230pub fn find_readonly_nodes_in_elements(
232 ctx: &XmlContext,
233 node: &XmlRoNode,
234 elements_to_match: &[&str],
235) -> Result<Vec<XmlRoNode>> {
236 xpath::find_readonly_nodes_in_elements(ctx, node, elements_to_match)
237}
238
239pub fn collect_text_values(xpath_value: &xpath::XPathResult) -> Vec<String> {
241 xpath::collect_text_values(xpath_value)
242}
243
244pub fn collect_text_value(xpath_value: &xpath::XPathResult) -> String {
246 xpath::collect_text_value(xpath_value)
247}
248
249pub fn get_root_node(document: &XmlDocument) -> Result<XmlNode> {
251 document.get_root_element()
252}
253
254pub fn get_root_readonly_node(document: &XmlDocument) -> Result<XmlRoNode> {
256 document.get_root_element_ro()
257}
258
259pub fn get_node_tag(node: &XmlNode) -> String {
261 node.qname()
262}
263
264pub fn get_readonly_node_tag(node: &XmlRoNode) -> String {
266 node.qname()
267}
268
269pub fn get_node_prefix(node: &XmlNode) -> String {
271 node.get_prefix().unwrap_or_default()
272}
273
274pub fn get_readonly_node_prefix(node: &XmlRoNode) -> String {
276 node.get_prefix().unwrap_or_default()
277}
278
279pub fn node_to_xml_string(document: &XmlDocument, node: &mut XmlNode) -> Result<String> {
281 serialize::node_to_xml_string(document, node)
282}
283
284pub fn readonly_node_to_xml_string(document: &XmlDocument, node: &XmlRoNode) -> Result<String> {
286 serialize::readonly_node_to_xml_string(document, node)
287}
288
289pub fn create_xml_schema_validation_context(
291 schema_location: String,
292) -> Result<schema::XmlSchemaValidationContext> {
293 schema::create_xml_schema_validation_context(&schema_location)
294}
295
296pub fn create_xml_schema_validation_context_from_buffer(
298 schema: &[u8],
299) -> Result<schema::XmlSchemaValidationContext> {
300 schema::create_xml_schema_validation_context_from_buffer(schema)
301}
302
303pub fn validate_document_by_schema(
305 document: &XmlDocument,
306 schema_location: String,
307) -> Result<Vec<StructuredError>> {
308 schema::validate_document_by_schema(document, &schema_location)
309}
310
311pub fn validate_document_by_schema_context(
313 document: &XmlDocument,
314 ctx: &schema::XmlSchemaValidationContext,
315) -> Result<Vec<StructuredError>> {
316 schema::validate_document_by_schema_context(document, ctx)
317}
318
319pub fn parse_xsd(content: &[u8]) -> Result<schema::types::CompiledSchema> {
321 schema::parse_xsd(content)
322}
323
324#[cfg(feature = "ureq")]
326pub fn parse_xsd_with_imports(
327 content: &[u8],
328 base_uri: &str,
329 fetcher: &schema::UreqFetcher,
330 store: &impl schema::store::SchemaStore,
331) -> Result<schema::types::CompiledSchema> {
332 schema::parse_xsd_with_imports(content, base_uri, fetcher, store)
333}
334
335#[cfg(feature = "tokio")]
357pub async fn parse_xsd_with_imports_async<
358 F: schema::AsyncSchemaFetcher,
359 S: schema::AsyncSchemaStore,
360>(
361 content: &[u8],
362 base_uri: &str,
363 fetcher: &F,
364 store: &S,
365) -> Result<schema::types::CompiledSchema> {
366 schema::parse_xsd_with_imports_async(content, base_uri, fetcher, store).await
367}
368
369#[cfg(feature = "ureq")]
388pub fn validate_with_schema_location(document: &XmlDocument) -> Result<Vec<StructuredError>> {
389 schema::validate_with_schema_location(document)
390}
391
392pub fn validate_with_schema_location_and_fetcher<F: schema::SchemaFetcher>(
398 document: &XmlDocument,
399 fetcher: &F,
400) -> Result<Vec<StructuredError>> {
401 schema::validate_with_schema_location_and_fetcher(document, fetcher)
402}
403
404#[cfg(feature = "ureq")]
426pub fn get_schema_from_schema_location(
427 xml_content: &[u8],
428) -> Result<schema::types::CompiledSchema> {
429 schema::get_schema_from_schema_location(xml_content)
430}
431
432pub fn get_schema_from_schema_location_with_fetcher<F: schema::SchemaFetcher>(
434 xml_content: &[u8],
435 fetcher: &F,
436) -> Result<schema::types::CompiledSchema> {
437 schema::get_schema_from_schema_location_with_fetcher(xml_content, fetcher)
438}
439
440#[cfg(feature = "ureq")]
458pub fn streaming_validate_with_schema_location<R: std::io::BufRead>(
459 reader: R,
460) -> Result<Vec<StructuredError>> {
461 schema::streaming_validate_with_schema_location(reader)
462}
463
464pub fn streaming_validate_with_schema_location_and_fetcher<
466 R: std::io::BufRead,
467 F: schema::SchemaFetcher + 'static,
468>(
469 reader: R,
470 fetcher: F,
471) -> Result<Vec<StructuredError>> {
472 schema::streaming_validate_with_schema_location_and_fetcher(reader, fetcher)
473}
474
475#[cfg(feature = "tokio")]
494pub async fn validate_with_schema_location_async(
495 document: &XmlDocument,
496) -> Result<Vec<StructuredError>> {
497 schema::validate_with_schema_location_async(document).await
498}
499
500#[cfg(feature = "tokio")]
505pub async fn validate_with_schema_location_with_async_fetcher<
506 F: schema::AsyncSchemaFetcher,
507 S: schema::AsyncSchemaStore,
508>(
509 document: &XmlDocument,
510 fetcher: &F,
511 store: &S,
512) -> Result<Vec<StructuredError>> {
513 schema::validate_with_schema_location_with_async_fetcher(document, fetcher, store).await
514}
515
516#[cfg(feature = "tokio")]
532pub async fn get_schema_from_schema_location_async(
533 xml_content: &[u8],
534) -> Result<schema::types::CompiledSchema> {
535 schema::get_schema_from_schema_location_async(xml_content).await
536}
537
538#[cfg(feature = "tokio")]
543pub async fn get_schema_from_schema_location_with_async_fetcher<
544 F: schema::AsyncSchemaFetcher,
545 S: schema::AsyncSchemaStore,
546>(
547 xml_content: &[u8],
548 fetcher: &F,
549 store: &S,
550) -> Result<schema::types::CompiledSchema> {
551 schema::get_schema_from_schema_location_with_async_fetcher(xml_content, fetcher, store).await
552}
553
554#[cfg(test)]
555mod tests {
556 use super::*;
557
558 #[test]
559 fn test_basic_parsing() {
560 let xml = r#"<root attr="value"><child>text</child></root>"#;
561 let doc = parse(xml).unwrap();
562
563 let root = get_root_node(&doc).unwrap();
564 assert_eq!(get_node_tag(&root), "root");
565 }
566
567 #[test]
568 fn test_xpath_evaluation() {
569 let xml = r#"<root><Building/><Room/><Window/></root>"#;
570 let doc = parse(xml).unwrap();
571
572 let result = evaluate(&doc, "//*[name()='Building']").unwrap();
573 let nodes = result.into_nodes();
574 assert_eq!(nodes.len(), 1);
575 assert_eq!(nodes[0].get_name(), "Building");
576 }
577
578 #[test]
579 fn test_context_xpath() {
580 let xml = r#"<root><a>1</a><a>2</a></root>"#;
581 let doc = parse(xml).unwrap();
582 let ctx = create_context(&doc).unwrap();
583 let root = get_root_readonly_node(&doc).unwrap();
584
585 let nodes = find_readonly_nodes_by_xpath(&ctx, "//a", &root).unwrap();
586 assert_eq!(nodes.len(), 2);
587 }
588
589 #[test]
590 fn test_text_collection() {
591 let xml = r#"<root><a>one</a><a>two</a></root>"#;
592 let doc = parse(xml).unwrap();
593
594 let result = evaluate(&doc, "/root/a").unwrap();
595 let texts = collect_text_values(&result);
596 assert_eq!(texts, vec!["one", "two"]);
597 }
598
599 #[test]
600 fn test_namespaced_xml() {
601 let xml = r#"<gml:root xmlns:gml="http://www.opengis.net/gml">
602 <gml:name>test</gml:name>
603 </gml:root>"#;
604 let doc = parse(xml).unwrap();
605
606 let result = evaluate(&doc, "/gml:root/gml:name").unwrap();
607 let nodes = result.into_nodes();
608 assert_eq!(nodes.len(), 1);
609 }
610
611 #[test]
612 fn test_serialization() {
613 let xml = r#"<root><child>text</child></root>"#;
614 let doc = parse(xml).unwrap();
615 let mut root = get_root_node(&doc).unwrap();
616
617 let serialized = node_to_xml_string(&doc, &mut root).unwrap();
618 assert!(serialized.contains("<root>"));
619 assert!(serialized.contains("<child>text</child>"));
620 }
621
622 #[test]
623 fn test_create_safe_context() {
624 let xml = r#"<root><a>1</a></root>"#;
625 let doc = parse(xml).unwrap();
626 let ctx = create_safe_context(&doc).unwrap();
627 let root = get_root_readonly_node(&doc).unwrap();
628
629 let nodes = find_safe_readonly_nodes_by_xpath(&ctx, "//a", &root).unwrap();
630 assert_eq!(nodes.len(), 1);
631 }
632
633 #[test]
634 fn test_find_nodes_by_xpath() {
635 let xml = r#"<root><a>1</a><b>2</b></root>"#;
636 let doc = parse(xml).unwrap();
637 let ctx = create_context(&doc).unwrap();
638 let root = get_root_node(&doc).unwrap();
639
640 let nodes = find_nodes_by_xpath(&ctx, "//a", &root).unwrap();
641 assert_eq!(nodes.len(), 1);
642 assert_eq!(nodes[0].get_name(), "a");
643 }
644
645 #[test]
646 fn test_find_readonly_nodes_in_elements() {
647 let xml = r#"<root><a>1</a><b>2</b><c>3</c></root>"#;
648 let doc = parse(xml).unwrap();
649 let ctx = create_context(&doc).unwrap();
650 let root = get_root_readonly_node(&doc).unwrap();
651
652 let nodes = find_readonly_nodes_in_elements(&ctx, &root, &["a", "c"]).unwrap();
653 assert_eq!(nodes.len(), 2);
654 }
655
656 #[test]
657 fn test_collect_text_value_single() {
658 let xml = r#"<root><a>hello</a></root>"#;
659 let doc = parse(xml).unwrap();
660
661 let result = evaluate(&doc, "/root/a").unwrap();
662 let text = collect_text_value(&result);
663 assert_eq!(text, "hello");
664 }
665
666 #[test]
667 fn test_collect_text_value_empty() {
668 let xml = r#"<root><a/></root>"#;
669 let doc = parse(xml).unwrap();
670
671 let result = evaluate(&doc, "/root/nonexistent").unwrap();
672 let text = collect_text_value(&result);
673 assert!(text.is_empty());
674 }
675
676 #[test]
677 fn test_get_readonly_node_tag() {
678 let xml = r#"<ns:root xmlns:ns="http://example.com"/>"#;
679 let doc = parse(xml).unwrap();
680 let root = get_root_readonly_node(&doc).unwrap();
681
682 assert_eq!(get_readonly_node_tag(&root), "ns:root");
683 }
684
685 #[test]
686 fn test_get_node_prefix() {
687 let xml = r#"<ns:root xmlns:ns="http://example.com"/>"#;
688 let doc = parse(xml).unwrap();
689 let root = get_root_node(&doc).unwrap();
690
691 assert_eq!(get_node_prefix(&root), "ns");
692 }
693
694 #[test]
695 fn test_get_node_prefix_empty() {
696 let xml = r#"<root/>"#;
697 let doc = parse(xml).unwrap();
698 let root = get_root_node(&doc).unwrap();
699
700 assert_eq!(get_node_prefix(&root), "");
701 }
702
703 #[test]
704 fn test_get_readonly_node_prefix() {
705 let xml = r#"<ns:root xmlns:ns="http://example.com"/>"#;
706 let doc = parse(xml).unwrap();
707 let root = get_root_readonly_node(&doc).unwrap();
708
709 assert_eq!(get_readonly_node_prefix(&root), "ns");
710 }
711
712 #[test]
713 fn test_get_readonly_node_prefix_empty() {
714 let xml = r#"<root/>"#;
715 let doc = parse(xml).unwrap();
716 let root = get_root_readonly_node(&doc).unwrap();
717
718 assert_eq!(get_readonly_node_prefix(&root), "");
719 }
720
721 #[test]
722 fn test_readonly_node_to_xml_string() {
723 let xml = r#"<root><child>text</child></root>"#;
724 let doc = parse(xml).unwrap();
725 let root = get_root_readonly_node(&doc).unwrap();
726
727 let serialized = readonly_node_to_xml_string(&doc, &root).unwrap();
728 assert!(serialized.contains("<root>"));
729 assert!(serialized.contains("<child>text</child>"));
730 }
731
732 #[test]
733 fn test_evaluate_with_string_ref() {
734 let xml = r#"<root><a>1</a></root>"#;
735 let doc = parse(xml).unwrap();
736 let xpath_str = String::from("//a");
737
738 let result = evaluate(&doc, &xpath_str).unwrap();
739 let nodes = result.into_nodes();
740 assert_eq!(nodes.len(), 1);
741 }
742
743 #[test]
744 fn test_parse_xsd_basic() {
745 let xsd = br#"<?xml version="1.0"?>
746<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
747 <xs:element name="root" type="xs:string"/>
748</xs:schema>"#;
749
750 let schema = parse_xsd(xsd).unwrap();
751 assert!(!schema.elements.is_empty() || !schema.types.is_empty());
752 }
753
754 #[test]
755 fn test_create_xml_schema_validation_context_from_buffer() {
756 let xsd = br#"<?xml version="1.0"?>
757<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
758 <xs:element name="root" type="xs:string"/>
759</xs:schema>"#;
760
761 let ctx = create_xml_schema_validation_context_from_buffer(xsd).unwrap();
762 let _ = ctx;
764 }
765}