174 lines
5 KiB
Rust
174 lines
5 KiB
Rust
use anyhow::{bail, Result};
|
|
use std::collections::hash_map;
|
|
use std::collections::HashMap;
|
|
use toml::Value;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct PortConfig {
|
|
pub enabled: bool,
|
|
pub description: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct ServerConfig {
|
|
auto: bool,
|
|
ports: HashMap<u16, PortConfig>,
|
|
}
|
|
|
|
impl ServerConfig {
|
|
#[cfg(test)]
|
|
pub fn default() -> ServerConfig {
|
|
ServerConfig { auto: true, ports: HashMap::new() }
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub fn set_auto(&mut self, auto: bool) {
|
|
self.auto = auto;
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub fn insert(&mut self, port: u16, config: PortConfig) {
|
|
self.ports.insert(port, config);
|
|
}
|
|
|
|
pub fn auto(&self) -> bool {
|
|
self.auto
|
|
}
|
|
|
|
pub fn iter(&self) -> hash_map::Iter<u16, PortConfig> {
|
|
self.ports.iter()
|
|
}
|
|
|
|
pub fn contains_key(&self, port: u16) -> bool {
|
|
self.ports.contains_key(&port)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Config {
|
|
auto: bool,
|
|
servers: HashMap<String, ServerConfig>,
|
|
}
|
|
|
|
impl Config {
|
|
pub fn get(&self, remote: &str) -> ServerConfig {
|
|
match self.servers.get(remote) {
|
|
Some(cfg) => cfg.clone(),
|
|
None => ServerConfig { auto: self.auto, ports: HashMap::new() },
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn load_config() -> Result<Config> {
|
|
use std::io::ErrorKind;
|
|
|
|
let mut home = match home::home_dir() {
|
|
Some(h) => h,
|
|
None => return Ok(default()),
|
|
};
|
|
home.push(".fwd");
|
|
|
|
let contents = match std::fs::read_to_string(home) {
|
|
Ok(contents) => contents,
|
|
Err(e) => match e.kind() {
|
|
ErrorKind::NotFound => return Ok(default()),
|
|
_ => return Err(e.into()),
|
|
},
|
|
};
|
|
|
|
parse_config(&contents.parse::<Value>()?)
|
|
}
|
|
|
|
fn default() -> Config {
|
|
Config { auto: true, servers: HashMap::new() }
|
|
}
|
|
|
|
fn parse_config(value: &Value) -> Result<Config> {
|
|
match value {
|
|
Value::Table(table) => Ok({
|
|
let auto = match table.get("auto") {
|
|
None => true,
|
|
Some(Value::Boolean(v)) => *v,
|
|
Some(v) => bail!("expected a true or false, got {:?}", v),
|
|
};
|
|
Config { auto, servers: get_servers(table, auto)? }
|
|
}),
|
|
_ => bail!("top level must be a table"),
|
|
}
|
|
}
|
|
|
|
fn get_servers(
|
|
table: &toml::value::Table,
|
|
auto: bool,
|
|
) -> Result<HashMap<String, ServerConfig>> {
|
|
match table.get("servers") {
|
|
None => Ok(HashMap::new()),
|
|
Some(Value::Table(table)) => Ok({
|
|
let mut servers = HashMap::new();
|
|
for (k, v) in table {
|
|
servers.insert(k.clone(), get_server(v, auto)?);
|
|
}
|
|
servers
|
|
}),
|
|
v => bail!("expected a table in the servers key, got {:?}", v),
|
|
}
|
|
}
|
|
|
|
fn get_server(value: &Value, auto: bool) -> Result<ServerConfig> {
|
|
match value {
|
|
Value::Table(table) => Ok(ServerConfig {
|
|
auto: match table.get("auto") {
|
|
None => auto, // Default to global default
|
|
Some(Value::Boolean(v)) => *v,
|
|
Some(v) => bail!("expected true or false, got {:?}", v),
|
|
},
|
|
ports: get_ports(table)?,
|
|
}),
|
|
value => bail!("expected a table, got {:?}", value),
|
|
}
|
|
}
|
|
|
|
fn get_ports(table: &toml::value::Table) -> Result<HashMap<u16, PortConfig>> {
|
|
match table.get("ports") {
|
|
None => Ok(HashMap::new()),
|
|
Some(Value::Table(table)) => Ok({
|
|
let mut ports = HashMap::new();
|
|
for (k,v) in table {
|
|
let port:u16 = k.parse()?;
|
|
let config = match v {
|
|
Value::Boolean(enabled) => PortConfig{enabled:*enabled, description:None},
|
|
Value::Table(table) => PortConfig{
|
|
enabled: match table.get("enabled") {
|
|
Some(Value::Boolean(enabled)) => *enabled,
|
|
_ => bail!("not implemented"),
|
|
},
|
|
description: match table.get("description") {
|
|
Some(Value::String(desc)) => Some(desc.clone()),
|
|
Some(v) => bail!("expect a string description, got {:?}", v),
|
|
None => None,
|
|
},
|
|
},
|
|
_ => bail!("expected either a boolean (enabled) or a table for a port config, got {:?}", v),
|
|
};
|
|
ports.insert(port, config);
|
|
}
|
|
ports
|
|
}),
|
|
Some(Value::Array(array)) => Ok({
|
|
let mut ports = HashMap::new();
|
|
for v in array {
|
|
ports.insert(get_port_number(v)?, PortConfig{enabled:true, description:None});
|
|
}
|
|
ports
|
|
}),
|
|
Some(v) => bail!("ports must be either a table of '<port> = ...' or an array of ports, got {:?}", v),
|
|
}
|
|
}
|
|
|
|
fn get_port_number(v: &Value) -> Result<u16> {
|
|
let port: u16 = match v {
|
|
Value::Integer(i) => (*i).try_into()?,
|
|
v => bail!("port must be a small number, got {:?}", v),
|
|
};
|
|
Ok(port)
|
|
}
|