1use std::ops::Range;
41
42use super::index::XPathNodeKind;
43use super::NodeId;
44
45pub const RTF_BASE: NodeId = 1_usize << (usize::BITS - 2);
50
51const RTF_LOCAL_BITS: u32 = 32;
55const RTF_LOCAL_MASK: NodeId = (1_usize << RTF_LOCAL_BITS) - 1;
56const RTF_INDEX_MASK: NodeId = !(RTF_BASE | super::context::SYNTHETIC_TEXT_BASE | RTF_LOCAL_MASK);
60
61#[inline(always)]
63pub fn is_rtf_id(id: NodeId) -> bool {
64 id & RTF_BASE != 0 && id & super::context::SYNTHETIC_TEXT_BASE == 0
65}
66
67#[inline(always)]
69pub fn decode_rtf_id(id: NodeId) -> (usize, NodeId) {
70 let local = id & RTF_LOCAL_MASK;
71 let rtf_i = (id & RTF_INDEX_MASK) >> RTF_LOCAL_BITS;
72 (rtf_i, local)
73}
74
75#[inline(always)]
79pub fn encode_rtf_id(rtf_i: usize, local: NodeId) -> NodeId {
80 debug_assert!(local <= RTF_LOCAL_MASK,
81 "RTF local id {local} exceeds 32-bit budget");
82 debug_assert!(rtf_i <= (RTF_INDEX_MASK >> RTF_LOCAL_BITS),
83 "RTF index {rtf_i} exceeds 30-bit budget");
84 RTF_BASE | (rtf_i << RTF_LOCAL_BITS) | local
85}
86
87#[derive(Debug)]
101pub struct RtfNode {
102 pub kind: RtfNodeKind,
103 pub parent: Option<NodeId>,
104 pub children: Vec<NodeId>,
105 pub attr_start: NodeId,
106 pub attr_end: NodeId,
107 pub ns_start: NodeId,
108 pub ns_end: NodeId,
109}
110
111#[derive(Debug)]
112pub enum RtfNodeKind {
113 Document,
114 Element {
115 name: Box<str>,
116 local_name: Box<str>,
117 prefix: Option<Box<str>>,
118 namespace_uri: Box<str>,
119 },
120 Attribute {
121 name: Box<str>,
122 local_name: Box<str>,
123 prefix: Option<Box<str>>,
124 namespace_uri: Box<str>,
125 value: Box<str>,
126 },
127 Text(Box<str>),
128 Comment(Box<str>),
129 PI { target: Box<str>, data: Box<str> },
130 Namespace { prefix: Option<Box<str>>, uri: Box<str> },
131}
132
133#[derive(Debug)]
137pub struct RtfIndex {
138 pub nodes: Vec<RtfNode>,
144 pub host_index: usize,
148}
149
150impl RtfIndex {
151 pub fn children(&self, local_id: NodeId) -> &[NodeId] {
154 &self.nodes[local_id].children
155 }
156
157 pub fn parent(&self, local_id: NodeId) -> Option<NodeId> {
160 self.nodes[local_id].parent
161 }
162
163 pub fn attr_range(&self, local_id: NodeId) -> Range<NodeId> {
164 let n = &self.nodes[local_id];
165 n.attr_start..n.attr_end
166 }
167
168 pub fn ns_range(&self, local_id: NodeId) -> Range<NodeId> {
169 let n = &self.nodes[local_id];
170 n.ns_start..n.ns_end
171 }
172
173 pub fn kind(&self, local_id: NodeId) -> XPathNodeKind {
174 match self.nodes[local_id].kind {
175 RtfNodeKind::Document => XPathNodeKind::Document,
176 RtfNodeKind::Element { .. } => XPathNodeKind::Element,
177 RtfNodeKind::Attribute { .. } => XPathNodeKind::Attribute,
178 RtfNodeKind::Text(_) => XPathNodeKind::Text,
179 RtfNodeKind::Comment(_) => XPathNodeKind::Comment,
180 RtfNodeKind::PI { .. } => XPathNodeKind::PI,
181 RtfNodeKind::Namespace { .. } => XPathNodeKind::Namespace,
182 }
183 }
184
185 pub fn pi_target(&self, local_id: NodeId) -> &str {
186 match &self.nodes[local_id].kind {
187 RtfNodeKind::PI { target, .. } => target,
188 _ => "",
189 }
190 }
191
192 pub fn node_name(&self, local_id: NodeId) -> &str {
193 match &self.nodes[local_id].kind {
194 RtfNodeKind::Element { name, .. }
195 | RtfNodeKind::Attribute { name, .. } => name,
196 RtfNodeKind::PI { target, .. } => target,
197 RtfNodeKind::Namespace { prefix, .. } =>
201 prefix.as_deref().unwrap_or(""),
202 _ => "",
203 }
204 }
205
206 pub fn local_name(&self, local_id: NodeId) -> &str {
207 match &self.nodes[local_id].kind {
208 RtfNodeKind::Element { local_name, .. }
209 | RtfNodeKind::Attribute { local_name, .. } => local_name,
210 RtfNodeKind::PI { target, .. } => target,
211 RtfNodeKind::Namespace { prefix, .. } =>
212 prefix.as_deref().unwrap_or(""),
213 _ => "",
214 }
215 }
216
217 pub fn namespace_uri(&self, local_id: NodeId) -> &str {
218 match &self.nodes[local_id].kind {
219 RtfNodeKind::Element { namespace_uri, .. }
220 | RtfNodeKind::Attribute { namespace_uri, .. } => namespace_uri,
221 _ => "",
222 }
223 }
224
225 pub fn namespace_prefix(&self, local_id: NodeId) -> Option<&str> {
226 match &self.nodes[local_id].kind {
227 RtfNodeKind::Element { prefix, .. }
228 | RtfNodeKind::Attribute { prefix, .. } => prefix.as_deref(),
229 _ => None,
230 }
231 }
232
233 pub fn string_value(&self, local_id: NodeId) -> String {
237 match &self.nodes[local_id].kind {
238 RtfNodeKind::Text(s) => s.to_string(),
239 RtfNodeKind::Comment(s) => s.to_string(),
240 RtfNodeKind::PI { data, .. } => data.to_string(),
241 RtfNodeKind::Attribute { value, .. } => value.to_string(),
242 RtfNodeKind::Namespace { uri, .. } => uri.to_string(),
243 RtfNodeKind::Element { .. } | RtfNodeKind::Document => {
244 let mut out = String::new();
245 self.append_text(local_id, &mut out);
246 out
247 }
248 }
249 }
250
251 fn append_text(&self, local_id: NodeId, out: &mut String) {
252 for &child in &self.nodes[local_id].children {
257 let child_local = child & RTF_LOCAL_MASK;
258 match &self.nodes[child_local].kind {
259 RtfNodeKind::Text(s) => out.push_str(s),
260 RtfNodeKind::Element { .. } | RtfNodeKind::Document =>
261 self.append_text(child_local, out),
262 _ => {}
263 }
264 }
265 }
266}
267
268
269pub struct RtfBuilder {
286 pub(crate) host_index: usize,
290 pub(crate) nodes: Vec<RtfNode>,
291 pub typed_nodes: Vec<(NodeId, Box<(String, String)>)>,
297}
298
299impl RtfBuilder {
300 #[allow(dead_code)] pub(crate) fn new(host_index: usize) -> Self {
302 Self { host_index, nodes: Vec::new(), typed_nodes: Vec::new() }
303 }
304
305 #[inline]
306 fn glob(&self, local: NodeId) -> NodeId {
307 encode_rtf_id(self.host_index, local)
308 }
309
310 fn push(&mut self, kind: RtfNodeKind, parent: Option<NodeId>) -> NodeId {
311 let local_id = self.nodes.len();
312 self.nodes.push(RtfNode {
313 kind,
314 parent,
315 children: Vec::new(),
316 attr_start: 0, attr_end: 0,
317 ns_start: 0, ns_end: 0,
318 });
319 self.glob(local_id)
320 }
321
322 fn local_of(global: NodeId) -> NodeId {
323 global & RTF_LOCAL_MASK
324 }
325
326 pub fn add_document(&mut self) -> NodeId {
330 assert!(self.nodes.is_empty(),
331 "RtfBuilder::add_document must be the first call");
332 self.push(RtfNodeKind::Document, None)
333 }
334
335 pub fn add_element(
339 &mut self, parent: NodeId,
340 qname: &str, namespace_uri: &str, prefix: Option<&str>,
341 ) -> NodeId {
342 let (local_name, _) = qname.rsplit_once(':')
343 .map(|(_, l)| (l, true))
344 .unwrap_or((qname, false));
345 let id = self.push(RtfNodeKind::Element {
346 name: qname.into(),
347 local_name: local_name.into(),
348 prefix: prefix.map(Into::into),
349 namespace_uri: namespace_uri.into(),
350 }, Some(parent));
351 let parent_local = Self::local_of(parent);
352 self.nodes[parent_local].children.push(id);
353 id
354 }
355
356 pub fn add_text(&mut self, parent: NodeId, content: &str) -> NodeId {
357 let id = self.push(RtfNodeKind::Text(content.into()), Some(parent));
358 let parent_local = Self::local_of(parent);
359 self.nodes[parent_local].children.push(id);
360 id
361 }
362
363 pub fn add_comment(&mut self, parent: NodeId, content: &str) -> NodeId {
364 let id = self.push(RtfNodeKind::Comment(content.into()), Some(parent));
365 let parent_local = Self::local_of(parent);
366 self.nodes[parent_local].children.push(id);
367 id
368 }
369
370 pub fn add_pi(&mut self, parent: NodeId, target: &str, data: &str) -> NodeId {
371 let id = self.push(RtfNodeKind::PI {
372 target: target.into(), data: data.into(),
373 }, Some(parent));
374 let parent_local = Self::local_of(parent);
375 self.nodes[parent_local].children.push(id);
376 id
377 }
378
379 pub fn start_attrs(&mut self, elem_global: NodeId) {
385 let elem_local = Self::local_of(elem_global);
386 let start_local = self.nodes.len();
387 self.nodes[elem_local].attr_start = self.glob(start_local);
388 self.nodes[elem_local].attr_end = self.glob(start_local);
389 }
390
391 pub fn add_attribute(
392 &mut self, elem_global: NodeId,
393 qname: &str, namespace_uri: &str, prefix: Option<&str>,
394 value: &str,
395 ) -> NodeId {
396 let (local_name, _) = qname.rsplit_once(':')
397 .map(|(_, l)| (l, true))
398 .unwrap_or((qname, false));
399 let id = self.push(RtfNodeKind::Attribute {
400 name: qname.into(),
401 local_name: local_name.into(),
402 prefix: prefix.map(Into::into),
403 namespace_uri: namespace_uri.into(),
404 value: value.into(),
405 }, Some(elem_global));
406 let elem_local = Self::local_of(elem_global);
407 let new_end_local = Self::local_of(id) + 1;
409 self.nodes[elem_local].attr_end = self.glob(new_end_local);
410 id
411 }
412
413 pub fn start_ns(&mut self, elem_global: NodeId) {
420 let elem_local = Self::local_of(elem_global);
421 let start_local = self.nodes.len();
422 self.nodes[elem_local].ns_start = self.glob(start_local);
423 self.nodes[elem_local].ns_end = self.glob(start_local);
424 }
425
426 pub fn add_namespace_node(
433 &mut self, elem_global: NodeId,
434 prefix: Option<&str>, uri: &str,
435 ) -> NodeId {
436 let prefix = prefix.filter(|p| !p.is_empty());
437 let id = self.push(
438 RtfNodeKind::Namespace {
439 prefix: prefix.map(Into::into),
440 uri: uri.into(),
441 },
442 Some(elem_global),
443 );
444 let elem_local = Self::local_of(elem_global);
445 let new_end_local = Self::local_of(id) + 1;
446 self.nodes[elem_local].ns_end = self.glob(new_end_local);
447 id
448 }
449
450 pub fn build(self) -> RtfIndex {
452 RtfIndex { nodes: self.nodes, host_index: self.host_index }
453 }
454}
455
456#[cfg(test)]
457mod tests {
458 use super::*;
459
460 #[test]
461 fn rtf_id_round_trips() {
462 let id = encode_rtf_id(7, 42);
463 assert!(is_rtf_id(id));
464 assert_eq!(decode_rtf_id(id), (7, 42));
465 }
466
467 #[test]
468 fn rtf_marker_is_disjoint_from_synthetic() {
469 let s = super::super::context::SYNTHETIC_TEXT_BASE;
470 let r = encode_rtf_id(0, 0);
471 assert!(!is_rtf_id(s),
472 "synthetic ids must not look like RTF ids");
473 assert!(r & s == 0,
474 "RTF and synthetic markers must be distinct bits");
475 }
476}