aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaniel Schadt <kingdread@gmx.de>2019-12-14 03:23:46 +0100
committerDaniel Schadt <kingdread@gmx.de>2019-12-14 03:23:46 +0100
commit97f2678563e6ca997ea33d7befe38ab2acfa01a9 (patch)
treed0ee757fcc01555362ec4578dafbf946034c0e64 /src
parent2aef95f62788163b789fae92b5a6546b2413d9e4 (diff)
downloadkondou-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.rs18
-rw-r--r--src/render.rs37
-rw-r--r--src/useropts.rs127
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()
+}