Skip to content

Commit

Permalink
Change Theme trait to return Result<> (#45)
Browse files Browse the repository at this point in the history
* Change Theme trait to return results

* Add ThemeError
  • Loading branch information
conways-glider committed Aug 1, 2024
1 parent bfa4ef0 commit 77a8c01
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 19 deletions.
1 change: 1 addition & 0 deletions examples/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use identicon_rs::error::IdenticonError;
use identicon_rs::theme::HSLRange;
use identicon_rs::Identicon;

fn main() -> Result<(), IdenticonError> {
Expand Down
24 changes: 22 additions & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use thiserror::Error;

/// Identicon errors.
use crate::theme;

/// Identicon errors
#[derive(Error, Debug)]
pub enum IdenticonError {
/// Failed to generate the image.
Expand Down Expand Up @@ -34,11 +36,15 @@ pub enum IdenticonError {
/// Currently set scale value.
scale: u32,
},

/// Indicates an issue with the provided theme.
#[error(transparent)]
ThemeError(#[from] theme::error::ThemeError),
}

#[cfg(test)]
mod tests {
use crate::error::IdenticonError;
use crate::{error::IdenticonError, theme::error::ThemeError};

#[test]
fn generate_image_error_works() {
Expand Down Expand Up @@ -76,4 +82,18 @@ mod tests {
"identicon size too large: 5, must be less or equal to identicon scale: 3";
assert_eq!(expected_text, error.to_string());
}

#[test]
fn theme_error_works() {
let theme_error = ThemeError::ThemeProcessingError("bad field".to_string());
let identicon_error: IdenticonError = theme_error.into();
match identicon_error {
IdenticonError::ThemeError(inner_error) => match inner_error {
ThemeError::ThemeProcessingError(_) => assert!(true),
_ => assert!(false, "wrong inner error type"),
},
_ => assert!(false, "wrong error type"),
}
// assert_eq!(theme_error, identicon_error);
}
}
8 changes: 5 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,8 @@ impl Identicon {
let grid = grid::generate_full_grid(self.size, &self.hash);

// Create pixel objects
let color_active = self.theme.main_color(&self.hash);
let color_background = self.theme.background_color(&self.hash);
let color_active = self.theme.main_color(&self.hash)?;
let color_background = self.theme.background_color(&self.hash)?;
let pixel_active = image::Rgb([color_active.red, color_active.green, color_active.blue]);
let pixel_background = image::Rgb([
color_background.red,
Expand Down Expand Up @@ -312,7 +312,9 @@ mod tests {

let image = Identicon::new("test");
let grid = crate::grid::generate_full_grid(image.size, &image.hash);
let color = crate::theme::default_theme().main_color(&image.hash);
let color = crate::theme::default_theme()
.main_color(&image.hash)
.expect("could not get color");

assert_eq!(expected_color, color);

Expand Down
15 changes: 15 additions & 0 deletions src/theme/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use thiserror::Error;

/// Theme Errors
///
/// These are provided for writing new types of themes using the Theme trait.
#[derive(Error, Debug)]
pub enum ThemeError {
/// Theme failed validation
#[error("theme validation failed: {0}")]
ThemeValidationError(String),

/// Theme failed to generate a color
#[error("theme processing failed: {0}")]
ThemeProcessingError(String),
}
76 changes: 62 additions & 14 deletions src/theme.rs → src/theme/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
use error::ThemeError;

use crate::{color::RGB, map_values::map_values};

/// Theme Errors
///
/// Identicon Errors can wrap these errors
pub mod error;

/// Trait defining requirements for an identicon theme
pub trait Theme {
/// This should return the main color within the identicon image
fn main_color(&self, hash: &[u8]) -> RGB;
fn main_color(&self, hash: &[u8]) -> Result<RGB, ThemeError>;

/// This should return the background color within the identicon image
fn background_color(&self, hash: &[u8]) -> RGB;
fn background_color(&self, hash: &[u8]) -> Result<RGB, ThemeError>;
}

/// Simple selection theme struct
Expand All @@ -27,14 +34,26 @@ pub struct Selection {
}

impl Theme for Selection {
fn main_color(&self, hash: &[u8]) -> RGB {
let index = hash[0 % hash.len()] as usize % self.main.len();
self.main[index]
fn main_color(&self, hash: &[u8]) -> Result<RGB, ThemeError> {
if self.main.is_empty() {
Err(ThemeError::ThemeValidationError(
"main color selection is empty".to_string(),
))
} else {
let index = hash[0 % hash.len()] as usize % self.main.len();
Ok(self.main[index])
}
}

fn background_color(&self, hash: &[u8]) -> RGB {
let index = hash[2 % hash.len()] as usize % self.background.len();
self.background[index]
fn background_color(&self, hash: &[u8]) -> Result<RGB, ThemeError> {
if self.background.is_empty() {
Err(ThemeError::ThemeValidationError(
"background color selection is empty".to_string(),
))
} else {
let index = hash[2 % hash.len()] as usize % self.background.len();
Ok(self.background[index])
}
}
}

Expand Down Expand Up @@ -79,8 +98,31 @@ pub struct HSLRange {
background: Vec<RGB>,
}

impl HSLRange {
fn validate(&self) -> Result<(), ThemeError> {
if self.hue_max < self.hue_min {
Err(ThemeError::ThemeValidationError(
"hue_max must be larger than hue_min".to_string(),
))
} else if self.saturation_max < self.saturation_min {
Err(ThemeError::ThemeValidationError(
"saturation_max must be larger than saturation_min".to_string(),
))
} else if self.lightness_max < self.lightness_min {
Err(ThemeError::ThemeValidationError(
"lightness_max must be larger than lightness_min".to_string(),
))
} else {
Ok(())
}
}
}

impl Theme for HSLRange {
fn main_color(&self, hash: &[u8]) -> RGB {
fn main_color(&self, hash: &[u8]) -> Result<RGB, ThemeError> {
// Validate the fields
self.validate()?;

// Compute hash for hue space in larger bitspace
let hue_hash = ((hash[0 % hash.len()] as u16) << 8) | hash[1 % hash.len()] as u16;

Expand Down Expand Up @@ -138,16 +180,22 @@ impl Theme for HSLRange {
let green = (g_prime + m) * 255.0;
let blue = (b_prime + m) * 255.0;

RGB {
Ok(RGB {
red: red as u8,
green: green as u8,
blue: blue as u8,
}
})
}

fn background_color(&self, hash: &[u8]) -> RGB {
let index = hash[2 % hash.len()] as usize % self.background.len();
self.background[index]
fn background_color(&self, hash: &[u8]) -> Result<RGB, ThemeError> {
if self.background.is_empty() {
Err(ThemeError::ThemeValidationError(
"background color selection is empty".to_string(),
))
} else {
let index = hash[2 % hash.len()] as usize % self.background.len();
Ok(self.background[index])
}
}
}

Expand Down

0 comments on commit 77a8c01

Please sign in to comment.