diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main.rs | 16 | ||||
-rw-r--r-- | src/render.rs | 37 | ||||
-rw-r--r-- | src/useropts.rs | 142 |
3 files changed, 176 insertions, 19 deletions
diff --git a/src/main.rs b/src/main.rs index 767d5ee..d8e66a1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ extern crate num_traits; extern crate reqwest; extern crate rusttype; extern crate termcolor; +extern crate toml; extern crate xdg; use std::error::Error as StdError; @@ -32,6 +33,7 @@ mod bt; mod cache; mod output; mod render; +mod useropts; use clap::{App, Arg, ArgMatches}; @@ -346,6 +348,12 @@ fn run() -> MainResult<()> { .long("no-cache") .takes_value(false), ) + .arg( + Arg::with_name("config") + .help("Specifies the render option file.") + .long("config") + .takes_value(true), + ) .get_matches(); let mut api = if matches.is_present("no-cache") { @@ -365,7 +373,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(); diff --git a/src/render.rs b/src/render.rs index 7b796e1..9341848 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..2e98926 --- /dev/null +++ b/src/useropts.rs @@ -0,0 +1,142 @@ +//! 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 skill_size: Option<u32>, + pub traitline_brightness: Option<i32>, + pub traitline_use_gradient: Option<bool>, + pub traitline_gradient_size: Option<u32>, + pub traitline_x_offset: Option<u32>, + pub trait_size: Option<u32>, + pub line_color: Option<[u8; 4]>, + pub line_height: Option<u32>, + pub font_path: Option<String>, + pub text_color: Option<[u8; 4]>, + pub text_size: Option<u32>, + pub background_color: Option<[u8; 4]>, + pub render_specialization_names: Option<bool>, + pub skill_offset: Option<u32>, + 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(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)?; + result.font = font; + } + + 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, + } + + 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() +} |