1use std::collections::HashMap;
4use std::net::Ipv4Addr;
5
6use super::section::borrowed::Section;
7use crate::section::{AddressRef, HostRef};
8use crate::line::borrowed::Line;
9
10mod global;
11pub use global::Global;
12pub mod frontend;
13pub use frontend::Frontend;
14mod backend;
15pub use backend::Backend;
16mod listen;
17pub use listen::Listen;
18mod userlist;
19pub use userlist::{Userlist, User, Group, Password};
20mod error;
21
22#[derive(Debug, Clone, Hash, PartialEq, Eq)]
23pub struct Acl {
24 pub name: String,
25 pub rule: String,
26}
27
28#[derive(Debug, Clone, Hash, PartialEq, Eq)]
29pub struct Bind {
30 pub addr: Address,
31 pub config: Option<String>,
32}
33
34#[derive(Debug, Clone, Hash, PartialEq, Eq)]
35pub struct Server {
36 pub name: String,
37 pub addr: Address,
38 pub option: Option<String>,
39}
40
41#[derive(Debug, Clone, Hash, PartialEq, Eq)]
43pub struct Address {
44 pub host: Host,
45 pub port: Option<u16>,
46}
47
48impl From<&AddressRef<'_>> for Address {
49 fn from(r: &AddressRef<'_>) -> Self {
50 Address {
51 host: Host::from(&r.host),
52 port: r.port,
53 }
54 }
55}
56
57#[derive(Debug, Clone, Hash, PartialEq, Eq)]
59pub enum Host {
60 Ipv4(Ipv4Addr),
61 Dns(String),
62 Wildcard,
63}
64
65impl From<&HostRef<'_>> for Host {
66 fn from(h: &HostRef<'_>) -> Self {
67 match h {
68 HostRef::Ipv4(a) => Host::Ipv4(*a),
69 HostRef::Dns(s) => Host::Dns((*s).to_string()),
70 HostRef::Wildcard => Host::Wildcard,
71 }
72 }
73}
74
75pub type Name = String;
76
77#[derive(Debug)]
121pub struct Config {
122 pub global: Global,
123 pub default: Default,
124 pub frontends: HashMap<Name, Frontend>,
125 pub backends: HashMap<Name, Backend>,
126 pub listen: HashMap<Name, Listen>,
127 pub userlists: HashMap<Name, Userlist>,
128}
129
130impl<'a> TryFrom<&'a Vec<Section<'a>>> for Config {
131 type Error = error::Error;
132
133 fn try_from(vec: &'a Vec<Section<'a>>) -> Result<Self, Self::Error> {
134 Config::try_from(vec.as_slice())
135 }
136}
137
138impl<'a> TryFrom<&'a [Section<'a>]> for Config {
139 type Error = error::Error;
140
141 fn try_from(entries: &'a [Section<'a>]) -> Result<Self, Self::Error> {
142 let unknown_lines: Vec<_> = entries
143 .iter()
144 .filter_map(|l| match l {
145 Section::UnknownLine { line } => Some(*line),
146 _ => None,
147 })
148 .collect();
149
150 if !unknown_lines.is_empty() {
151 return Err(error::Error::unknown_lines(unknown_lines));
152 }
153
154 Ok(Config {
155 global: Global::try_from(entries)?,
156 default: Default::try_from(entries)?,
157 frontends: Frontend::parse_multiple(entries)?,
158 backends: Backend::parse_multiple(entries)?,
159 listen: Listen::parse_multiple(entries)?,
160 userlists: Userlist::parse_multiple(entries)?,
161 })
162 }
163}
164
165
166#[derive(Debug, Clone, Default, PartialEq, Eq)]
167pub struct Default {
168 pub proxy: Option<String>,
169 pub config: HashMap<String, Option<String>>,
170 pub options: HashMap<String, Option<String>>,
171}
172
173impl<'a> TryFrom<&'a [Section<'a>]> for Default {
174 type Error = error::Error;
175
176 fn try_from(entries: &'a [Section<'_>]) -> Result<Self, Self::Error> {
177 let default_entries: Vec<_> = entries
178 .iter()
179 .filter(|e| matches!(e, Section::Default { .. }))
180 .collect();
181
182 if default_entries.len() > 1 {
183 return Err(error::Error::multiple_default_entries(default_entries));
184 }
185
186 let Some(Section::Default{ proxy, lines, ..}) = default_entries.first() else {
187 return Ok(Default::default());
188 };
189
190 let mut config = HashMap::new();
191 let mut options = HashMap::new();
192 let mut other = Vec::new();
193 for line in lines
194 .iter()
195 .filter(|l| !matches!(l, Line::Blank | Line::Comment(_)))
196 {
197 match line {
198 Line::Config { key, value, .. } => {
199 let key = (*key).to_string();
200 let value = value.map(ToOwned::to_owned);
201 config.insert(key, value);
202 }
203 Line::Option {
204 keyword: key,
205 value,
206 ..
207 } => {
208 let key = (*key).to_string();
209 let value = value.map(ToOwned::to_owned);
210 options.insert(key, value);
211 }
212 o => other.push(o),
213 }
214 }
215
216 if !other.is_empty() {
217 return Err(error::Error::wrong_default_lines(other));
218 }
219
220 Ok(Default {
221 proxy: proxy.map(ToOwned::to_owned),
222 config,
223 options,
224 })
225 }
226}