2020-11-29 18:37:33 +00:00
|
|
|
// False positive with itertools::process_results
|
|
|
|
#![allow (clippy::redundant_closure)]
|
|
|
|
|
2020-11-26 23:30:33 +00:00
|
|
|
use std::{
|
2020-11-29 18:37:33 +00:00
|
|
|
collections::HashMap,
|
2020-11-26 23:30:33 +00:00
|
|
|
convert::{TryFrom, TryInto},
|
2020-12-12 15:35:33 +00:00
|
|
|
fmt,
|
2020-11-26 23:30:33 +00:00
|
|
|
iter::FromIterator,
|
2020-12-12 15:35:33 +00:00
|
|
|
ops::Deref,
|
2020-11-26 23:30:33 +00:00
|
|
|
path::Path,
|
|
|
|
};
|
|
|
|
|
2020-12-12 15:35:33 +00:00
|
|
|
use serde::{
|
|
|
|
Deserialize,
|
|
|
|
Deserializer,
|
|
|
|
de::{
|
|
|
|
self,
|
|
|
|
Visitor,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2020-11-29 16:58:56 +00:00
|
|
|
use crate::errors::ConfigError;
|
2020-11-26 23:30:33 +00:00
|
|
|
|
2020-12-12 15:35:33 +00:00
|
|
|
pub struct BlakeHashWrapper (blake3::Hash);
|
|
|
|
|
|
|
|
impl Deref for BlakeHashWrapper {
|
|
|
|
type Target = blake3::Hash;
|
|
|
|
|
|
|
|
fn deref (&self) -> &<Self as Deref>::Target {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct BlakeHashVisitor;
|
|
|
|
|
|
|
|
impl <'de> Visitor <'de> for BlakeHashVisitor {
|
|
|
|
type Value = blake3::Hash;
|
|
|
|
|
|
|
|
fn expecting (&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
formatter.write_str ("a 32-byte blake3 hash, encoded as base64")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_str <E: de::Error> (self, value: &str)
|
|
|
|
-> Result <Self::Value, E>
|
|
|
|
{
|
|
|
|
let bytes: Vec <u8> = base64::decode (value).map_err (|_| E::custom (format! ("str is not base64: {}", value)))?;
|
|
|
|
let bytes: [u8; 32] = (&bytes [..]).try_into ().map_err (|_| E::custom (format! ("decode base64 is not 32 bytes long: {}", value)))?;
|
|
|
|
|
|
|
|
let tripcode = blake3::Hash::from (bytes);
|
|
|
|
|
|
|
|
Ok (tripcode)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl <'de> Deserialize <'de> for BlakeHashWrapper {
|
|
|
|
fn deserialize <D: Deserializer <'de>> (deserializer: D) -> Result <Self, D::Error> {
|
|
|
|
Ok (BlakeHashWrapper (deserializer.deserialize_str (BlakeHashVisitor)?))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-26 23:30:33 +00:00
|
|
|
// Stuff we need to load from the config file and use to
|
|
|
|
// set up the HTTP server
|
|
|
|
|
|
|
|
pub mod file {
|
2020-11-29 18:37:33 +00:00
|
|
|
use serde::Deserialize;
|
2020-11-26 23:30:33 +00:00
|
|
|
|
2020-12-12 15:35:33 +00:00
|
|
|
use super::*;
|
|
|
|
|
2020-11-26 23:30:33 +00:00
|
|
|
#[derive (Deserialize)]
|
|
|
|
pub struct Server {
|
2020-11-30 15:52:15 +00:00
|
|
|
pub name: String,
|
2020-11-26 23:30:33 +00:00
|
|
|
pub tripcode: String,
|
|
|
|
pub display_name: Option <String>,
|
|
|
|
}
|
|
|
|
|
2020-12-12 01:26:58 +00:00
|
|
|
// Stuff that's identical between the file and the runtime structures
|
|
|
|
|
|
|
|
#[derive (Default, Deserialize)]
|
|
|
|
pub struct Isomorphic {
|
|
|
|
#[serde (default)]
|
|
|
|
pub enable_dev_mode: bool,
|
|
|
|
#[serde (default)]
|
|
|
|
pub enable_scraper_auth: bool,
|
2020-12-12 15:35:33 +00:00
|
|
|
|
|
|
|
pub dev_scraper_key: Option <BlakeHashWrapper>,
|
2020-12-12 01:26:58 +00:00
|
|
|
}
|
|
|
|
|
2020-11-26 23:30:33 +00:00
|
|
|
#[derive (Deserialize)]
|
|
|
|
pub struct Config {
|
|
|
|
pub port: Option <u16>,
|
2020-11-30 15:52:15 +00:00
|
|
|
pub servers: Vec <Server>,
|
2020-12-12 01:26:58 +00:00
|
|
|
#[serde (flatten)]
|
|
|
|
pub iso: Isomorphic,
|
2020-11-26 23:30:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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>,
|
2020-12-12 01:26:58 +00:00
|
|
|
pub iso: file::Isomorphic,
|
2020-11-26 23:30:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 ()
|
2020-11-30 15:52:15 +00:00
|
|
|
.map (|server| Ok::<_, ConfigError> ((server.name.clone (), server.try_into ()?)));
|
2020-11-26 23:30:33 +00:00
|
|
|
|
|
|
|
let servers = itertools::process_results (servers, |i| HashMap::from_iter (i))?;
|
|
|
|
|
|
|
|
Ok (Self {
|
|
|
|
servers,
|
2020-12-12 01:26:58 +00:00
|
|
|
iso: f.iso,
|
2020-11-26 23:30:33 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Config {
|
|
|
|
pub async fn from_file (path: &Path) -> Result <Self, ConfigError> {
|
|
|
|
use tokio::prelude::*;
|
|
|
|
|
|
|
|
let mut f = tokio::fs::File::open (path).await?;
|
|
|
|
|
2020-11-29 18:37:33 +00:00
|
|
|
let mut buffer = vec! [0_u8; 4096];
|
2020-11-26 23:30:33 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|