ptth/src/relay/config.rs

109 lines
2.3 KiB
Rust

use std::{
collections::*,
convert::{TryFrom, TryInto},
iter::FromIterator,
path::Path,
};
use serde::Deserialize;
use thiserror::Error;
#[derive (Error, Debug)]
pub enum ConfigError {
#[error ("I/O error")]
Io (#[from] std::io::Error),
#[error ("UTF-8 decoding failed")]
Utf8 (#[from] std::string::FromUtf8Error),
#[error ("TOML parsing failed")]
Toml (#[from] toml::de::Error),
#[error ("base64 decoding failed")]
Base64Decode (#[from] base64::DecodeError),
#[error ("tripcode not 32 bytes after decoding")]
TripcodeBadLength,
#[error ("unknown config error")]
Unknown,
}
// Stuff we need to load from the config file and use to
// set up the HTTP server
pub mod file {
use super::*;
#[derive (Deserialize)]
pub struct Server {
pub tripcode: String,
pub display_name: Option <String>,
}
#[derive (Deserialize)]
pub struct Config {
pub port: Option <u16>,
pub servers: HashMap <String, Server>,
}
}
// Stuff we actually need at runtime
pub struct Server {
pub tripcode: blake3::Hash,
pub display_name: Option <String>,
}
pub struct Config {
pub servers: HashMap <String, Server>,
}
impl TryFrom <file::Server> for Server {
type Error = ConfigError;
fn try_from (f: file::Server) -> Result <Self, Self::Error> {
let bytes: Vec <u8> = base64::decode (f.tripcode)?;
let bytes: [u8; 32] = (&bytes [..]).try_into ().map_err (|_| ConfigError::TripcodeBadLength)?;
let tripcode = blake3::Hash::from (bytes);
Ok (Self {
tripcode,
display_name: f.display_name,
})
}
}
impl TryFrom <file::Config> for Config {
type Error = ConfigError;
fn try_from (f: file::Config) -> Result <Self, Self::Error> {
let servers = f.servers.into_iter ()
.map (|(k, v)| Ok::<_, ConfigError> ((k, v.try_into ()?)));
let servers = itertools::process_results (servers, |i| HashMap::from_iter (i))?;
Ok (Self {
servers,
})
}
}
impl Config {
pub async fn from_file (path: &Path) -> Result <Self, ConfigError> {
use tokio::prelude::*;
let mut f = tokio::fs::File::open (path).await?;
let mut buffer = vec! [0u8; 4096];
let bytes_read = f.read (&mut buffer).await?;
buffer.truncate (bytes_read);
let config_s = String::from_utf8 (buffer)?;
let new_config: file::Config = toml::from_str (&config_s)?;
Self::try_from (new_config)
}
}