use crate::errors::OliframeError;
use derive_getters::Getters;
use std::str::FromStr;
use strum::IntoEnumIterator;
use strum_macros::{AsRefStr, EnumIter, EnumString};
#[derive(Debug, Default, Getters, PartialEq)]
pub struct RelativePosition {
horizontal: HorizontalPosition,
vertical: VerticalPosition,
}
impl FromStr for RelativePosition {
type Err = OliframeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let horizontal = extract_position(s).ok_or(OliframeError::InvalidInput(
"Please provide only one horizontal position.".to_string(),
))?;
let vertical = extract_position(s).ok_or(OliframeError::InvalidInput(
"Please provide only one vertical position.".to_string(),
))?;
Ok(Self {
horizontal,
vertical,
})
}
}
fn extract_position<P>(s: &str) -> Option<P>
where
P: FromStr + IntoEnumIterator + AsRef<str> + Default + PartialEq,
P::Err: std::fmt::Debug,
{
P::iter()
.filter(|pos| s.contains(pos.as_ref()))
.try_fold(P::default(), |acc, pos| match (acc, pos) {
(acc, pos) if acc == P::default() => Ok(pos),
(acc, pos) if pos == P::default() => Ok(acc),
_ => Err(()),
})
.ok()
}
#[derive(AsRefStr, Debug, Default, EnumIter, EnumString, PartialEq)]
#[strum(serialize_all = "snake_case")]
pub enum HorizontalPosition {
Left,
#[default]
Center,
Right,
}
#[derive(AsRefStr, Debug, Default, EnumIter, EnumString, PartialEq)]
#[strum(serialize_all = "snake_case")]
pub enum VerticalPosition {
Top,
#[default]
Center,
Bottom,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_horizontal_position_from_string() {
let position = "left".parse::<HorizontalPosition>().unwrap();
assert_eq!(position, HorizontalPosition::Left);
let position = "center".parse::<HorizontalPosition>().unwrap();
assert_eq!(position, HorizontalPosition::Center);
let position = "right".parse::<HorizontalPosition>().unwrap();
assert_eq!(position, HorizontalPosition::Right);
}
#[test]
fn parse_vertical_position_from_string() {
let position = "top".parse::<VerticalPosition>().unwrap();
assert_eq!(position, VerticalPosition::Top);
let position = "center".parse::<VerticalPosition>().unwrap();
assert_eq!(position, VerticalPosition::Center);
let position = "bottom".parse::<VerticalPosition>().unwrap();
assert_eq!(position, VerticalPosition::Bottom);
}
#[test]
fn parse_relative_position_from_string() {
let position = "left,top".parse::<RelativePosition>().unwrap();
assert_eq!(position.horizontal, HorizontalPosition::Left);
assert_eq!(position.vertical, VerticalPosition::Top);
let position = "center,bottom".parse::<RelativePosition>().unwrap();
assert_eq!(position.horizontal, HorizontalPosition::Center);
assert_eq!(position.vertical, VerticalPosition::Bottom);
let position = "right,center".parse::<RelativePosition>().unwrap();
assert_eq!(position.horizontal, HorizontalPosition::Right);
assert_eq!(position.vertical, VerticalPosition::Center);
let position = "center,center".parse::<RelativePosition>().unwrap();
assert_eq!(position.horizontal, HorizontalPosition::Center);
assert_eq!(position.vertical, VerticalPosition::Center);
let position = "center".parse::<RelativePosition>().unwrap();
assert_eq!(position.horizontal, HorizontalPosition::Center);
assert_eq!(position.vertical, VerticalPosition::Center);
}
#[test]
fn parse_invalid_relative_position_from_string() {
let position = "left,right,bottom".parse::<RelativePosition>();
assert!(position.is_err());
assert_eq!(
position.unwrap_err().to_string(),
"Invalid input: Please provide only one horizontal position."
);
let position = "center,bottom,top".parse::<RelativePosition>();
assert!(position.is_err());
assert_eq!(
position.unwrap_err().to_string(),
"Invalid input: Please provide only one vertical position."
);
}
}