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
85027
    pub(crate) fn kwd(&self) -> T {
40
85027
        self.kwd
41
85027
    }
42
    /// Check whether a single Item matches this TokenFmt rule, with respect
43
    /// to its number of arguments.
44
59372
    fn item_matches_args(&self, item: &Item<'_, T>) -> Result<()> {
45
59372
        let n_args = item.n_args();
46
59372
        if let Some(max) = self.max_args {
47
13210
            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
13208
            }
52
46162
        }
53
59370
        if let Some(min) = self.min_args {
54
33497
            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
33493
            }
59
25873
        }
60
59366
        Ok(())
61
59372
    }
62

            
63
    /// Check whether a single Item matches a TokenFmt rule, with respect
64
    /// to its object's presence and type.
65
59366
    fn item_matches_obj(&self, item: &Item<'_, T>) -> Result<()> {
66
59366
        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
59362
            (_, _) => Ok(()),
74
        }
75
59366
    }
76

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

            
84
    /// Check whether this kind of item may appear this many times.
85
135431
    pub(crate) fn check_multiplicity(&self, items: &[Item<'_, T>]) -> Result<()> {
86
135431
        match items.len() {
87
            0 => {
88
21784
                if self.required {
89
6
                    return Err(EK::MissingToken.with_msg(self.kwd.to_str()));
90
21778
                }
91
            }
92
108163
            1 => (),
93
            _ => {
94
5484
                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
5482
                }
99
            }
100
        }
101
135423
        Ok(())
102
135431
    }
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 struct TokenFmtBuilder<T: Keyword>(TokenFmt<T>);
121

            
122
impl<T: Keyword> From<TokenFmtBuilder<T>> for TokenFmt<T> {
123
6152
    fn from(builder: TokenFmtBuilder<T>) -> Self {
124
6152
        builder.0
125
6152
    }
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
6152
    pub(crate) fn new(t: T) -> Self {
134
6152
        Self(TokenFmt {
135
6152
            kwd: t,
136
6152
            min_args: None,
137
6152
            max_args: None,
138
6152
            required: false,
139
6152
            may_repeat: false,
140
6152
            obj: ObjKind::NoObj,
141
6152
        })
142
6152
    }
143

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

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

            
164
    /// Indicate that this Item takes no arguments.
165
    ///
166
    /// By default, items may take any number of arguments.
167
590
    pub(crate) fn no_args(self) -> Self {
168
590
        Self(TokenFmt {
169
590
            max_args: Some(0),
170
590
            ..self.0
171
590
        })
172
590
    }
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
3171
    pub(crate) fn args<R>(self, r: R) -> Self
177
3171
    where
178
3171
        R: std::ops::RangeBounds<usize>,
179
3171
    {
180
        use std::ops::Bound::*;
181
3171
        let min_args = match r.start_bound() {
182
3171
            Included(x) => Some(*x),
183
            Excluded(x) => Some(*x + 1),
184
            Unbounded => None,
185
        };
186
3171
        let max_args = match r.end_bound() {
187
149
            Included(x) => Some(*x),
188
            Excluded(x) => Some(*x - 1),
189
3022
            Unbounded => None,
190
        };
191
3171
        Self(TokenFmt {
192
3171
            min_args,
193
3171
            max_args,
194
3171
            ..self.0
195
3171
        })
196
3171
    }
197
    /// Indicate that this token takes an optional object.
198
    ///
199
    /// By default, objects are not allowed.
200
878
    pub(crate) fn obj_optional(self) -> Self {
201
878
        Self(TokenFmt {
202
878
            obj: ObjKind::ObjOk,
203
878
            ..self.0
204
878
        })
205
878
    }
206
    /// Indicate that this token takes an required object.
207
    ///
208
    /// By default, objects are not allowed.
209
1021
    pub(crate) fn obj_required(self) -> Self {
210
1021
        Self(TokenFmt {
211
1021
            obj: ObjKind::RequireObj,
212
1021
            ..self.0
213
1021
        })
214
1021
    }
215
}