haproxy_config/config/
frontend.rs

1use 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/// Backend to which a frontend can forward messages
9#[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}