haproxy_config/config/
frontend.rs1use std::collections::{HashMap, HashSet};
2
3use super::{Acl, error::Error, Name, Bind, Address};
4use crate::section;
5use crate::section::{BackendModifier, borrowed::Section};
6use crate::line::borrowed::Line;
7
8#[derive(Debug, Clone, Hash, PartialEq, Eq)]
10pub struct Backend {
11 pub name: String,
12 pub modifier: Option<BackendModifier>,
13 pub condition: Option<String>,
14}
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct Frontend {
18 pub name: String,
19 pub config: HashMap<String, Option<String>>,
20 pub options: HashMap<String, Option<String>>,
21 pub acls: HashSet<Acl>,
22 pub backends: Vec<Backend>,
23 pub bind: Bind,
24}
25
26type Pair = (Name, Frontend);
27
28impl<'a> TryFrom<&'a Section<'a>> for Pair {
29 type Error = Error;
30
31 fn try_from(entry: &'a Section<'a>) -> Result<Self, Self::Error> {
32 let Section::Frontend{ proxy, lines, header_addr, ..} = entry else {
33 unreachable!()
34 };
35
36 let mut config = HashMap::new();
37 let mut options = HashMap::new();
38 let mut acls = HashSet::new();
39 let mut backends = Vec::new();
40 let mut binds = Vec::new();
41 let mut other = Vec::new();
42
43 for line in lines
44 .iter()
45 .filter(|l| !matches!(l, Line::Blank | Line::Comment(_)))
46 {
47 match line {
48 Line::Config { key, value, .. } => {
49 let key = (*key).to_string();
50 let value = value.map(ToOwned::to_owned);
51 config.insert(key, value);
52 }
53 Line::Option {
54 keyword: key,
55 value,
56 ..
57 } => {
58 let key = (*key).to_string();
59 let value = value.map(ToOwned::to_owned);
60 options.insert(key, value);
61 }
62 Line::Acl { name, rule, .. } => {
63 acls.insert(Acl {
64 name: (*name).to_string(),
65 rule: rule.ok_or(Error::acl_without_rule(name))?.to_string(),
66 });
67 }
68 Line::Backend {
69 name,
70 modifier,
71 condition,
72 ..
73 } => backends.push(Backend {
74 name: (*name).to_string(),
75 modifier: modifier.clone(),
76 condition: condition.map(ToOwned::to_owned),
77 }),
78 Line::Bind { .. } => binds.push(line),
79 wrong => other.push(wrong),
80 }
81 }
82
83 if !other.is_empty() {
84 return Err(Error::wrong_frontend_lines(other));
85 }
86
87 if binds.len() > 1 {
88 return Err(Error::more_then_one_bind(binds));
89 }
90
91 let (addr, bind_config) = match (binds.first(), header_addr) {
92 (None, None) => return Err(Error::NoBind),
93 (None, Some((addr, config))) => (addr, config),
94 (Some(Line::Bind { addr, value, .. }), None) => (addr, value),
95 (Some(_), None) => unreachable!(),
96 (Some(_), Some(_)) => return Err(Error::HeaderAndBindLine),
97 };
98
99 Ok((
100 (*proxy).to_string(),
101 Frontend {
102 name: (*proxy).to_string(),
103 config,
104 options,
105 acls,
106 backends,
107 bind: Bind {
108 addr: Address::from(addr),
109 config: bind_config.map(ToOwned::to_owned),
110 },
111 },
112 ))
113 }
114}
115
116impl<'a> Frontend {
117 pub(crate) fn parse_multiple(entries: &'a [section::borrowed::Section<'a>]) -> Result<HashMap<Name, Self>, Error> {
118 entries
119 .iter()
120 .filter(|e| matches!(e, Section::Frontend { .. }))
121 .map(Pair::try_from)
122 .collect()
123 }
124}