rusty_xml_parser/
catalog.rs1use rusty_xml_tree::{NodeKind, XmlDoc};
4use std::collections::HashMap;
5use std::path::{Path, PathBuf};
6
7const CAT_NS: &str = "urn:oasis:names:tc:entity:xmlns:xml:catalog";
8
9#[derive(Clone, Debug, Default)]
10pub struct XmlCatalog {
11 pub public: HashMap<String, String>,
12 pub system: HashMap<String, String>,
13 pub uri: HashMap<String, String>,
14 pub rewrite_system: Vec<(String, String)>,
15 pub rewrite_uri: Vec<(String, String)>,
16 pub next_catalog: Vec<PathBuf>,
17}
18
19impl XmlCatalog {
20 #[doc(alias = "xmlLoadCatalog")]
22 pub fn xml_load_catalog(path: &Path) -> Result<Self, String> {
23 let bytes = std::fs::read(path).map_err(|e| e.to_string())?;
24 let doc = crate::xml_read_memory(&bytes, path.to_str(), None, crate::default_parse_options())
25 .map_err(|e| e.to_string())?;
26 let mut cat = XmlCatalog::default();
27 walk(&doc, doc.xml_doc_get_root_element(), &mut cat, path.parent().unwrap_or(path));
28 Ok(cat)
29 }
30
31 #[doc(alias = "xmlCatalogResolve")]
33 pub fn xml_catalog_resolve(&self, public_id: Option<&str>, system_id: Option<&str>) -> Option<String> {
34 if let Some(p) = public_id {
35 if let Some(u) = self.public.get(p) {
36 return Some(u.clone());
37 }
38 }
39 if let Some(s) = system_id {
40 if let Some(u) = self.system.get(s) {
41 return Some(u.clone());
42 }
43 for (prefix, replace) in &self.rewrite_system {
44 if s.starts_with(prefix) {
45 return Some(format!("{}{}", replace, &s[prefix.len()..]));
46 }
47 }
48 }
49 None
50 }
51
52 #[doc(alias = "xmlCatalogResolveURI")]
54 pub fn xml_catalog_resolve_uri(&self, uri: &str) -> Option<String> {
55 if let Some(u) = self.uri.get(uri) {
56 return Some(u.clone());
57 }
58 for (prefix, replace) in &self.rewrite_uri {
59 if uri.starts_with(prefix) {
60 return Some(format!("{}{}", replace, &uri[prefix.len()..]));
61 }
62 }
63 None
64 }
65}
66
67fn walk(doc: &XmlDoc, node: Option<rusty_xml_tree::NodeId>, cat: &mut XmlCatalog, base: &Path) {
68 let Some(id) = node else { return };
69 if doc.kind(id) == NodeKind::Element {
70 let name = doc.name(id);
71 let ns = doc.ns_uri(id);
72 let in_cat = ns == Some(CAT_NS) || ns.is_none();
73 if in_cat {
74 match name {
75 "public" => {
76 if let (Some(idv), Some(uri)) = (doc.xml_get_prop(id, "publicId"), doc.xml_get_prop(id, "uri")) {
77 cat.public.insert(idv, resolve_uri(base, &uri));
78 }
79 }
80 "system" => {
81 if let (Some(idv), Some(uri)) = (doc.xml_get_prop(id, "systemId"), doc.xml_get_prop(id, "uri")) {
82 cat.system.insert(idv, resolve_uri(base, &uri));
83 }
84 }
85 "uri" => {
86 if let (Some(name), Some(uri)) = (doc.xml_get_prop(id, "name"), doc.xml_get_prop(id, "uri")) {
87 cat.uri.insert(name, resolve_uri(base, &uri));
88 }
89 }
90 "rewriteSystem" => {
91 if let (Some(p), Some(r)) = (
92 doc.xml_get_prop(id, "systemIdStartString"),
93 doc.xml_get_prop(id, "rewritePrefix"),
94 ) {
95 cat.rewrite_system.push((p, resolve_uri(base, &r)));
96 }
97 }
98 "rewriteURI" => {
99 if let (Some(p), Some(r)) = (
100 doc.xml_get_prop(id, "uriStartString"),
101 doc.xml_get_prop(id, "rewritePrefix"),
102 ) {
103 cat.rewrite_uri.push((p, resolve_uri(base, &r)));
104 }
105 }
106 "nextCatalog" => {
107 if let Some(c) = doc.xml_get_prop(id, "catalog") {
108 cat.next_catalog.push(base.join(c));
109 }
110 }
111 _ => {}
112 }
113 }
114 let mut ch = doc.first_child(id);
115 while let Some(c) = ch {
116 walk(doc, Some(c), cat, base);
117 ch = doc.next_sibling(c);
118 }
119 }
120}
121
122fn resolve_uri(base: &Path, uri: &str) -> String {
123 if uri.contains("://") {
124 uri.to_string()
125 } else {
126 base.join(uri).to_string_lossy().into_owned()
127 }
128}
129
130#[doc(alias = "xmlInitializeCatalog")]
132pub fn xml_initialize_catalog() {}
133
134#[doc(alias = "xmlCatalogCleanup")]
136pub fn xml_catalog_cleanup() {}