1use super::list::inspect_two_group_item;
2use super::*;
3
4#[derive(Debug, Clone, Copy, PartialEq)]
10pub struct RdSection<'a> {
11 pub title: &'a [RdNode],
14 pub body: &'a [RdNode],
17}
18
19#[derive(Debug, Clone, Copy, PartialEq)]
22pub struct RdArgument<'a> {
23 pub name: &'a [RdNode],
26 pub description: &'a [RdNode],
29}
30
31#[derive(Debug, Clone, Copy, PartialEq)]
33pub struct RdAlias<'a> {
34 nodes: &'a [RdNode],
35}
36
37#[derive(Debug, Clone, Copy, PartialEq)]
39pub struct RdKeyword<'a> {
40 nodes: &'a [RdNode],
41}
42
43impl<'a> RdKeyword<'a> {
44 pub fn nodes(&self) -> &'a [RdNode] {
45 self.nodes
46 }
47 pub fn text_contents(&self) -> String {
48 text_contents(self.nodes)
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq)]
54pub struct RdConcept<'a> {
55 nodes: &'a [RdNode],
56}
57
58impl<'a> RdConcept<'a> {
59 pub fn nodes(&self) -> &'a [RdNode] {
60 self.nodes
61 }
62 pub fn text_contents(&self) -> String {
63 text_contents(self.nodes)
64 }
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69#[non_exhaustive]
70pub enum RdSectionKind {
71 Section,
72 Subsection,
73}
74
75#[derive(Debug, Clone, PartialEq)]
77pub struct RdSectionVisit<'a> {
78 path: RdPath,
79 kind: RdSectionKind,
80 nesting: usize,
81 title: &'a [RdNode],
82 body: &'a [RdNode],
83}
84
85impl<'a> RdSectionVisit<'a> {
86 pub fn path(&self) -> &RdPath {
87 &self.path
88 }
89 pub fn kind(&self) -> RdSectionKind {
90 self.kind
91 }
92 pub fn nesting(&self) -> usize {
95 self.nesting
96 }
97 pub fn title(&self) -> &'a [RdNode] {
98 self.title
99 }
100 pub fn body(&self) -> &'a [RdNode] {
101 self.body
102 }
103}
104
105impl<'a> RdAlias<'a> {
106 pub fn nodes(&self) -> &'a [RdNode] {
107 self.nodes
108 }
109 pub fn text_contents(&self) -> String {
110 text_contents(self.nodes)
111 }
112}
113impl RdDocument {
114 pub fn title(&self) -> Option<&[RdNode]> {
123 self.first_tagged_children(RdTag::Title)
124 }
125
126 pub fn description(&self) -> Option<&[RdNode]> {
132 self.first_tagged_children(RdTag::Description)
133 }
134
135 pub fn usage(&self) -> Option<&[RdNode]> {
141 self.first_tagged_children(RdTag::Usage)
142 }
143
144 pub fn value(&self) -> Option<&[RdNode]> {
150 self.first_tagged_children(RdTag::Value)
151 }
152
153 pub fn name(&self) -> Option<&[RdNode]> {
156 self.first_tagged_children(RdTag::Name)
157 }
158 pub fn details(&self) -> Option<&[RdNode]> {
161 self.first_tagged_children(RdTag::Details)
162 }
163 pub fn note(&self) -> Option<&[RdNode]> {
166 self.first_tagged_children(RdTag::Note)
167 }
168 pub fn author(&self) -> Option<&[RdNode]> {
171 self.first_tagged_children(RdTag::Author)
172 }
173 pub fn references(&self) -> Option<&[RdNode]> {
176 self.first_tagged_children(RdTag::References)
177 }
178 pub fn see_also(&self) -> Option<&[RdNode]> {
181 self.first_tagged_children(RdTag::SeeAlso)
182 }
183 pub fn examples(&self) -> Option<&[RdNode]> {
186 self.first_tagged_children(RdTag::Examples)
187 }
188 pub fn format(&self) -> Option<&[RdNode]> {
191 self.first_tagged_children(RdTag::Format)
192 }
193 pub fn source(&self) -> Option<&[RdNode]> {
196 self.first_tagged_children(RdTag::Source)
197 }
198 pub fn encoding(&self) -> Option<&[RdNode]> {
201 self.first_tagged_children(RdTag::Encoding)
202 }
203 pub fn doc_type(&self) -> Option<&[RdNode]> {
206 self.first_tagged_children(RdTag::DocType)
207 }
208 pub fn rd_version(&self) -> Option<&[RdNode]> {
211 self.first_tagged_children(RdTag::Rdversion)
212 }
213 pub fn synopsis(&self) -> Option<&[RdNode]> {
216 self.first_tagged_children(RdTag::Synopsis)
217 }
218
219 fn first_tagged_children(&self, tag: RdTag) -> Option<&[RdNode]> {
222 self.nodes().iter().find_map(|node| {
223 let tagged = node.as_tagged()?;
224 (tagged.tag() == &tag).then(|| tagged.children())
225 })
226 }
227
228 pub fn aliases(&self) -> impl Iterator<Item = String> + '_ {
242 self.nodes().iter().filter_map(|node| {
243 let tagged = node.as_tagged()?;
244 (tagged.tag() == &RdTag::Alias).then(|| text_contents(tagged.children()))
245 })
246 }
247
248 pub fn keywords(&self) -> impl Iterator<Item = String> + '_ {
250 self.nodes().iter().filter_map(|node| {
251 let tagged = node.as_tagged()?;
252 (tagged.tag() == &RdTag::Keyword).then(|| text_contents(tagged.children()))
253 })
254 }
255
256 pub fn concepts(&self) -> impl Iterator<Item = String> + '_ {
258 self.nodes().iter().filter_map(|node| {
259 let tagged = node.as_tagged()?;
260 (tagged.tag() == &RdTag::Concept).then(|| text_contents(tagged.children()))
261 })
262 }
263
264 pub fn sections(&self) -> impl Iterator<Item = RdSection<'_>> {
280 self.nodes().iter().filter_map(|node| {
281 let tagged = node.as_tagged()?;
282 if tagged.tag() != &RdTag::Section || tagged.option().is_some() {
283 return None;
284 }
285 let [RdNode::Group(title), RdNode::Group(body)] = tagged.children() else {
286 return None;
287 };
288 Some(RdSection {
289 title: title.children(),
290 body: body.children(),
291 })
292 })
293 }
294
295 pub fn arguments(&self) -> impl Iterator<Item = RdArgument<'_>> {
321 let children = self.first_tagged_children(RdTag::Arguments).unwrap_or(&[]);
322 children.iter().filter_map(|node| {
323 let tagged = node.as_tagged()?;
324 if tagged.tag() != &RdTag::Item || tagged.option().is_some() {
325 return None;
326 }
327 let [RdNode::Group(name), RdNode::Group(description)] = tagged.children() else {
328 return None;
329 };
330 Some(RdArgument {
331 name: name.children(),
332 description: description.children(),
333 })
334 })
335 }
336
337 pub fn inspect_title(&self) -> Result<Option<&[RdNode]>, RdShapeError> {
339 self.inspect_fixed(RdTag::Title)
340 }
341 pub fn inspect_description(&self) -> Result<Option<&[RdNode]>, RdShapeError> {
343 self.inspect_fixed(RdTag::Description)
344 }
345 pub fn inspect_usage(&self) -> Result<Option<&[RdNode]>, RdShapeError> {
347 self.inspect_fixed(RdTag::Usage)
348 }
349 pub fn inspect_value(&self) -> Result<Option<&[RdNode]>, RdShapeError> {
351 self.inspect_fixed(RdTag::Value)
352 }
353
354 pub fn inspect_name(&self) -> Result<Option<&[RdNode]>, RdShapeError> {
356 self.inspect_fixed(RdTag::Name)
357 }
358 pub fn inspect_details(&self) -> Result<Option<&[RdNode]>, RdShapeError> {
360 self.inspect_fixed(RdTag::Details)
361 }
362 pub fn inspect_note(&self) -> Result<Option<&[RdNode]>, RdShapeError> {
364 self.inspect_fixed(RdTag::Note)
365 }
366 pub fn inspect_author(&self) -> Result<Option<&[RdNode]>, RdShapeError> {
368 self.inspect_fixed(RdTag::Author)
369 }
370 pub fn inspect_references(&self) -> Result<Option<&[RdNode]>, RdShapeError> {
372 self.inspect_fixed(RdTag::References)
373 }
374 pub fn inspect_see_also(&self) -> Result<Option<&[RdNode]>, RdShapeError> {
376 self.inspect_fixed(RdTag::SeeAlso)
377 }
378 pub fn inspect_examples(&self) -> Result<Option<&[RdNode]>, RdShapeError> {
380 self.inspect_fixed(RdTag::Examples)
381 }
382 pub fn inspect_format(&self) -> Result<Option<&[RdNode]>, RdShapeError> {
384 self.inspect_fixed(RdTag::Format)
385 }
386 pub fn inspect_source(&self) -> Result<Option<&[RdNode]>, RdShapeError> {
388 self.inspect_fixed(RdTag::Source)
389 }
390 pub fn inspect_encoding(&self) -> Result<Option<&[RdNode]>, RdShapeError> {
392 self.inspect_fixed(RdTag::Encoding)
393 }
394 pub fn inspect_doc_type(&self) -> Result<Option<&[RdNode]>, RdShapeError> {
396 self.inspect_fixed(RdTag::DocType)
397 }
398 pub fn inspect_rd_version(&self) -> Result<Option<&[RdNode]>, RdShapeError> {
400 self.inspect_fixed(RdTag::Rdversion)
401 }
402 pub fn inspect_synopsis(&self) -> Result<Option<&[RdNode]>, RdShapeError> {
404 self.inspect_fixed(RdTag::Synopsis)
405 }
406
407 fn inspect_fixed(&self, wanted: RdTag) -> Result<Option<&[RdNode]>, RdShapeError> {
408 let mut found: Option<(usize, &[RdNode])> = None;
409 for (index, node) in self.nodes().iter().enumerate() {
410 let path = top_path(index);
411 if let Some(raw) = node.as_raw() {
412 if raw.tag() == Some(wanted.as_rd_tag()) {
413 return Err(shape(
414 path,
415 Some(wanted.clone()),
416 RdShapeErrorKind::UnexpectedNode {
417 expected: RdExpectedNode::Tagged,
418 actual: RdNodeKind::Raw,
419 },
420 ));
421 }
422 continue;
423 }
424 let Some(tagged) = node.as_tagged() else {
425 continue;
426 };
427 if tagged.tag() != &wanted {
428 continue;
429 }
430 if let Some((first_index, _)) = found {
431 let first_path = top_path(first_index);
432 return Err(shape(
433 path,
434 Some(wanted.clone()),
435 RdShapeErrorKind::Duplicate {
436 construct: RdConstruct::Tag(wanted.clone()),
437 first_path,
438 },
439 ));
440 }
441 if tagged.option().is_some() {
442 return Err(shape(
443 path,
444 Some(wanted.clone()),
445 RdShapeErrorKind::UnexpectedOption,
446 ));
447 }
448 found = Some((index, tagged.children()));
449 }
450 Ok(found.map(|(_, children)| children))
451 }
452
453 pub fn inspect_aliases(&self) -> impl Iterator<Item = Result<RdAlias<'_>, RdShapeError>> + '_ {
455 self.nodes().iter().enumerate().filter_map(|(index, node)| {
456 let path = top_path(index);
457 if node
458 .as_raw()
459 .is_some_and(|raw| raw.tag() == Some(RdTag::Alias.as_rd_tag()))
460 {
461 return Some(Err(shape(
462 path,
463 Some(RdTag::Alias),
464 RdShapeErrorKind::UnexpectedNode {
465 expected: RdExpectedNode::Tagged,
466 actual: RdNodeKind::Raw,
467 },
468 )));
469 }
470 let tagged = node.as_tagged()?;
471 if tagged.tag() != &RdTag::Alias {
472 return None;
473 }
474 if tagged.option().is_some() {
475 return Some(Err(shape(
476 path,
477 Some(RdTag::Alias),
478 RdShapeErrorKind::UnexpectedOption,
479 )));
480 }
481 Some(Ok(RdAlias {
482 nodes: tagged.children(),
483 }))
484 })
485 }
486
487 pub fn inspect_keywords(
489 &self,
490 ) -> impl Iterator<Item = Result<RdKeyword<'_>, RdShapeError>> + '_ {
491 self.nodes().iter().enumerate().filter_map(|(index, node)| {
492 let path = top_path(index);
493 if node
494 .as_raw()
495 .is_some_and(|raw| raw.tag() == Some(RdTag::Keyword.as_rd_tag()))
496 {
497 return Some(Err(shape(
498 path,
499 Some(RdTag::Keyword),
500 RdShapeErrorKind::UnexpectedNode {
501 expected: RdExpectedNode::Tagged,
502 actual: RdNodeKind::Raw,
503 },
504 )));
505 }
506 let tagged = node.as_tagged()?;
507 if tagged.tag() != &RdTag::Keyword {
508 return None;
509 }
510 if tagged.option().is_some() {
511 return Some(Err(shape(
512 path,
513 Some(RdTag::Keyword),
514 RdShapeErrorKind::UnexpectedOption,
515 )));
516 }
517 Some(Ok(RdKeyword {
518 nodes: tagged.children(),
519 }))
520 })
521 }
522
523 pub fn inspect_concepts(
525 &self,
526 ) -> impl Iterator<Item = Result<RdConcept<'_>, RdShapeError>> + '_ {
527 self.nodes().iter().enumerate().filter_map(|(index, node)| {
528 let path = top_path(index);
529 if node
530 .as_raw()
531 .is_some_and(|raw| raw.tag() == Some(RdTag::Concept.as_rd_tag()))
532 {
533 return Some(Err(shape(
534 path,
535 Some(RdTag::Concept),
536 RdShapeErrorKind::UnexpectedNode {
537 expected: RdExpectedNode::Tagged,
538 actual: RdNodeKind::Raw,
539 },
540 )));
541 }
542 let tagged = node.as_tagged()?;
543 if tagged.tag() != &RdTag::Concept {
544 return None;
545 }
546 if tagged.option().is_some() {
547 return Some(Err(shape(
548 path,
549 Some(RdTag::Concept),
550 RdShapeErrorKind::UnexpectedOption,
551 )));
552 }
553 Some(Ok(RdConcept {
554 nodes: tagged.children(),
555 }))
556 })
557 }
558
559 pub fn section_tree(&self) -> impl Iterator<Item = RdSectionVisit<'_>> {
566 let mut visits = Vec::new();
567 for (index, node) in self.nodes().iter().enumerate() {
568 collect_section_visits(
569 node,
570 top_path(index),
571 RdSectionKind::Section,
572 0,
573 false,
574 &mut visits,
575 );
576 }
577 visits.into_iter().filter_map(Result::ok)
578 }
579
580 pub fn inspect_section_tree(
588 &self,
589 ) -> impl Iterator<Item = Result<RdSectionVisit<'_>, RdShapeError>> {
590 let mut visits = Vec::new();
591 for (index, node) in self.nodes().iter().enumerate() {
592 collect_section_visits(
593 node,
594 top_path(index),
595 RdSectionKind::Section,
596 0,
597 true,
598 &mut visits,
599 );
600 }
601 visits.into_iter()
602 }
603
604 pub fn inspect_sections(
606 &self,
607 ) -> impl Iterator<Item = Result<RdSection<'_>, RdShapeError>> + '_ {
608 self.nodes().iter().enumerate().filter_map(|(index, node)| {
609 let path = top_path(index);
610 if node
611 .as_raw()
612 .is_some_and(|raw| raw.tag() == Some(RdTag::Section.as_rd_tag()))
613 {
614 return Some(Err(shape(
615 path,
616 Some(RdTag::Section),
617 RdShapeErrorKind::UnexpectedNode {
618 expected: RdExpectedNode::Tagged,
619 actual: RdNodeKind::Raw,
620 },
621 )));
622 }
623 let tagged = node.as_tagged()?;
624 if tagged.tag() != &RdTag::Section {
625 return None;
626 }
627 if tagged.option().is_some() {
628 return Some(Err(shape(
629 path,
630 Some(RdTag::Section),
631 RdShapeErrorKind::UnexpectedOption,
632 )));
633 }
634 if tagged.children().len() != 2 {
635 return Some(Err(shape(
636 path,
637 Some(RdTag::Section),
638 RdShapeErrorKind::WrongArity {
639 expected: RdArity::Exactly(2),
640 actual: tagged.children().len(),
641 },
642 )));
643 }
644 let [title, body] = tagged.children() else {
645 unreachable!()
646 };
647 for (child_index, child) in tagged.children().iter().enumerate() {
648 if child.as_group().is_none() {
649 return Some(Err(shape(
650 child_path(index, child_index),
651 Some(RdTag::Section),
652 RdShapeErrorKind::UnexpectedNode {
653 expected: RdExpectedNode::Group,
654 actual: RdNodeKind::of(child),
655 },
656 )));
657 }
658 }
659 Some(Ok(RdSection {
660 title: title.as_group().unwrap().children(),
661 body: body.as_group().unwrap().children(),
662 }))
663 })
664 }
665
666 pub fn inspect_arguments(
668 &self,
669 ) -> Result<impl Iterator<Item = Result<RdArgument<'_>, RdShapeError>> + '_, RdShapeError> {
670 let mut container = None;
671 for (index, node) in self.nodes().iter().enumerate() {
672 let path = top_path(index);
673 if node
674 .as_raw()
675 .is_some_and(|raw| raw.tag() == Some(RdTag::Arguments.as_rd_tag()))
676 {
677 return Err(shape(
678 path,
679 Some(RdTag::Arguments),
680 RdShapeErrorKind::UnexpectedNode {
681 expected: RdExpectedNode::Tagged,
682 actual: RdNodeKind::Raw,
683 },
684 ));
685 }
686 let Some(tagged) = node.as_tagged() else {
687 continue;
688 };
689 if tagged.tag() != &RdTag::Arguments {
690 continue;
691 }
692 if container.is_some() {
693 let first_index = container.map(|(index, _)| index).unwrap_or(index);
694 return Err(shape(
695 path,
696 Some(RdTag::Arguments),
697 RdShapeErrorKind::Duplicate {
698 construct: RdConstruct::Tag(RdTag::Arguments),
699 first_path: top_path(first_index),
700 },
701 ));
702 }
703 if tagged.option().is_some() {
704 return Err(shape(
705 path,
706 Some(RdTag::Arguments),
707 RdShapeErrorKind::UnexpectedOption,
708 ));
709 }
710 container = Some((index, tagged.children()));
711 }
712 let parent_index = container.map_or(0, |(index, _)| index);
713 let children = container.map_or(&[][..], |(_, children)| children);
714 Ok(children
715 .iter()
716 .enumerate()
717 .filter_map(move |(index, node)| {
718 if is_inter_item_trivia(node) {
719 return None;
720 }
721 let path = child_path(parent_index, index);
722 let Some(tagged) = node.as_tagged() else {
723 return Some(Err(shape(
724 path,
725 node_tag(node),
726 RdShapeErrorKind::UnexpectedContent {
727 actual: RdNodeKind::of(node),
728 },
729 )));
730 };
731 if tagged.tag() != &RdTag::Item {
732 return Some(Err(shape(
733 path,
734 Some(tagged.tag().clone()),
735 RdShapeErrorKind::UnexpectedContent {
736 actual: RdNodeKind::Tagged,
737 },
738 )));
739 }
740 let (name, description) = match inspect_two_group_item(tagged, &path) {
741 Ok(groups) => groups,
742 Err(error) => return Some(Err(error)),
743 };
744 Some(Ok(RdArgument { name, description }))
745 }))
746 }
747}
748
749fn collect_section_visits<'a>(
750 node: &'a RdNode,
751 path: RdPath,
752 kind: RdSectionKind,
753 nesting: usize,
754 strict: bool,
755 output: &mut Vec<Result<RdSectionVisit<'a>, RdShapeError>>,
756) {
757 let wanted = match kind {
758 RdSectionKind::Section => RdTag::Section,
759 RdSectionKind::Subsection => RdTag::Subsection,
760 };
761 let Some(tagged) = node.as_tagged() else {
762 if strict
763 && node
764 .as_raw()
765 .is_some_and(|raw| raw.tag() == Some(wanted.as_rd_tag()))
766 {
767 output.push(Err(shape(
768 path,
769 Some(wanted),
770 RdShapeErrorKind::UnexpectedNode {
771 expected: RdExpectedNode::Tagged,
772 actual: RdNodeKind::Raw,
773 },
774 )));
775 }
776 return;
777 };
778 if tagged.tag() != &wanted {
779 return;
780 }
781 if tagged.option().is_some() {
782 if strict {
783 output.push(Err(shape(
784 path,
785 Some(wanted),
786 RdShapeErrorKind::UnexpectedOption,
787 )));
788 }
789 return;
790 }
791 if tagged.children().len() != 2 {
792 if strict {
793 output.push(Err(shape(
794 path,
795 Some(wanted),
796 RdShapeErrorKind::WrongArity {
797 expected: RdArity::Exactly(2),
798 actual: tagged.children().len(),
799 },
800 )));
801 }
802 return;
803 }
804 let [title, body] = tagged.children() else {
805 unreachable!()
806 };
807 if let Some((child_index, child)) = tagged
808 .children()
809 .iter()
810 .enumerate()
811 .find(|(_, child)| child.as_group().is_none())
812 {
813 if strict {
814 output.push(Err(shape(
815 path.with_child(child_index),
816 Some(wanted),
817 RdShapeErrorKind::UnexpectedNode {
818 expected: RdExpectedNode::Group,
819 actual: RdNodeKind::of(child),
820 },
821 )));
822 }
823 return;
824 }
825 let title = title.as_group().unwrap().children();
826 let body = body.as_group().unwrap().children();
827 output.push(Ok(RdSectionVisit {
828 path: path.clone(),
829 kind,
830 nesting,
831 title,
832 body,
833 }));
834
835 let body_path = path.with_child(1);
836 for (index, child) in body.iter().enumerate() {
837 collect_section_visits(
838 child,
839 body_path.with_child(index),
840 RdSectionKind::Subsection,
841 nesting + 1,
842 strict,
843 output,
844 );
845 }
846}