From 97f2678563e6ca997ea33d7befe38ab2acfa01a9 Mon Sep 17 00:00:00 2001 From: Daniel Schadt Date: Sat, 14 Dec 2019 03:23:46 +0100 Subject: add preliminary support for user configurations --- src/main.rs | 18 +++++++- src/render.rs | 37 +++++++++-------- src/useropts.rs | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+), 20 deletions(-) create mode 100644 src/useropts.rs (limited to 'src') 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, - line_height: u32, - font: Font<'static>, - text_color: Rgba, - text_size: u32, - background_color: Rgba, - 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, + pub line_height: u32, + pub font: Font<'static>, + pub text_color: Rgba, + pub text_size: u32, + pub background_color: Rgba, + 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, + pub trait_size: Option, + pub line_color: Option<[u8; 4]>, + pub line_height: Option, + pub text_size: Option, + pub background_color: Option<[u8; 4]>, + pub font_path: Option, + pub render_specialization_names: Option, + pub skill_alignment: Option, +} + +impl UserOptions { + /// Converts this `UserOptions` to a proper + /// [`RenderOptions`](../render/struct.RenderOptions.html). + pub fn convert(self) -> Result { + 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>(path: P) -> Result { + let data = fs::read(path)?; + let opts = toml::from_slice::(&data)?; + opts.convert() +} -- cgit v1.2.3 From 7f2f9b03debc7fc16f9614df7cb6fa4c0999d128 Mon Sep 17 00:00:00 2001 From: Daniel Schadt Date: Wed, 25 Dec 2019 00:56:44 +0100 Subject: add more user configurable options --- src/useropts.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/useropts.rs b/src/useropts.rs index e6de8de..2e98926 100644 --- a/src/useropts.rs +++ b/src/useropts.rs @@ -71,14 +71,20 @@ macro_rules! maybe_take_from { /// `image::Rgba` for `background_color`). #[derive(Debug, Serialize, Deserialize)] pub struct UserOptions { + pub skill_size: Option, + pub traitline_brightness: Option, + pub traitline_use_gradient: Option, + pub traitline_gradient_size: Option, pub traitline_x_offset: Option, pub trait_size: Option, pub line_color: Option<[u8; 4]>, pub line_height: Option, + pub font_path: Option, + pub text_color: Option<[u8; 4]>, pub text_size: Option, pub background_color: Option<[u8; 4]>, - pub font_path: Option, pub render_specialization_names: Option, + pub skill_offset: Option, pub skill_alignment: Option, } @@ -96,6 +102,10 @@ impl UserOptions { result.line_color = Rgba(data); } + if let Some(data) = self.text_color { + result.text_color = Rgba(data); + } + if let Some(path) = self.font_path { let data = fs::read(path)?; let font = Font::from_bytes(data)?; @@ -104,11 +114,16 @@ impl UserOptions { maybe_take_from! { from: self, to: result, + skill_size, + traitline_brightness, + traitline_use_gradient, + traitline_gradient_size, traitline_x_offset, trait_size, line_height, text_size, render_specialization_names, + skill_offset, skill_alignment, } -- cgit v1.2.3