1
//! Keywords for interpreting items and rules for validating them.
2

            
3
use crate::parse::keyword::Keyword;
4
use crate::parse::tokenize::Item;
5
use crate::{NetdocErrorKind as EK, Result};
6

            
7
/// May an Item take an object?
8
#[derive(Copy, Clone)]
9
enum ObjKind {
10
    /// No object is allowed.
11
    NoObj,
12
    /// An object is required.
13
    RequireObj,
14
    /// An object is optional.
15
    ObjOk,
16
}
17

            
18
/// A set of restrictions to place on Items for a single keyword.
19
///
20
/// These are built by the TokenFmtBuilder API.
21
#[derive(Clone)]
22
pub(crate) struct TokenFmt<T: Keyword> {
23
    /// Which keyword is being restricted?
24
    kwd: T,
25
    /// If present, a lower bound on how many arguments may be present.
26
    min_args: Option<usize>,
27
    /// If present, an upper bound on how many arguments may be present.
28
    max_args: Option<usize>,
29
    /// If true, then at least one of this Item must appear.
30
    required: bool,
31
    /// If false, then no more than one this Item may appear.
32
    may_repeat: bool,
33
    /// May this Item have an object? Must it?
34
    obj: ObjKind,
35
}
36

            
37
impl<T: Keyword> TokenFmt<T> {
38
    /// Return the keyword that this rule restricts.
39
88507
    pub(crate) fn kwd(&self) -> T {
40
88507
        self.kwd
41
88507
    }
42
    /// Check whether a single Item matches this TokenFmt rule, with respect
43
    /// to its number of arguments.
44
82085
    fn item_matches_args(&self, item: &Item<'_, T>) -> Result<()> {
45
82085
        let n_args = item.n_args();
46
82085
        if let Some(max) = self.max_args {
47
14749
            if n_args > max {
48
2
                return Err(EK::TooManyArguments
49
2
                    .with_msg(self.kwd.to_str())
50
2
                    .at_pos(item.pos()));
51
14747
            }
52
67336
        }
53
82083
        if let Some(min) = self.min_args {
54
43436
            if n_args < min {
55
4
                return Err(EK::TooFewArguments
56
4
                    .with_msg(self.kwd.to_str())
57
4
                    .at_pos(item.pos()));
58
43432
            }
59
38647
        }
60
82079
        Ok(())
61
82085
    }
62

            
63
    /// Check whether a single Item matches a TokenFmt rule, with respect
64
    /// to its object's presence and type.
65
82079
    fn item_matches_obj(&self, item: &Item<'_, T>) -> Result<()> {
66
82079
        match (&self.obj, item.has_obj()) {
67
2
            (ObjKind::NoObj, true) => Err(EK::UnexpectedObject
68
2
                .with_msg(self.kwd.to_str())
69
2
                .at_pos(item.pos())),
70
2
            (ObjKind::RequireObj, false) => Err(EK::MissingObject
71
2
                .with_msg(self.kwd.to_str())
72
2
                .at_pos(item.pos())),
73
82075
            (_, _) => Ok(()),
74
        }
75
82079
    }
76

            
77
    /// Check whether a single item has the right number of arguments
78
    /// and object.
79
82085
    pub(crate) fn check_item(&self, item: &Item<'_, T>) -> Result<()> {
80
82085
        self.item_matches_args(item)?;
81
82079
        self.item_matches_obj(item)
82
82085
    }
83

            
84
    /// Check whether this kind of item may appear this many times.
85
188101
    pub(crate) fn check_multiplicity(&self, items: &[Item<'_, T>]) -> Result<()> {
86
188101
        match items.len() {
87
            0 => {
88
29238
                if self.required {
89
6
                    return Err(EK::MissingToken.with_msg(self.kwd.to_str()));
90
29232
                }
91
            }
92
153155
            1 => (),
93
            _ => {
94
5708
                if !self.may_repeat {
95
2
                    return Err(EK::DuplicateToken
96
2
                        .with_msg(self.kwd.to_str())
97
2
                        .at_pos(items[1].pos()));
98
5706
                }
99
            }
100
        }
101
188093
        Ok(())
102
188101
    }
103
}
104

            
105
/// Represents a TokenFmt under construction.
106
///
107
/// To construct a rule, create this type with Keyword::rule(), then
108
/// call method on it to set its fields, and then pass it to
109
/// SectionRules::add().
110
///
111
/// # Example
112
///
113
/// ```ignore
114
/// // There must be exactly one "ROUTER" entry, with 5 or more arguments.
115
/// section_rules.add(D.rule().required().args(5..));
116
/// ```
117
///
118
/// TODO: I'd rather have this be pub(crate), but I haven't figured out
119
/// how to make that work.  There are complicated cascading side-effects.
120
pub(crate) struct TokenFmtBuilder<T: Keyword>(TokenFmt<T>);
121

            
122
impl<T: Keyword> From<TokenFmtBuilder<T>> for TokenFmt<T> {
123
6404
    fn from(builder: TokenFmtBuilder<T>) -> Self {
124
6404
        builder.0
125
6404
    }
126
}
127

            
128
impl<T: Keyword> TokenFmtBuilder<T> {
129
    /// Make a new TokenFmtBuilder with default behavior.
130
    ///
131
    /// (By default, all arguments are allowed, the Item may appear 0
132
    /// or 1 times, and it may not take an object.)
133
6404
    pub(crate) fn new(t: T) -> Self {
134
6404
        Self(TokenFmt {
135
6404
            kwd: t,
136
6404
            min_args: None,
137
6404
            max_args: None,
138
6404
            required: false,
139
6404
            may_repeat: false,
140
6404
            obj: ObjKind::NoObj,
141
6404
        })
142
6404
    }
143

            
144
    /// Indicate that this Item is required.
145
    ///
146
    /// By default, no item is required.
147
3389
    pub(crate) fn required(self) -> Self {
148
3389
        Self(TokenFmt {
149
3389
            required: true,
150
3389
            ..self.0
151
3389
        })
152
3389
    }
153

            
154
    /// Indicate that this Item may appear more than once.
155
    ///
156
    /// By default, items may not repeat.
157
1418
    pub(crate) fn may_repeat(self) -> Self {
158
1418
        Self(TokenFmt {
159
1418
            may_repeat: true,
160
1418
            ..self.0
161
1418
        })
162
1418
    }
163

            
164
    /// Indicate that this Item takes no arguments.
165
    ///
166
    /// By default, items may take any number of arguments.
167
614
    pub(crate) fn no_args(self) -> Self {
168
614
        Self(TokenFmt {
169
614
            max_args: Some(0),
170
614
            ..self.0
171
614
        })
172
614
    }
173
    /// Indicate that this item takes a certain number of arguments.
174
    ///
175
    /// The number of arguments is provided as a range, like `5..`.
176
3301
    pub(crate) fn args<R>(self, r: R) -> Self
177
3301
    where
178
3301
        R: std::ops::RangeBounds<usize>,
179
    {
180
        use std::ops::Bound::*;
181
3301
        let min_args = match r.start_bound() {
182
3301
            Included(x) => Some(*x),
183
            Excluded(x) => Some(*x + 1),
184
            Unbounded => None,
185
        };
186
3301
        let max_args = match r.end_bound() {
187
155
            Included(x) => Some(*x),
188
            Excluded(x) => Some(*x - 1),
189
3146
            Unbounded => None,
190
        };
191
3301
        Self(TokenFmt {
192
3301
            min_args,
193
3301
            max_args,
194
3301
            ..self.0
195
3301
        })
196
3301
    }
197
    /// Indicate that this token takes an optional object.
198
    ///
199
    /// By default, objects are not allowed.
200
914
    pub(crate) fn obj_optional(self) -> Self {
201
914
        Self(TokenFmt {
202
914
            obj: ObjKind::ObjOk,
203
914
            ..self.0
204
914
        })
205
914
    }
206
    /// Indicate that this token takes an required object.
207
    ///
208
    /// By default, objects are not allowed.
209
1063
    pub(crate) fn obj_required(self) -> Self {
210
1063
        Self(TokenFmt {
211
1063
            obj: ObjKind::RequireObj,
212
1063
            ..self.0
213
1063
        })
214
1063
    }
215
}