use alloc::string::{String, ToString};
use crate::{
props::{
basic::{
length::{parse_float_value, FloatValue},
pixel::{
parse_pixel_value, CssPixelValueParseError, CssPixelValueParseErrorOwned,
PixelValue,
},
},
formatter::PrintAsCssValue,
},
shape::CssShape,
};
#[derive(Debug, Clone, PartialEq)]
#[repr(C, u8)]
pub enum ShapeOutside {
None,
Shape(CssShape),
}
impl Eq for ShapeOutside {}
impl core::hash::Hash for ShapeOutside {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
core::mem::discriminant(self).hash(state);
if let ShapeOutside::Shape(s) = self {
s.hash(state);
}
}
}
impl PartialOrd for ShapeOutside {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ShapeOutside {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
match (self, other) {
(ShapeOutside::None, ShapeOutside::None) => core::cmp::Ordering::Equal,
(ShapeOutside::None, ShapeOutside::Shape(_)) => core::cmp::Ordering::Less,
(ShapeOutside::Shape(_), ShapeOutside::None) => core::cmp::Ordering::Greater,
(ShapeOutside::Shape(a), ShapeOutside::Shape(b)) => a.cmp(b),
}
}
}
impl Default for ShapeOutside {
fn default() -> Self {
Self::None
}
}
impl PrintAsCssValue for ShapeOutside {
fn print_as_css_value(&self) -> String {
match self {
Self::None => "none".to_string(),
Self::Shape(shape) => format!("{:?}", shape), }
}
}
#[derive(Debug, Clone, PartialEq)]
#[repr(C, u8)]
pub enum ShapeInside {
None,
Shape(CssShape),
}
impl Eq for ShapeInside {}
impl core::hash::Hash for ShapeInside {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
core::mem::discriminant(self).hash(state);
if let ShapeInside::Shape(s) = self {
s.hash(state);
}
}
}
impl PartialOrd for ShapeInside {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ShapeInside {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
match (self, other) {
(ShapeInside::None, ShapeInside::None) => core::cmp::Ordering::Equal,
(ShapeInside::None, ShapeInside::Shape(_)) => core::cmp::Ordering::Less,
(ShapeInside::Shape(_), ShapeInside::None) => core::cmp::Ordering::Greater,
(ShapeInside::Shape(a), ShapeInside::Shape(b)) => a.cmp(b),
}
}
}
impl Default for ShapeInside {
fn default() -> Self {
Self::None
}
}
impl PrintAsCssValue for ShapeInside {
fn print_as_css_value(&self) -> String {
match self {
Self::None => "none".to_string(),
Self::Shape(shape) => format!("{:?}", shape), }
}
}
#[derive(Debug, Clone, PartialEq)]
#[repr(C, u8)]
pub enum ClipPath {
None,
Shape(CssShape),
}
impl Eq for ClipPath {}
impl core::hash::Hash for ClipPath {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
core::mem::discriminant(self).hash(state);
if let ClipPath::Shape(s) = self {
s.hash(state);
}
}
}
impl PartialOrd for ClipPath {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ClipPath {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
match (self, other) {
(ClipPath::None, ClipPath::None) => core::cmp::Ordering::Equal,
(ClipPath::None, ClipPath::Shape(_)) => core::cmp::Ordering::Less,
(ClipPath::Shape(_), ClipPath::None) => core::cmp::Ordering::Greater,
(ClipPath::Shape(a), ClipPath::Shape(b)) => a.cmp(b),
}
}
}
impl Default for ClipPath {
fn default() -> Self {
Self::None
}
}
impl PrintAsCssValue for ClipPath {
fn print_as_css_value(&self) -> String {
match self {
Self::None => "none".to_string(),
Self::Shape(shape) => format!("{:?}", shape), }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct ShapeMargin {
pub inner: PixelValue,
}
impl Default for ShapeMargin {
fn default() -> Self {
Self {
inner: PixelValue::zero(),
}
}
}
impl PrintAsCssValue for ShapeMargin {
fn print_as_css_value(&self) -> String {
self.inner.print_as_css_value()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct ShapeImageThreshold {
pub inner: FloatValue,
}
impl Default for ShapeImageThreshold {
fn default() -> Self {
Self {
inner: FloatValue::const_new(0),
}
}
}
impl PrintAsCssValue for ShapeImageThreshold {
fn print_as_css_value(&self) -> String {
self.inner.to_string()
}
}
impl crate::format_rust_code::FormatAsRustCode for ShapeOutside {
fn format_as_rust_code(&self, _tabs: usize) -> String {
match self {
ShapeOutside::None => String::from("ShapeOutside::None"),
ShapeOutside::Shape(_s) => String::from("ShapeOutside::Shape(/* ... */)"), }
}
}
impl crate::format_rust_code::FormatAsRustCode for ShapeInside {
fn format_as_rust_code(&self, _tabs: usize) -> String {
match self {
ShapeInside::None => String::from("ShapeInside::None"),
ShapeInside::Shape(_s) => String::from("ShapeInside::Shape(/* ... */)"), }
}
}
impl crate::format_rust_code::FormatAsRustCode for ClipPath {
fn format_as_rust_code(&self, _tabs: usize) -> String {
match self {
ClipPath::None => String::from("ClipPath::None"),
ClipPath::Shape(_s) => String::from("ClipPath::Shape(/* ... */)"), }
}
}
impl crate::format_rust_code::FormatAsRustCode for ShapeMargin {
fn format_as_rust_code(&self, _tabs: usize) -> String {
format!(
"ShapeMargin {{ inner: {} }}",
crate::format_rust_code::format_pixel_value(&self.inner)
)
}
}
impl crate::format_rust_code::FormatAsRustCode for ShapeImageThreshold {
fn format_as_rust_code(&self, _tabs: usize) -> String {
format!(
"ShapeImageThreshold {{ inner: {} }}",
crate::format_rust_code::format_float_value(&self.inner)
)
}
}
#[cfg(feature = "parser")]
mod parser {
use core::num::ParseFloatError;
use super::*;
use crate::shape_parser::{parse_shape, ShapeParseError};
pub fn parse_shape_outside(input: &str) -> Result<ShapeOutside, ShapeParseError> {
let trimmed = input.trim();
if trimmed == "none" {
Ok(ShapeOutside::None)
} else {
let shape = parse_shape(trimmed)?;
Ok(ShapeOutside::Shape(shape))
}
}
pub fn parse_shape_inside(input: &str) -> Result<ShapeInside, ShapeParseError> {
let trimmed = input.trim();
if trimmed == "none" {
Ok(ShapeInside::None)
} else {
let shape = parse_shape(trimmed)?;
Ok(ShapeInside::Shape(shape))
}
}
pub fn parse_clip_path(input: &str) -> Result<ClipPath, ShapeParseError> {
let trimmed = input.trim();
if trimmed == "none" {
Ok(ClipPath::None)
} else {
let shape = parse_shape(trimmed)?;
Ok(ClipPath::Shape(shape))
}
}
pub fn parse_shape_margin(input: &str) -> Result<ShapeMargin, CssPixelValueParseError> {
Ok(ShapeMargin {
inner: parse_pixel_value(input)?,
})
}
pub fn parse_shape_image_threshold(
input: &str,
) -> Result<ShapeImageThreshold, ParseFloatError> {
let val = parse_float_value(input)?;
let clamped = val.get().max(0.0).min(1.0);
Ok(ShapeImageThreshold {
inner: FloatValue::new(clamped),
})
}
}
#[cfg(feature = "parser")]
pub use parser::*;
#[cfg(all(test, feature = "parser"))]
mod tests {
use super::*;
#[test]
fn test_parse_shape_properties() {
assert!(matches!(
parse_shape_outside("none").unwrap(),
ShapeOutside::None
));
assert!(matches!(
parse_shape_outside("circle(50px)").unwrap(),
ShapeOutside::Shape(_)
));
assert!(matches!(
parse_shape_inside("none").unwrap(),
ShapeInside::None
));
assert!(matches!(
parse_shape_inside("circle(100px at 50px 50px)").unwrap(),
ShapeInside::Shape(_)
));
assert!(matches!(parse_clip_path("none").unwrap(), ClipPath::None));
assert!(matches!(
parse_clip_path("polygon(0 0, 100px 0, 100px 100px, 0 100px)").unwrap(),
ClipPath::Shape(_)
));
assert_eq!(
parse_shape_margin("10px").unwrap().inner,
PixelValue::px(10.0)
);
assert_eq!(parse_shape_image_threshold("0.5").unwrap().inner.get(), 0.5);
}
}