use crate::{
prelude::{FromLength, TaffyMaxContent, TaffyMinContent, TaffyZero},
sys::abs,
Size,
};
#[cfg(feature = "parse")]
use crate::util::parse::{from_str_from_css, parse_css_str_entirely, CssParseResult, FromCss, Parser, Token};
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum AvailableSpace {
Definite(f32),
MinContent,
MaxContent,
}
impl TaffyZero for AvailableSpace {
const ZERO: Self = Self::Definite(0.0);
}
impl TaffyMaxContent for AvailableSpace {
const MAX_CONTENT: Self = Self::MaxContent;
}
impl TaffyMinContent for AvailableSpace {
const MIN_CONTENT: Self = Self::MinContent;
}
impl FromLength for AvailableSpace {
fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
Self::Definite(value.into())
}
}
#[cfg(feature = "parse")]
impl FromCss for AvailableSpace {
fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> {
match parser.next()?.clone() {
Token::Number { value, .. } if value >= 0.0 => Ok(Self::Definite(value)),
Token::Dimension { value, .. } if value >= 0.0 => Ok(Self::Definite(value)),
Token::Ident(ident) if ident == "max-content" => Ok(Self::MaxContent),
Token::Ident(ident) if ident == "min-content" => Ok(Self::MinContent),
token => Err(parser.new_unexpected_token_error(token))?,
}
}
}
#[cfg(feature = "parse")]
from_str_from_css!(AvailableSpace);
impl AvailableSpace {
pub const fn is_definite(self) -> bool {
matches!(self, AvailableSpace::Definite(_))
}
pub const fn into_option(self) -> Option<f32> {
match self {
AvailableSpace::Definite(value) => Some(value),
_ => None,
}
}
pub fn unwrap_or(self, default: f32) -> f32 {
self.into_option().unwrap_or(default)
}
#[track_caller]
pub fn unwrap(self) -> f32 {
self.into_option().unwrap()
}
pub fn or(self, default: AvailableSpace) -> AvailableSpace {
match self {
AvailableSpace::Definite(_) => self,
_ => default,
}
}
pub fn or_else(self, default_cb: impl FnOnce() -> AvailableSpace) -> AvailableSpace {
match self {
AvailableSpace::Definite(_) => self,
_ => default_cb(),
}
}
pub fn unwrap_or_else(self, default_cb: impl FnOnce() -> f32) -> f32 {
self.into_option().unwrap_or_else(default_cb)
}
pub fn maybe_set(self, value: Option<f32>) -> AvailableSpace {
match value {
Some(value) => AvailableSpace::Definite(value),
None => self,
}
}
pub fn map_definite_value(self, map_function: impl FnOnce(f32) -> f32) -> AvailableSpace {
match self {
AvailableSpace::Definite(value) => AvailableSpace::Definite(map_function(value)),
_ => self,
}
}
pub fn compute_free_space(&self, used_space: f32) -> f32 {
match self {
AvailableSpace::MaxContent => f32::INFINITY,
AvailableSpace::MinContent => 0.0,
AvailableSpace::Definite(available_space) => available_space - used_space,
}
}
pub fn is_roughly_equal(self, other: AvailableSpace) -> bool {
use AvailableSpace::*;
match (self, other) {
(Definite(a), Definite(b)) => abs(a - b) < f32::EPSILON,
(MinContent, MinContent) => true,
(MaxContent, MaxContent) => true,
_ => false,
}
}
}
impl From<f32> for AvailableSpace {
fn from(value: f32) -> Self {
Self::Definite(value)
}
}
impl From<Option<f32>> for AvailableSpace {
fn from(option: Option<f32>) -> Self {
match option {
Some(value) => Self::Definite(value),
None => Self::MaxContent,
}
}
}
impl Size<AvailableSpace> {
pub fn into_options(self) -> Size<Option<f32>> {
Size { width: self.width.into_option(), height: self.height.into_option() }
}
pub fn maybe_set(self, value: Size<Option<f32>>) -> Size<AvailableSpace> {
Size { width: self.width.maybe_set(value.width), height: self.height.maybe_set(value.height) }
}
}