Skip to main content

rd_ast/view/
example.rs

1use super::*;
2
3/// The source-level spelling of an example-control wrapper.
4///
5/// This classifies spelling only and does not prescribe execution, visibility,
6/// testing, or output-comparison behavior. `\dontshow` and `\testonly` are
7/// deliberately distinct variants even though R treats them as synonyms:
8/// views are a shape/spelling layer.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10#[non_exhaustive]
11pub enum RdExampleControlKind {
12    DontRun,
13    DontTest,
14    DontShow,
15    DontDiff,
16    TestOnly,
17}
18
19/// A borrowed, structurally valid example-control wrapper view.
20#[derive(Debug, Clone, PartialEq)]
21pub struct RdExampleControl<'a> {
22    path: RdPath,
23    kind: RdExampleControlKind,
24    body: &'a [RdNode],
25}
26
27impl<'a> RdExampleControl<'a> {
28    pub fn path(&self) -> &RdPath {
29        &self.path
30    }
31
32    pub fn kind(&self) -> RdExampleControlKind {
33        self.kind
34    }
35
36    /// Returns all direct children of the wrapper exactly as stored: no clone,
37    /// flattening, leaf filtering, or text concatenation is performed.
38    /// Consumers own all policy about the body.
39    pub fn body(&self) -> &'a [RdNode] {
40        self.body
41    }
42}
43
44fn example_control_kind(tag: &RdTag) -> Option<RdExampleControlKind> {
45    match tag {
46        RdTag::DontRun => Some(RdExampleControlKind::DontRun),
47        RdTag::DontTest => Some(RdExampleControlKind::DontTest),
48        RdTag::DontShow => Some(RdExampleControlKind::DontShow),
49        RdTag::DontDiff => Some(RdExampleControlKind::DontDiff),
50        RdTag::TestOnly => Some(RdExampleControlKind::TestOnly),
51        _ => None,
52    }
53}
54
55impl RdNode {
56    /// Lossily views a canonical example-control wrapper without an option.
57    pub fn example_control(&self, base_path: &RdPath) -> Option<RdExampleControl<'_>> {
58        let tagged = self.as_tagged()?;
59        let kind = example_control_kind(tagged.tag())?;
60        tagged.option().is_none().then(|| RdExampleControl {
61            path: base_path.clone(),
62            kind,
63            body: tagged.children(),
64        })
65    }
66
67    /// Strictly inspects a canonical example-control wrapper without changing
68    /// or validating any of its direct children.
69    pub fn inspect_example_control(
70        &self,
71        base_path: &RdPath,
72    ) -> Result<Option<RdExampleControl<'_>>, RdShapeError> {
73        match self {
74            RdNode::Tagged(tagged) => {
75                let Some(kind) = example_control_kind(tagged.tag()) else {
76                    return Ok(None);
77                };
78                if tagged.option().is_some() {
79                    return Err(shape(
80                        base_path.clone(),
81                        Some(tagged.tag().clone()),
82                        RdShapeErrorKind::UnexpectedOption,
83                    ));
84                }
85                Ok(Some(RdExampleControl {
86                    path: base_path.clone(),
87                    kind,
88                    body: tagged.children(),
89                }))
90            }
91            RdNode::Raw(raw) => {
92                let Some(tag) = raw.tag().map(RdTag::from_rd_tag) else {
93                    return Ok(None);
94                };
95                if example_control_kind(&tag).is_none() {
96                    return Ok(None);
97                }
98                Err(shape(
99                    base_path.clone(),
100                    Some(tag),
101                    RdShapeErrorKind::UnexpectedNode {
102                        expected: RdExpectedNode::Tagged,
103                        actual: RdNodeKind::Raw,
104                    },
105                ))
106            }
107            _ => Ok(None),
108        }
109    }
110}