1use 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 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}