use std::collections::HashMap;
use std::net::Ipv4Addr;
use super::section::borrowed::Section;
use crate::section::{AddressRef, HostRef};
use crate::line::borrowed::Line;
mod global;
pub use global::Global;
mod frontend;
pub use frontend::Frontend;
mod backend;
pub use backend::Backend;
mod listen;
pub use listen::Listen;
mod userlist;
pub use userlist::{Userlist, User, Group, Password};
mod error;
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Acl {
pub name: String,
pub rule: String,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Bind {
pub addr: Address,
pub config: Option<String>,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Server {
pub name: String,
pub addr: Address,
pub option: Option<String>,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Address {
pub host: Host,
pub port: Option<u16>,
}
impl From<&AddressRef<'_>> for Address {
fn from(r: &AddressRef<'_>) -> Self {
Address {
host: Host::from(&r.host),
port: r.port,
}
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum Host {
Ipv4(Ipv4Addr),
Dns(String),
Wildcard,
}
impl From<&HostRef<'_>> for Host {
fn from(h: &HostRef<'_>) -> Self {
match h {
HostRef::Ipv4(a) => Host::Ipv4(*a),
HostRef::Dns(s) => Host::Dns((*s).to_string()),
HostRef::Wildcard => Host::Wildcard,
}
}
}
pub type Name = String;
#[derive(Debug)]
pub struct Config {
pub global: Global,
pub default: Default,
pub frontends: HashMap<Name, Frontend>,
pub backends: HashMap<Name, Backend>,
pub listen: HashMap<Name, Listen>,
pub userlists: HashMap<Name, Userlist>,
}
impl<'a> TryFrom<&'a Vec<Section<'a>>> for Config {
type Error = error::Error;
fn try_from(vec: &'a Vec<Section<'a>>) -> Result<Self, Self::Error> {
Config::try_from(vec.as_slice())
}
}
impl<'a> TryFrom<&'a [Section<'a>]> for Config {
type Error = error::Error;
fn try_from(entries: &'a [Section<'a>]) -> Result<Self, Self::Error> {
let unknown_lines: Vec<_> = entries
.iter()
.filter_map(|l| match l {
Section::UnknownLine { line } => Some(*line),
_ => None,
})
.collect();
if !unknown_lines.is_empty() {
return Err(error::Error::unknown_lines(unknown_lines));
}
Ok(Config {
global: Global::try_from(entries)?,
default: Default::try_from(entries)?,
frontends: Frontend::parse_multiple(entries)?,
backends: Backend::parse_multiple(entries)?,
listen: Listen::parse_multiple(entries)?,
userlists: Userlist::parse_multiple(entries)?,
})
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Default {
pub proxy: Option<String>,
pub config: HashMap<String, Option<String>>,
pub options: HashMap<String, Option<String>>,
}
impl<'a> TryFrom<&'a [Section<'a>]> for Default {
type Error = error::Error;
fn try_from(entries: &'a [Section<'_>]) -> Result<Self, Self::Error> {
let default_entries: Vec<_> = entries
.iter()
.filter(|e| matches!(e, Section::Default { .. }))
.collect();
if default_entries.len() > 1 {
return Err(error::Error::multiple_default_entries(default_entries));
}
let Some(Section::Default{ proxy, lines, ..}) = default_entries.first() else {
return Ok(Default::default());
};
let mut config = HashMap::new();
let mut options = HashMap::new();
let mut other = Vec::new();
for line in lines
.iter()
.filter(|l| !matches!(l, Line::Blank | Line::Comment(_)))
{
match line {
Line::Config { key, value, .. } => {
let key = (*key).to_string();
let value = value.map(ToOwned::to_owned);
config.insert(key, value);
}
Line::Option {
keyword: key,
value,
..
} => {
let key = (*key).to_string();
let value = value.map(ToOwned::to_owned);
options.insert(key, value);
}
o => other.push(o),
}
}
if !other.is_empty() {
return Err(error::Error::wrong_default_lines(other));
}
Ok(Default {
proxy: proxy.map(ToOwned::to_owned),
config,
options,
})
}
}