use crate::error::Error;
use serde::Deserialize;
use std::fs;
use std::fs::File;
use std::io::BufRead;
#[derive(Deserialize)]
struct Network {
bucket: usize,
replication: usize,
signaling: String,
port: usize,
cache: usize,
}
#[derive(Deserialize)]
struct LoadConfig {
network: Network,
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct Config {
pub bucket: usize,
pub replication: usize,
pub signaling: String,
pub port: usize,
pub cache: usize,
}
#[derive(Deserialize)]
struct LoadCenter {
ip: String,
port: usize,
hostname: String,
}
pub struct Signaling {
server: String,
port: usize,
}
pub struct CenterConfig {
pub ip: String,
pub port: usize,
pub secret: Option<[u8; 32]>,
pub hostname: String,
}
impl Signaling {
pub fn new(server: String, port: usize) -> Self {
Self { server, port }
}
pub fn to_string(&self) -> String {
let elements = [self.server.clone(), self.port.to_string()];
elements.join(":")
}
}
impl Config {
pub fn new(
bucket: usize,
replication: usize,
cache: usize,
signaling: String,
port: usize,
) -> Self {
Self {
bucket,
replication,
signaling,
port,
cache,
}
}
pub fn from_file(path: &str) -> Result<Self, Error> {
let content = fs::read_to_string(path)?;
Self::from_string(content)
}
pub fn from_string(content: String) -> Result<Self, Error> {
let config: Result<LoadConfig, toml::de::Error> = toml::from_str(&content);
match config {
Ok(c) => {
log::info!("Successfully loaded system config from file!");
return Ok(Self {
bucket: c.network.bucket,
replication: c.network.replication,
signaling: c.network.signaling,
port: c.network.port,
cache: c.network.cache,
});
}
Err(e) => {
log::error!("System config is not valid: {}", e);
return Err(Error::Config(String::from("unable to parse toml")));
}
}
}
}
impl CenterConfig {
pub fn new(ip: String, port: usize, secret: [u8; 32], hostname: String) -> Self {
Self {
ip,
port,
secret: Some(secret),
hostname,
}
}
pub fn from_file(path: &str) -> Result<Self, Error> {
let content = fs::read_to_string(path)?;
Self::from_string(content)
}
pub fn from_string(config: String) -> Result<Self, Error> {
let config: Result<LoadCenter, toml::de::Error> = toml::from_str(&config);
match config {
Ok(c) => {
log::info!("Successfully loaded center config from file!");
return Ok(Self {
ip: c.ip,
port: c.port,
secret: None,
hostname: c.hostname,
});
}
Err(e) => {
log::error!("Config is not valid: {}", e);
return Err(Error::Config(String::from(
"unable to parse config from toml",
)));
}
}
}
pub fn load_key(path: &str) -> Result<[u8; 32], Error> {
let file = File::open(path)?;
let reader = std::io::BufReader::new(file);
let line = reader.split(b'\n').next();
match line {
Some(rkey) => {
let key = rkey?;
if key.len() != 32 {
return Err(Error::Config(String::from("invalid byte length in key")));
}
let mut bytes: [u8; 32] = [0; 32];
for (i, j) in key.iter().enumerate() {
bytes[i] = *j;
}
return Ok(bytes);
}
None => {
return Err(Error::Config(String::from("key file is empty")));
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_system_parse() {
let c = "# Example Actaeon config.
[network]
bucket = 32
signaling = '127.0.0.1'
replication = 3
port = 4242
cache = 32
";
let config = Config::from_string(c.to_string()).unwrap();
let created = Config::new(32, 3, 32, "127.0.0.1".to_owned(), 4242);
assert_eq!(config, created);
}
#[test]
fn test_center_parse() {
let c = "# Example Actaeon config.
ip = '127.0.0.1'
port = 42
hostname = 'actaeon'
";
let config = CenterConfig::from_string(c.to_string()).unwrap();
let created = CenterConfig::new("127.0.0.1".to_owned(), 42, [0; 32], "actaeon".to_owned());
assert_eq!(config.ip, created.ip);
}
}