haproxy_config/config/
global.rs

1use crate::config::error::Error;
2use crate::line::borrowed::Line;
3
4use super::super::section::borrowed::Section;
5
6use std::collections::HashMap;
7
8#[derive(Debug, Clone, Default, PartialEq, Eq)]
9pub struct Global {
10    pub config: HashMap<String, Option<String>>,
11    /// system user to run haproxy as
12    pub user: Option<String>,
13    /// system group to run haproxy as
14    pub group: Option<String>,
15}
16
17impl<'a> TryFrom<&'a [Section<'a>]> for Global {
18    type Error = Error;
19
20    fn try_from(entries: &'a [Section<'_>]) -> Result<Self, Self::Error> {
21        let global_entries: Vec<_> = entries
22            .iter()
23            .filter(|e| matches!(e, Section::Global { .. }))
24            .collect();
25
26        if global_entries.len() > 1 {
27            return Err(Error::multiple_global_entries(global_entries));
28        }
29
30        let Some(Section::Global{ lines, .. }) = global_entries.first() else {
31            return Ok(Global::default());
32        };
33
34        let mut config = HashMap::new();
35        let mut user_lines = Vec::new();
36        let mut group_lines = Vec::new();
37        let mut other = Vec::new();
38
39        for line in lines
40            .iter()
41            .filter(|l| !matches!(l, Line::Blank | Line::Comment(_)))
42        {
43            match line {
44                Line::Config { key, value, .. } => {
45                    let key = (*key).to_string();
46                    let value = value.map(ToOwned::to_owned);
47                    config.insert(key, value);
48                }
49                Line::SysUser { .. } => {
50                    user_lines.push(line);
51                }
52                Line::Group { .. } => {
53                    group_lines.push(line);
54                }
55                wrong => other.push(wrong),
56            }
57        }
58
59        if !other.is_empty() {
60            return Err(Error::wrong_global_lines(other));
61        }
62
63        let (user, group) = extract_sys_user_group(user_lines, group_lines)?;
64
65        Ok(Global {
66            config,
67            user,
68            group,
69        })
70    }
71}
72
73fn extract_sys_user_group<'a>(
74    mut user_lines: Vec<&'a Line>,
75    mut group_lines: Vec<&'a Line>,
76) -> Result<(Option<String>, Option<String>), Error> {
77    if user_lines.len() > 1 {
78        return Err(Error::multiple_sys_users(user_lines));
79    }
80    if group_lines.len() > 1 {
81        return Err(Error::multiple_sys_groups(group_lines));
82    }
83
84    let user = match user_lines.pop() {
85        None => None,
86        Some(Line::SysUser { name, .. }) => Some((*name).to_string()),
87        _wrong => unreachable!(),
88    };
89
90    let group = match group_lines.pop() {
91        None => None,
92        Some(line @ Line::Group { name, users, .. }) => {
93            if !users.is_empty() {
94                return Err(Error::sys_group_has_users(line));
95            }
96            Some((*name).to_string())
97        }
98        _wrong => unreachable!(),
99    };
100
101    Ok((user, group))
102}