misc_conf/apache/
mod.rs

1//! Nom parser for apache configuration
2
3pub mod lexer;
4
5use std::path::Path;
6
7use crate::{
8    ast::{Directive, DirectiveTrait},
9    lexer::line_column2,
10};
11
12use self::lexer::*;
13
14use anyhow::Context;
15use nom::{
16    combinator::{fail, map, map_opt, verify},
17    sequence::tuple,
18    IResult,
19};
20
21#[derive(Debug, Clone, Default)]
22pub struct Apache;
23
24impl DirectiveTrait<Apache> for Directive<Apache> {
25    fn parse(input: &[u8]) -> anyhow::Result<Vec<Self>> {
26        let res = parse_block(input).map_err(|err| {
27            err.map(|e| {
28                let ((l, c), pos) = line_column2(input, e.input).unwrap();
29                anyhow::anyhow!("{pos}({l}:{c}) err: {:?}", e.code)
30            })
31        })?;
32        Ok(res.1)
33    }
34
35    fn resolve_include_inner(mut self, dir: &Path, out: &mut Vec<Self>) -> anyhow::Result<()> {
36        let optional = self.name.eq_ignore_ascii_case("IncludeOptional");
37        if self.name.eq_ignore_ascii_case("include") || optional {
38            let path = Path::new(
39                self.args
40                    .get(0)
41                    .context("include directive expect one arg")?
42                    .as_str(),
43            );
44            for path in glob::glob(
45                &if path.is_absolute() {
46                    path.to_path_buf()
47                } else {
48                    dir.join(path)
49                }
50                .to_string_lossy(),
51            )?
52            .flatten()
53            {
54                if optional && !path.exists() {
55                    continue;
56                }
57                let data = std::fs::read(&path)?;
58                for c in Self::parse(&data).with_context(|| format!("parse {path:?}"))? {
59                    c.resolve_include_inner(dir, out)?;
60                }
61            }
62        } else {
63            self.resolve_include(dir)?;
64            out.push(self);
65        }
66        Ok(())
67    }
68}
69
70fn parse_block(mut input: &[u8]) -> IResult<&[u8], Vec<Directive<Apache>>> {
71    let mut result = vec![];
72    loop {
73        let (rest, tok) = tokenizer(input)?;
74        match tok {
75            Token::NewLine => {
76                input = rest;
77                continue;
78            }
79            Token::CloseTag | Token::Eof => break,
80            _ => {}
81        }
82        let res = parse_one(rest, tok)?;
83        result.push(res.1);
84        input = res.0;
85    }
86    Ok((input, result))
87}
88
89fn parse_one<'a>(input: &'a [u8], tok: Token<'a>) -> IResult<&'a [u8], Directive<Apache>> {
90    match tok {
91        Token::OpenTag => {
92            let (rest, name) = map_opt(tokenizer, |tok| tok.ident())(input)?;
93            map(
94                tuple((
95                    parse_args::<false>,
96                    verify(tokenizer, |tok| tok == &Token::EndTag),
97                    parse_block,
98                    verify(tuple((tokenizer, tokenizer, tokenizer)), |&(b, tag, e)| {
99                        b == Token::CloseTag
100                            && tag
101                                .raw_string()
102                                .unwrap_or_default()
103                                .eq_ignore_ascii_case(name)
104                            && e == Token::EndTag
105                    }),
106                )),
107                |(args, _, children, _)| Directive {
108                    name: name.into(),
109                    args,
110                    children: Some(children),
111                    ..Default::default()
112                },
113            )(rest)
114        }
115        Token::Literal(l) => map(parse_args::<true>, |args| Directive {
116            name: l.raw.into(),
117            args,
118            children: None,
119            ..Default::default()
120        })(input),
121        _ => fail(input),
122    }
123}
124
125fn parse_args<const NL: bool>(mut input: &[u8]) -> IResult<&[u8], Vec<String>> {
126    let mut result = vec![];
127    loop {
128        let (rest, tok) = inner_tokenizer::<NL>(input)?;
129        if let Some(l) = tok.literal() {
130            result.push(l.into());
131        } else {
132            break;
133        }
134        input = rest;
135    }
136    // println!("[args] {result:?}");
137    Ok((input, result))
138}