misc_conf/
ast.rs

1//! Common AST structs and traits
2
3use std::{
4    fmt::Debug,
5    marker::PhantomData,
6    path::{Path, PathBuf},
7};
8
9use anyhow::Context;
10
11use crate::{
12    cpath::{CPath, Filter},
13    lexer::Literal,
14};
15
16pub trait FromLiteral: Eq + PartialEq + for<'a> From<Literal<'a>> + Clone + Default {}
17
18impl<T: Eq + PartialEq + for<'a> From<Literal<'a>> + Clone + Default> FromLiteral for T {}
19
20#[derive(Debug, Clone, Default)]
21pub struct Config<S, T = String>
22where
23    S: Clone + Default,
24    T: FromLiteral,
25{
26    pub path: PathBuf,
27    pub root: Directive<S, T>,
28}
29
30impl<S, T> Config<S, T>
31where
32    Directive<S, T>: DirectiveTrait<S, T>,
33    S: Clone + Default,
34    T: FromLiteral,
35{
36    pub fn parse(path: PathBuf) -> anyhow::Result<Self> {
37        let data = std::fs::read(&path)?;
38        Ok(Config {
39            path,
40            root: Directive {
41                children: Some(Directive::parse(&data)?),
42                ..Default::default()
43            },
44            ..Default::default()
45        })
46    }
47
48    pub fn root_directives(&self) -> &[Directive<S, T>] {
49        self.root
50            .children
51            .as_ref()
52            .expect("root must have children")
53    }
54
55    pub fn resolve_include(&mut self, root_dir: Option<&Path>) -> anyhow::Result<()> {
56        self.root
57            .resolve_include(root_dir.or(self.path.parent()).context("no root_dir")?)?;
58        Ok(())
59    }
60}
61
62pub trait DirectiveTrait<S, T = String>: Sized + AsMut<Directive<S, T>>
63where
64    Directive<S, T>: DirectiveTrait<S, T>,
65    S: Clone + Default,
66    T: FromLiteral,
67{
68    fn parse(input: &[u8]) -> anyhow::Result<Vec<Self>>;
69
70    fn resolve_include(&mut self, dir: &Path) -> anyhow::Result<()> {
71        if let Some(childs) = self.as_mut().children.take() {
72            let mut result = vec![];
73            for c in childs {
74                c.resolve_include_inner(dir, &mut result)?;
75            }
76            self.as_mut().children.replace(result);
77        }
78        Ok(())
79    }
80
81    fn resolve_include_inner(self, dir: &Path, out: &mut Vec<Self>) -> anyhow::Result<()>;
82}
83
84#[derive(Clone, Default, Eq)]
85pub struct Directive<S, T = String>
86where
87    S: Clone + Default,
88    T: FromLiteral,
89{
90    pub name: T,
91    pub args: Vec<T>,
92    pub children: Option<Vec<Directive<S, T>>>,
93    pub(crate) _scheme: PhantomData<S>,
94}
95
96impl<S: Debug, T: Debug> Debug for Directive<S, T>
97where
98    S: Clone + Default,
99    T: FromLiteral,
100{
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102        let mut ds = f.debug_struct("Directive");
103        ds.field("name", &self.name).field("args", &self.args);
104        if let Some(children) = self.children.as_ref() {
105            ds.field("children", children);
106        }
107        ds.finish()
108    }
109}
110
111impl<S, T> PartialEq for Directive<S, T>
112where
113    S: Clone + Default,
114    T: FromLiteral,
115{
116    fn eq(&self, other: &Self) -> bool {
117        self.name == other.name && self.args == other.args && self.children == other.children
118    }
119}
120
121impl<S, T> AsMut<Self> for Directive<S, T>
122where
123    S: Clone + Default,
124    T: FromLiteral,
125{
126    fn as_mut(&mut self) -> &mut Self {
127        self
128    }
129}
130
131impl<S, T> Directive<S, T>
132where
133    S: Clone + Default,
134    T: FromLiteral + AsRef<str>,
135{
136    pub fn query(&self, path: &str) -> Vec<Self> {
137        let mut result = vec![];
138        if let Some(childs) = self.children.as_ref() {
139            Self::inner_query(childs, path, &mut result);
140        }
141        result
142    }
143
144    fn inner_query(dirs: &[Self], path: &str, out: &mut Vec<Self>) {
145        let mut pathitem = path;
146        let mut rest = None;
147        if let Some((i, r)) = path.split_once("/") {
148            pathitem = i;
149            rest.replace(r);
150        }
151        for d in dirs.iter() {
152            if d.name.as_ref().eq_ignore_ascii_case(pathitem) {
153                if let Some(path) = rest {
154                    Self::inner_query(
155                        d.children.as_ref().map(Vec::as_slice).unwrap_or(&[]),
156                        path,
157                        out,
158                    );
159                } else {
160                    out.push(d.clone());
161                }
162            }
163        }
164    }
165
166    pub fn cpath_query(&self, path: &CPath) -> Vec<Self> {
167        let mut result = vec![];
168        if let Some(childs) = self.children.as_ref() {
169            Self::inner_cpath_query(childs, path, &mut result);
170        }
171        result
172    }
173
174    fn inner_cpath_query(dirs: &[Self], path: &CPath, out: &mut Vec<Self>) {
175        let (item, rest, anylevel) = match path.peek() {
176            Some(x) => x,
177            _ => return,
178        };
179
180        for d in dirs.iter() {
181            let childs = d.children.as_ref().map(Vec::as_slice).unwrap_or(&[]);
182            if d.match_filter(&item.filter) {
183                // leaf match
184                if rest.is_empty() {
185                    out.push(d.clone());
186                } else {
187                    Self::inner_cpath_query(childs, rest, out);
188                }
189            }
190            if anylevel {
191                Self::inner_cpath_query(childs, path, out);
192            }
193        }
194    }
195
196    fn match_filter(&self, filter: &Filter) -> bool {
197        match filter {
198            Filter::Eq(n) => self.name.as_ref().eq_ignore_ascii_case(n),
199            Filter::Re(re) => re.is_match(self.name.as_ref()),
200            Filter::Any => true,
201            Filter::AnyLevel => false,
202        }
203    }
204}