Skip to main content

rd_ast/view/
method.rs

1use super::*;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4#[non_exhaustive]
5pub enum RdMethodKind {
6    Method,
7    S3Method,
8    S4Method,
9}
10
11#[derive(Debug, Clone, PartialEq)]
12/// A method usage node with exact Text-scalar generic and qualifier arguments.
13/// The following call syntax `(x, ...)` in `\usage` is not part of this
14/// method node; it remains a sibling `RCode` leaf of the parent section.
15pub struct RdMethod<'a> {
16    path: RdPath,
17    kind: RdMethodKind,
18    generic_nodes: &'a [RdNode],
19    generic: String,
20    qualifier_nodes: &'a [RdNode],
21    qualifier: String,
22}
23
24impl<'a> RdMethod<'a> {
25    pub fn path(&self) -> &RdPath {
26        &self.path
27    }
28    pub fn kind(&self) -> RdMethodKind {
29        self.kind
30    }
31    pub fn generic_nodes(&self) -> &'a [RdNode] {
32        self.generic_nodes
33    }
34    pub fn generic(&self) -> &str {
35        &self.generic
36    }
37    pub fn qualifier_nodes(&self) -> &'a [RdNode] {
38        self.qualifier_nodes
39    }
40    /// Class for `\method`/`\S3method`, signature for `\S4method`; consumers branch on `kind()`.
41    pub fn qualifier(&self) -> &str {
42        &self.qualifier
43    }
44}
45
46fn kind(tag: &RdTag) -> Option<RdMethodKind> {
47    match tag {
48        RdTag::Method => Some(RdMethodKind::Method),
49        RdTag::S3Method => Some(RdMethodKind::S3Method),
50        RdTag::S4Method => Some(RdMethodKind::S4Method),
51        _ => None,
52    }
53}
54
55impl RdNode {
56    pub fn method(&self, base_path: &RdPath) -> Option<RdMethod<'_>> {
57        self.inspect_method(base_path).ok().flatten()
58    }
59
60    pub fn inspect_method(&self, base_path: &RdPath) -> Result<Option<RdMethod<'_>>, RdShapeError> {
61        let tagged = match self {
62            RdNode::Tagged(tagged) => tagged,
63            RdNode::Raw(raw) => {
64                let Some(tag) = raw.tag().map(RdTag::from_rd_tag) else {
65                    return Ok(None);
66                };
67                if kind(&tag).is_some() {
68                    return Err(shape(
69                        base_path.clone(),
70                        Some(tag),
71                        RdShapeErrorKind::UnexpectedNode {
72                            expected: RdExpectedNode::Tagged,
73                            actual: RdNodeKind::Raw,
74                        },
75                    ));
76                }
77                return Ok(None);
78            }
79            _ => return Ok(None),
80        };
81        let Some(kind) = kind(tagged.tag()) else {
82            return Ok(None);
83        };
84        if tagged.option().is_some() {
85            return Err(shape(
86                base_path.clone(),
87                Some(tagged.tag().clone()),
88                RdShapeErrorKind::UnexpectedOption,
89            ));
90        }
91        let children = tagged.children();
92        if children.len() != 2 {
93            return Err(shape(
94                base_path.clone(),
95                Some(tagged.tag().clone()),
96                RdShapeErrorKind::WrongArity {
97                    expected: RdArity::Exactly(2),
98                    actual: children.len(),
99                },
100            ));
101        }
102        for (index, child) in children.iter().enumerate() {
103            if child.as_group().is_none() {
104                return Err(shape(
105                    base_path.with_child(index),
106                    Some(tagged.tag().clone()),
107                    RdShapeErrorKind::UnexpectedNode {
108                        expected: RdExpectedNode::Group,
109                        actual: RdNodeKind::of(child),
110                    },
111                ));
112            }
113        }
114        let generic_nodes = children[0].as_group().unwrap().children();
115        let qualifier_nodes = children[1].as_group().unwrap().children();
116        let generic = concat_exact(
117            generic_nodes,
118            RdNodeKind::Text,
119            &base_path.with_child(0),
120            tagged.tag(),
121        )?;
122        let qualifier = concat_exact(
123            qualifier_nodes,
124            RdNodeKind::Text,
125            &base_path.with_child(1),
126            tagged.tag(),
127        )?;
128        Ok(Some(RdMethod {
129            path: base_path.clone(),
130            kind,
131            generic_nodes,
132            generic,
133            qualifier_nodes,
134            qualifier,
135        }))
136    }
137}