diff options
author | Daniel Schadt <kingdread@gmx.de> | 2019-12-14 03:23:46 +0100 |
---|---|---|
committer | Daniel Schadt <kingdread@gmx.de> | 2019-12-14 03:23:46 +0100 |
commit | 97f2678563e6ca997ea33d7befe38ab2acfa01a9 (patch) | |
tree | d0ee757fcc01555362ec4578dafbf946034c0e64 /src | |
parent | 2aef95f62788163b789fae92b5a6546b2413d9e4 (diff) | |
download | kondou-97f2678563e6ca997ea33d7befe38ab2acfa01a9.tar.gz kondou-97f2678563e6ca997ea33d7befe38ab2acfa01a9.tar.bz2 kondou-97f2678563e6ca997ea33d7befe38ab2acfa01a9.zip |
add preliminary support for user configurations
Diffstat (limited to 'src')
-rw-r--r-- | src/main.rs | 18 | ||||
-rw-r--r-- | src/render.rs | 37 | ||||
-rw-r--r-- | src/useropts.rs | 127 |
3 files changed, 162 insertions, 20 deletions
diff --git a/src/main.rs b/src/main.rs index 395e683..f990319 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ extern crate num_traits; extern crate reqwest; extern crate rusttype; extern crate termcolor; +extern crate toml; extern crate xdg; #[macro_use] extern crate lazy_static; @@ -33,6 +34,7 @@ mod bt; mod cache; mod output; mod render; +mod useropts; use clap::{App, Arg, ArgMatches}; @@ -345,6 +347,12 @@ fn run() -> MainResult<()> { .default_value("buildtemplate.png") .takes_value(true), ) + .arg( + Arg::with_name("config") + .help("Specifies the render option file.") + .long("config") + .takes_value(true), + ) .get_matches(); let mut api = Api::new(cache::FileCache::new()); @@ -360,7 +368,13 @@ fn run() -> MainResult<()> { println!("{}", build.chatlink()); } - let mut renderer = render::Renderer::new(&mut api, Default::default()); + let render_options = if let Some(config_path) = matches.value_of("config") { + useropts::load_file(config_path)? + } else { + Default::default() + }; + + let mut renderer = render::Renderer::new(&mut api, render_options); match renderer.render_buildtemplate(&build) { Ok(img) => { let filename = matches.value_of("outfile").unwrap(); @@ -368,7 +382,7 @@ fn run() -> MainResult<()> { if !matches.is_present("quiet") { println!("Image saved in {}", filename); } - }, + } Err(RenderError::EmptyBuild) => (), Err(err) => { eprintln!("Image could not be rendered:"); diff --git a/src/render.rs b/src/render.rs index cbfd54f..6513cd5 100644 --- a/src/render.rs +++ b/src/render.rs @@ -7,6 +7,7 @@ use image::{ use imageproc::{drawing, rect::Rect}; use num_traits::{Num, NumCast}; use rusttype::{Font, Scale, SharedBytes}; +use serde::{Deserialize, Serialize}; use std::{error::Error, fmt}; #[derive(Debug)] @@ -43,7 +44,7 @@ impl Error for RenderError { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Alignment { Left, Center, @@ -52,23 +53,23 @@ pub enum Alignment { #[derive(Debug, Clone)] pub struct RenderOptions { - skill_size: u32, - traitline_height: u32, - traitline_width: u32, - traitline_brightness: i32, - traitline_use_gradient: bool, - traitline_gradient_size: u32, - traitline_x_offset: u32, - trait_size: u32, - line_color: Rgba<u8>, - line_height: u32, - font: Font<'static>, - text_color: Rgba<u8>, - text_size: u32, - background_color: Rgba<u8>, - render_specialization_names: bool, - skill_offset: u32, - skill_alignment: Alignment, + pub skill_size: u32, + pub traitline_height: u32, + pub traitline_width: u32, + pub traitline_brightness: i32, + pub traitline_use_gradient: bool, + pub traitline_gradient_size: u32, + pub traitline_x_offset: u32, + pub trait_size: u32, + pub line_color: Rgba<u8>, + pub line_height: u32, + pub font: Font<'static>, + pub text_color: Rgba<u8>, + pub text_size: u32, + pub background_color: Rgba<u8>, + pub render_specialization_names: bool, + pub skill_offset: u32, + pub skill_alignment: Alignment, } impl Default for RenderOptions { diff --git a/src/useropts.rs b/src/useropts.rs new file mode 100644 index 0000000..e6de8de --- /dev/null +++ b/src/useropts.rs @@ -0,0 +1,127 @@ +//! Support for loading user defined rendering options. +//! +//! User options are given in a toml file, for example +//! +//! ```toml +//! skill_size = 20 +//! ``` +//! +//! The file is automatically deserialized by serde, so all fields defined in +//! [`UserOptions`](struct.UserOptions.html) can be used. +use image::Rgba; +use rusttype::Font; +use serde::{Deserialize, Serialize}; +use std::{error::Error, fmt, fs, io, path::Path}; + +use super::render::{Alignment, RenderOptions}; + +/// Error that can occur during loading or converting user options. +#[derive(Debug)] +pub enum ConfigError { + Io(io::Error), + Serialization(toml::de::Error), + Font(rusttype::Error), +} + +error_froms! { + ConfigError, + err: io::Error => ConfigError::Io(err), + err: toml::de::Error => ConfigError::Serialization(err), + err: rusttype::Error => ConfigError::Font(err), +} + +impl fmt::Display for ConfigError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ConfigError: ")?; + match *self { + ConfigError::Io(_) => write!(f, "input/output error"), + ConfigError::Serialization(_) => write!(f, "serialization error"), + ConfigError::Font(_) => write!(f, "could not load the font"), + } + } +} + +impl Error for ConfigError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match *self { + ConfigError::Io(ref err) => Some(err), + ConfigError::Serialization(ref err) => Some(err), + ConfigError::Font(ref err) => Some(err), + } + } +} + +macro_rules! maybe_take_from { + (from: $from:expr, to: $to:ident, $($field:ident,)*) => { + $( + if let Some(data) = $from.$field { + $to.$field = data; + } + )* + } +} + +/// A struct for deserializing user defined options. +/// +/// This is basically a struct containing the same fields, but optional. If a field is given, it +/// should override the corresponding field in +/// [`RenderOptions`](../render/struct.RenderOptions.html). +/// +/// Some fields require some pre-processing, as the proper type cannot be serialized (e.g. +/// `image::Rgba` for `background_color`). +#[derive(Debug, Serialize, Deserialize)] +pub struct UserOptions { + pub traitline_x_offset: Option<u32>, + pub trait_size: Option<u32>, + pub line_color: Option<[u8; 4]>, + pub line_height: Option<u32>, + pub text_size: Option<u32>, + pub background_color: Option<[u8; 4]>, + pub font_path: Option<String>, + pub render_specialization_names: Option<bool>, + pub skill_alignment: Option<Alignment>, +} + +impl UserOptions { + /// Converts this `UserOptions` to a proper + /// [`RenderOptions`](../render/struct.RenderOptions.html). + pub fn convert(self) -> Result<RenderOptions, ConfigError> { + let mut result = RenderOptions::default(); + + if let Some(data) = self.background_color { + result.background_color = Rgba(data); + } + + if let Some(data) = self.line_color { + result.line_color = Rgba(data); + } + + if let Some(path) = self.font_path { + let data = fs::read(path)?; + let font = Font::from_bytes(data)?; + result.font = font; + } + + maybe_take_from! { + from: self, to: result, + traitline_x_offset, + trait_size, + line_height, + text_size, + render_specialization_names, + skill_alignment, + } + + Ok(result) + } +} + +/// Load user given options from the given file path. +/// +/// This is basically a convenience function around reading the data, deserializing it from toml +/// and returning [`UserOptions::convert`](struct.UserOptions.html#method.convert). +pub fn load_file<P: AsRef<Path>>(path: P) -> Result<RenderOptions, ConfigError> { + let data = fs::read(path)?; + let opts = toml::from_slice::<UserOptions>(&data)?; + opts.convert() +} |