use alloc::vec::Vec;
use core::fmt;
use strum::EnumIs;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, EnumIs)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Constraint {
Min(u16),
Max(u16),
Length(u16),
Percentage(u16),
Ratio(u32, u32),
Fill(u16),
}
impl Constraint {
#[deprecated(
since = "0.26.0",
note = "This field will be hidden in the next minor version."
)]
pub fn apply(&self, length: u16) -> u16 {
match *self {
Self::Percentage(p) => {
let p = f32::from(p) / 100.0;
let length = f32::from(length);
(p * length).min(length) as u16
}
Self::Ratio(numerator, denominator) => {
let percentage = numerator as f32 / denominator.max(1) as f32;
let length = f32::from(length);
(percentage * length).min(length) as u16
}
Self::Length(l) | Self::Fill(l) => length.min(l),
Self::Max(m) => length.min(m),
Self::Min(m) => length.max(m),
}
}
pub fn from_lengths<T>(lengths: T) -> Vec<Self>
where
T: IntoIterator<Item = u16>,
{
lengths.into_iter().map(Self::Length).collect()
}
pub fn from_ratios<T>(ratios: T) -> Vec<Self>
where
T: IntoIterator<Item = (u32, u32)>,
{
ratios.into_iter().map(|(n, d)| Self::Ratio(n, d)).collect()
}
pub fn from_percentages<T>(percentages: T) -> Vec<Self>
where
T: IntoIterator<Item = u16>,
{
percentages.into_iter().map(Self::Percentage).collect()
}
pub fn from_maxes<T>(maxes: T) -> Vec<Self>
where
T: IntoIterator<Item = u16>,
{
maxes.into_iter().map(Self::Max).collect()
}
pub fn from_mins<T>(mins: T) -> Vec<Self>
where
T: IntoIterator<Item = u16>,
{
mins.into_iter().map(Self::Min).collect()
}
pub fn from_fills<T>(proportional_factors: T) -> Vec<Self>
where
T: IntoIterator<Item = u16>,
{
proportional_factors.into_iter().map(Self::Fill).collect()
}
}
impl From<u16> for Constraint {
fn from(length: u16) -> Self {
Self::Length(length)
}
}
impl From<&Self> for Constraint {
fn from(constraint: &Self) -> Self {
*constraint
}
}
impl AsRef<Self> for Constraint {
fn as_ref(&self) -> &Self {
self
}
}
impl Default for Constraint {
fn default() -> Self {
Self::Percentage(100)
}
}
impl fmt::Display for Constraint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Percentage(p) => write!(f, "Percentage({p})"),
Self::Ratio(n, d) => write!(f, "Ratio({n}, {d})"),
Self::Length(l) => write!(f, "Length({l})"),
Self::Fill(l) => write!(f, "Fill({l})"),
Self::Max(m) => write!(f, "Max({m})"),
Self::Min(m) => write!(f, "Min({m})"),
}
}
}
#[cfg(test)]
mod tests {
use alloc::string::ToString;
use alloc::vec;
use super::*;
#[test]
fn default() {
assert_eq!(Constraint::default(), Constraint::Percentage(100));
}
#[test]
fn to_string() {
assert_eq!(Constraint::Percentage(50).to_string(), "Percentage(50)");
assert_eq!(Constraint::Ratio(1, 2).to_string(), "Ratio(1, 2)");
assert_eq!(Constraint::Length(10).to_string(), "Length(10)");
assert_eq!(Constraint::Max(10).to_string(), "Max(10)");
assert_eq!(Constraint::Min(10).to_string(), "Min(10)");
}
#[test]
fn from_lengths() {
let expected = [
Constraint::Length(1),
Constraint::Length(2),
Constraint::Length(3),
];
assert_eq!(Constraint::from_lengths([1, 2, 3]), expected);
assert_eq!(Constraint::from_lengths(vec![1, 2, 3]), expected);
}
#[test]
fn from_ratios() {
let expected = [
Constraint::Ratio(1, 4),
Constraint::Ratio(1, 2),
Constraint::Ratio(1, 4),
];
assert_eq!(Constraint::from_ratios([(1, 4), (1, 2), (1, 4)]), expected);
assert_eq!(
Constraint::from_ratios(vec![(1, 4), (1, 2), (1, 4)]),
expected
);
}
#[test]
fn from_percentages() {
let expected = [
Constraint::Percentage(25),
Constraint::Percentage(50),
Constraint::Percentage(25),
];
assert_eq!(Constraint::from_percentages([25, 50, 25]), expected);
assert_eq!(Constraint::from_percentages(vec![25, 50, 25]), expected);
}
#[test]
fn from_maxes() {
let expected = [Constraint::Max(1), Constraint::Max(2), Constraint::Max(3)];
assert_eq!(Constraint::from_maxes([1, 2, 3]), expected);
assert_eq!(Constraint::from_maxes(vec![1, 2, 3]), expected);
}
#[test]
fn from_mins() {
let expected = [Constraint::Min(1), Constraint::Min(2), Constraint::Min(3)];
assert_eq!(Constraint::from_mins([1, 2, 3]), expected);
assert_eq!(Constraint::from_mins(vec![1, 2, 3]), expected);
}
#[test]
fn from_fills() {
let expected = [
Constraint::Fill(1),
Constraint::Fill(2),
Constraint::Fill(3),
];
assert_eq!(Constraint::from_fills([1, 2, 3]), expected);
assert_eq!(Constraint::from_fills(vec![1, 2, 3]), expected);
}
#[test]
#[expect(deprecated)]
fn apply() {
assert_eq!(Constraint::Percentage(0).apply(100), 0);
assert_eq!(Constraint::Percentage(50).apply(100), 50);
assert_eq!(Constraint::Percentage(100).apply(100), 100);
assert_eq!(Constraint::Percentage(200).apply(100), 100);
assert_eq!(Constraint::Percentage(u16::MAX).apply(100), 100);
assert_eq!(Constraint::Ratio(0, 0).apply(100), 0);
assert_eq!(Constraint::Ratio(1, 0).apply(100), 100);
assert_eq!(Constraint::Ratio(0, 1).apply(100), 0);
assert_eq!(Constraint::Ratio(1, 2).apply(100), 50);
assert_eq!(Constraint::Ratio(2, 2).apply(100), 100);
assert_eq!(Constraint::Ratio(3, 2).apply(100), 100);
assert_eq!(Constraint::Ratio(u32::MAX, 2).apply(100), 100);
assert_eq!(Constraint::Length(0).apply(100), 0);
assert_eq!(Constraint::Length(50).apply(100), 50);
assert_eq!(Constraint::Length(100).apply(100), 100);
assert_eq!(Constraint::Length(200).apply(100), 100);
assert_eq!(Constraint::Length(u16::MAX).apply(100), 100);
assert_eq!(Constraint::Max(0).apply(100), 0);
assert_eq!(Constraint::Max(50).apply(100), 50);
assert_eq!(Constraint::Max(100).apply(100), 100);
assert_eq!(Constraint::Max(200).apply(100), 100);
assert_eq!(Constraint::Max(u16::MAX).apply(100), 100);
assert_eq!(Constraint::Min(0).apply(100), 100);
assert_eq!(Constraint::Min(50).apply(100), 100);
assert_eq!(Constraint::Min(100).apply(100), 100);
assert_eq!(Constraint::Min(200).apply(100), 200);
assert_eq!(Constraint::Min(u16::MAX).apply(100), u16::MAX);
}
}