1use super::{Span, Spanned, Value};
4
5#[derive(Debug, Clone, PartialEq)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub struct Directive {
9 pub item: DirectiveItem,
11 pub span: Span,
13}
14
15#[derive(Debug, Clone, PartialEq)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub enum DirectiveItem {
19 Simple {
21 name: String,
23 args: Vec<Value>,
25 },
26 Block {
28 name: String,
30 args: Vec<Value>,
32 children: Vec<Directive>,
34 },
35}
36
37impl Directive {
38 pub fn simple(name: impl Into<String>, args: Vec<String>) -> Self {
40 Self {
41 item: DirectiveItem::Simple {
42 name: name.into(),
43 args: args.into_iter().map(Value::from).collect(),
44 },
45 span: Span::default(),
46 }
47 }
48
49 pub fn simple_with_span(name: impl Into<String>, args: Vec<String>, span: Span) -> Self {
51 Self {
52 item: DirectiveItem::Simple {
53 name: name.into(),
54 args: args.into_iter().map(Value::from).collect(),
55 },
56 span,
57 }
58 }
59
60 pub fn simple_with_values(name: impl Into<String>, args: Vec<Value>) -> Self {
62 Self {
63 item: DirectiveItem::Simple {
64 name: name.into(),
65 args,
66 },
67 span: Span::default(),
68 }
69 }
70
71 pub fn block(name: impl Into<String>, args: Vec<String>, children: Vec<Directive>) -> Self {
73 Self {
74 item: DirectiveItem::Block {
75 name: name.into(),
76 args: args.into_iter().map(Value::from).collect(),
77 children,
78 },
79 span: Span::default(),
80 }
81 }
82
83 pub fn block_with_span(
85 name: impl Into<String>,
86 args: Vec<String>,
87 children: Vec<Directive>,
88 span: Span,
89 ) -> Self {
90 Self {
91 item: DirectiveItem::Block {
92 name: name.into(),
93 args: args.into_iter().map(Value::from).collect(),
94 children,
95 },
96 span,
97 }
98 }
99
100 pub fn block_with_values(
102 name: impl Into<String>,
103 args: Vec<Value>,
104 children: Vec<Directive>,
105 ) -> Self {
106 Self {
107 item: DirectiveItem::Block {
108 name: name.into(),
109 args,
110 children,
111 },
112 span: Span::default(),
113 }
114 }
115
116 #[must_use]
118 pub fn name(&self) -> &str {
119 match &self.item {
120 DirectiveItem::Simple { name, .. } | DirectiveItem::Block { name, .. } => name,
121 }
122 }
123
124 #[must_use]
126 pub fn args(&self) -> &[Value] {
127 match &self.item {
128 DirectiveItem::Simple { args, .. } | DirectiveItem::Block { args, .. } => args,
129 }
130 }
131
132 #[must_use]
134 pub fn children(&self) -> Option<&[Directive]> {
135 match &self.item {
136 DirectiveItem::Block { children, .. } => Some(children),
137 DirectiveItem::Simple { .. } => None,
138 }
139 }
140
141 pub fn children_mut(&mut self) -> Option<&mut Vec<Directive>> {
143 match &mut self.item {
144 DirectiveItem::Block { children, .. } => Some(children),
145 DirectiveItem::Simple { .. } => None,
146 }
147 }
148
149 #[must_use]
151 pub fn is_block(&self) -> bool {
152 matches!(self.item, DirectiveItem::Block { .. })
153 }
154
155 #[must_use]
157 pub fn is_simple(&self) -> bool {
158 matches!(self.item, DirectiveItem::Simple { .. })
159 }
160
161 #[must_use]
163 pub fn first_arg(&self) -> Option<String> {
164 self.args().first().map(std::string::ToString::to_string)
165 }
166
167 #[must_use]
169 pub fn args_as_strings(&self) -> Vec<String> {
170 self.args()
171 .iter()
172 .map(std::string::ToString::to_string)
173 .collect()
174 }
175
176 #[must_use]
178 pub fn find_children(&self, name: &str) -> Vec<&Directive> {
179 match &self.item {
180 DirectiveItem::Block { children, .. } => {
181 children.iter().filter(|d| d.name() == name).collect()
182 }
183 DirectiveItem::Simple { .. } => Vec::new(),
184 }
185 }
186
187 pub fn find_children_mut(&mut self, name: &str) -> Vec<&mut Directive> {
189 match &mut self.item {
190 DirectiveItem::Block { children, .. } => {
191 children.iter_mut().filter(|d| d.name() == name).collect()
192 }
193 DirectiveItem::Simple { .. } => Vec::new(),
194 }
195 }
196
197 #[must_use]
199 pub fn find_recursive(&self, name: &str) -> Vec<&Directive> {
200 let mut result = Vec::new();
201 self.find_recursive_impl(name, &mut result);
202 result
203 }
204
205 fn find_recursive_impl<'a>(&'a self, name: &str, result: &mut Vec<&'a Directive>) {
206 if self.name() == name {
207 result.push(self);
208 }
209 if let Some(children) = self.children() {
210 for child in children {
211 child.find_recursive_impl(name, result);
212 }
213 }
214 }
215}
216
217impl Spanned for Directive {
218 fn span(&self) -> Span {
219 self.span
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 #[test]
228 fn test_simple_directive() {
229 let directive = Directive::simple("user", vec!["nginx".to_string()]);
230 assert_eq!(directive.name(), "user");
231 assert_eq!(directive.args().len(), 1);
232 assert!(directive.is_simple());
233 assert!(!directive.is_block());
234 assert_eq!(directive.first_arg(), Some("nginx".to_string()));
235 }
236
237 #[test]
238 fn test_simple_directive_with_span() {
239 let span = Span::new(0, 10, 1, 1);
240 let directive = Directive::simple_with_span("listen", vec!["80".to_string()], span);
241 assert_eq!(directive.span, span);
242 assert_eq!(directive.name(), "listen");
243 }
244
245 #[test]
246 fn test_block_directive() {
247 let directive = Directive::block("server", vec![], vec![]);
248 assert_eq!(directive.name(), "server");
249 assert!(directive.is_block());
250 assert!(!directive.is_simple());
251 assert_eq!(directive.children().unwrap().len(), 0);
252 }
253
254 #[test]
255 fn test_block_with_children() {
256 let children = vec![
257 Directive::simple("listen", vec!["80".to_string()]),
258 Directive::simple("server_name", vec!["example.com".to_string()]),
259 ];
260 let server = Directive::block("server", vec![], children);
261
262 assert_eq!(server.children().unwrap().len(), 2);
263 assert_eq!(server.children().unwrap()[0].name(), "listen");
264 }
265
266 #[test]
267 fn test_find_children() {
268 let children = vec![
269 Directive::simple("listen", vec!["80".to_string()]),
270 Directive::simple("server_name", vec!["example.com".to_string()]),
271 Directive::simple("listen", vec!["443".to_string()]),
272 ];
273 let server = Directive::block("server", vec![], children);
274
275 let listen_dirs = server.find_children("listen");
276 assert_eq!(listen_dirs.len(), 2);
277 assert_eq!(listen_dirs[0].first_arg(), Some("80".to_string()));
278 assert_eq!(listen_dirs[1].first_arg(), Some("443".to_string()));
279 }
280
281 #[test]
282 fn test_args_as_strings() {
283 let directive = Directive::simple(
284 "server_name",
285 vec!["example.com".to_string(), "www.example.com".to_string()],
286 );
287 let args = directive.args_as_strings();
288 assert_eq!(args, vec!["example.com", "www.example.com"]);
289 }
290
291 #[test]
292 fn test_find_recursive() {
293 let location = Directive::block(
303 "location",
304 vec!["/".to_string()],
305 vec![Directive::simple(
306 "access_log",
307 vec!["/var/log/1.log".to_string()],
308 )],
309 );
310 let server = Directive::block("server", vec![], vec![location]);
311 let http = Directive::block(
312 "http",
313 vec![],
314 vec![
315 server,
316 Directive::simple("access_log", vec!["/var/log/2.log".to_string()]),
317 ],
318 );
319
320 let access_logs = http.find_recursive("access_log");
321 assert_eq!(access_logs.len(), 2);
322 }
323
324 #[test]
325 fn test_simple_directive_with_values() {
326 let args = vec![Value::literal("combined"), Value::variable("remote_addr")];
327 let directive = Directive::simple_with_values("log_format", args);
328
329 assert_eq!(directive.args().len(), 2);
330 assert!(directive.args()[1].is_variable());
331 }
332}