aboutsummaryrefslogtreecommitdiff
path: root/src/useropts.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/useropts.rs')
-rw-r--r--src/useropts.rs142
1 files changed, 142 insertions, 0 deletions
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()
+}