mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-11 20:32:12 +00:00
163 lines
4.8 KiB
Rust
163 lines
4.8 KiB
Rust
// Copyright (c) 2016 Brandon Thomas <bt@brand.io, echelon@gmail.com>
|
|
|
|
use resolve::hostname;
|
|
use std::convert::From;
|
|
use std::fs::File;
|
|
use std::io::Error;
|
|
use std::io::Read;
|
|
use std::io;
|
|
use toml;
|
|
|
|
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
|
|
pub struct Config {
|
|
/// Whether or not to send/respect client caching headers.
|
|
pub use_caching_headers: Option<bool>,
|
|
|
|
/// Where sound files are located.
|
|
pub sound_path: Option<String>,
|
|
pub html_path: Option<String>,
|
|
|
|
/// Where various arpabet files are located.
|
|
pub phoneme_dictionary_file: Option<String>,
|
|
pub arpabet_override_dictionary_file: Option<String>,
|
|
pub extra_dictionary_file: Option<String>,
|
|
pub square_dictionary_file: Option<String>,
|
|
|
|
/// Where the abbreviation file is found.
|
|
pub abbreviation_file: Option<String>,
|
|
|
|
/// Input control. Respond with 400 when over limit.
|
|
pub max_num_characters: Option<usize>,
|
|
pub max_num_words: Option<usize>,
|
|
|
|
/// What events to log.
|
|
pub log_handler: Option<bool>,
|
|
pub log_parsing: Option<bool>,
|
|
pub log_raw_sentence: Option<bool>,
|
|
pub log_syllable_decomposition: Option<bool>,
|
|
pub log_synthesis: Option<bool>,
|
|
pub log_timing: Option<bool>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum ConfigError {
|
|
/// Wraps an IO error.
|
|
IoError { cause: io::Error },
|
|
/// TOML parsing error.
|
|
TomlError,
|
|
/// Configs absent.
|
|
TomlMissing,
|
|
}
|
|
|
|
impl Config {
|
|
/// Static CTOR.
|
|
/// Read a [default] set of configs and merge with a ["$hostname"] set.
|
|
pub fn read(filename: &str) -> Result<Config, ConfigError> {
|
|
let contents = read_file(filename)?;
|
|
|
|
let table = match toml::Parser::new(&contents).parse() {
|
|
None => { return Err(ConfigError::TomlError); },
|
|
Some(table) => table,
|
|
};
|
|
|
|
let system_hostname = hostname::get_hostname().ok();
|
|
|
|
let maybe_host = if system_hostname.is_some() {
|
|
table.get(&system_hostname.unwrap())
|
|
.and_then(|t| toml::decode::<Config>(t.clone()))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let maybe_default = table.get("default")
|
|
.and_then(|t| toml::decode::<Config>(t.clone()));
|
|
|
|
if maybe_default.is_none() {
|
|
if maybe_host.is_none() {
|
|
return Err(ConfigError::TomlMissing);
|
|
} else {
|
|
return Ok(maybe_host.unwrap());
|
|
}
|
|
}
|
|
|
|
let default = maybe_default.unwrap();
|
|
|
|
if maybe_host.is_some() {
|
|
let host = maybe_host.unwrap();
|
|
Ok(host.merge(default))
|
|
} else {
|
|
Ok(default)
|
|
}
|
|
}
|
|
|
|
/// Merge another config object, keeping current values where they
|
|
/// exist, and overriding Optional values where they do not.
|
|
pub fn merge(&self, other: Config) -> Config {
|
|
// TODO: Simplify using generics. Maybe a trait, such as "x.as_default_or(y)"
|
|
Config {
|
|
use_caching_headers: self.use_caching_headers
|
|
.clone()
|
|
.or(other.use_caching_headers.clone()),
|
|
sound_path: self.sound_path
|
|
.clone()
|
|
.or(other.sound_path.clone()),
|
|
html_path: self.html_path
|
|
.clone()
|
|
.or(other.html_path.clone()),
|
|
phoneme_dictionary_file: self.phoneme_dictionary_file
|
|
.clone()
|
|
.or(other.phoneme_dictionary_file.clone()),
|
|
arpabet_override_dictionary_file: self.arpabet_override_dictionary_file
|
|
.clone()
|
|
.or(other.arpabet_override_dictionary_file.clone()),
|
|
extra_dictionary_file: self.extra_dictionary_file
|
|
.clone()
|
|
.or(other.extra_dictionary_file.clone()),
|
|
square_dictionary_file: self.square_dictionary_file
|
|
.clone()
|
|
.or(other.square_dictionary_file.clone()),
|
|
abbreviation_file: self.abbreviation_file
|
|
.clone()
|
|
.or(other.abbreviation_file.clone()),
|
|
max_num_characters: self.max_num_characters
|
|
.clone()
|
|
.or(other.max_num_characters.clone()),
|
|
max_num_words: self.max_num_words
|
|
.clone()
|
|
.or(other.max_num_words.clone()),
|
|
log_handler: self.log_handler
|
|
.clone()
|
|
.or(other.log_handler.clone()),
|
|
log_parsing: self.log_parsing
|
|
.clone()
|
|
.or(other.log_parsing.clone()),
|
|
log_raw_sentence: self.log_raw_sentence
|
|
.clone()
|
|
.or(other.log_raw_sentence.clone()),
|
|
log_syllable_decomposition: self.log_syllable_decomposition
|
|
.clone()
|
|
.or(other.log_syllable_decomposition.clone()),
|
|
log_synthesis: self.log_synthesis
|
|
.clone()
|
|
.or(other.log_synthesis.clone()),
|
|
log_timing: self.log_timing
|
|
.clone()
|
|
.or(other.log_timing.clone()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<io::Error> for ConfigError {
|
|
fn from(error: io::Error) -> ConfigError {
|
|
ConfigError::IoError { cause: error }
|
|
}
|
|
}
|
|
|
|
fn read_file(filename: &str) -> Result<String, Error> {
|
|
let mut file = File::open(filename)?;
|
|
let mut buf = String::new();
|
|
file.read_to_string(&mut buf)?;
|
|
Ok(buf)
|
|
}
|
|
|