aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.rs16
-rw-r--r--src/render.rs37
-rw-r--r--src/useropts.rs142
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()
+}