use crate::{Colors, Config, Countable, DimensionError, WallSwitchError, WallSwitchResult};
use serde::{Deserialize, Serialize};
use std::{fmt, num::ParseIntError};
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub struct Dimension {
pub width: u64,
pub height: u64,
}
impl Default for Dimension {
fn default() -> Self {
Dimension {
width: 3840,
height: 2160,
}
}
}
impl Dimension {
pub fn new(string: &str) -> WallSwitchResult<Dimension> {
let numbers: Vec<u64> = split_str(string)?;
let (width, height) = (numbers[0], numbers[1]);
Ok(Dimension { width, height })
}
pub fn minimum(&self) -> u64 {
self.height.min(self.width)
}
pub fn maximum(&self) -> u64 {
self.height.max(self.width)
}
pub fn is_valid(&self, config: &Config) -> bool {
config.in_range(self.minimum()) && config.in_range(self.maximum())
}
pub fn get_log_min(&self, config: &Config) -> String {
let num = self.maximum().count_chars();
let min = self.minimum();
let min = format!("{min:>num$}");
if !config.in_range(self.minimum()) {
format!(
"Minimum dimension: {min}. The condition ({config_min} <= {min} <= {config_max}) is false.\n",
min = min.yellow(),
config_min = config.min_dimension.green(),
config_max = config.max_dimension.green(),
)
} else {
"".to_string()
}
}
pub fn get_log_max(&self, config: &Config) -> String {
if !config.in_range(self.maximum()) {
format!(
"Maximum dimension: {max}. The condition ({config_min} <= {max} <= {config_max}) is false.\n",
max = self.maximum().yellow(),
config_min = config.min_dimension.green(),
config_max = config.max_dimension.green(),
)
} else {
"".to_string()
}
}
}
impl fmt::Display for Dimension {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let height = self.height;
let width = self.width;
let max = height.max(width);
let n = max.count_chars();
write!(
f,
"Dimension {{ height: {height:>n$}, width: {width:>n$} }}",
)
}
}
pub fn split_str(string: &str) -> WallSwitchResult<Vec<u64>> {
let numbers: Vec<u64> = string
.split('x')
.map(|s| s.trim().parse::<u64>())
.collect::<Result<Vec<u64>, ParseIntError>>()
.map_err(|parse_error| {
WallSwitchError::InvalidDimension(DimensionError::InvalidParse(
string.to_string(),
parse_error,
))
})?;
if numbers.len() != 2 {
return Err(WallSwitchError::InvalidDimension(
DimensionError::InvalidFormat,
));
}
if numbers.contains(&0) {
return Err(WallSwitchError::InvalidDimension(
DimensionError::ZeroDimension,
));
}
Ok(numbers)
}
#[cfg(test)]
mod test_dimension {
use super::*;
#[test]
fn split_str_sample_1() -> WallSwitchResult<()> {
let string = " 123 x 4567 ";
let numbers = split_str(string)?;
assert_eq!(numbers[0], 123);
assert_eq!(numbers[1], 4567);
Ok(())
}
#[test]
fn split_str_sample_2() {
let string = "x4567";
let result = split_str(string);
dbg!(&result);
assert!(
result.is_err(),
"The result should be an Err variant for input: {}",
string
);
let err = result.expect_err("Failure expected for empty width component");
assert!(
matches!(
&err,
WallSwitchError::InvalidDimension(DimensionError::InvalidParse(s, _)) if s == string
),
"Expected InvalidDimension(InvalidParse) for '{}', but got: {:?}",
string,
err
);
let expected_msg = format!(
"Invalid dimension format '{string}': failed to parse integer - cannot parse integer from empty string"
);
assert_eq!(err.to_string(), expected_msg);
}
#[test]
fn split_str_sample_3() {
let string = "12ab3x4567";
let parse_error_reason = "invalid digit found in string";
let result = split_str(string);
let err = result.expect_err("Parsing should fail when non-numeric characters are present");
assert!(
matches!(
&err,
WallSwitchError::InvalidDimension(DimensionError::InvalidParse(s, parse_err))
if s == string && parse_err.to_string() == parse_error_reason
),
"Expected InvalidDimension(InvalidParse) for input '{}' with reason '{}', but got: {:?}",
string,
parse_error_reason,
err
);
let expected_msg = format!(
"Invalid dimension format '{string}': failed to parse integer - {parse_error_reason}"
);
assert!(
err.to_string().contains(&expected_msg),
"The error message should contain the formatted reason.\nExpected: {}\nActual: {}",
expected_msg,
err
);
}
#[test]
fn split_str_sample_4() {
let string = "57x124x89";
let result = split_str(string);
dbg!(&result);
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
"Invalid dimension format: expected two numbers (width x height)"
);
}
#[test]
fn split_str_sample_5() {
let string = "57x0";
let result = split_str(string);
dbg!(&result);
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
"Zero is not a valid dimension component"
);
}
}