1use core::fmt;
2use std::num::ParseFloatError;
3
4use crate::props::{
5 basic::{FloatValue, SizeMetric},
6 formatter::FormatAsCssValue,
7};
8
9pub const DEFAULT_FONT_SIZE: f32 = 16.0;
25
26pub const PT_TO_PX: f32 = 96.0 / 72.0;
28
29#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
35#[repr(transparent)]
36pub struct NormalizedPercentage(f32);
37
38impl NormalizedPercentage {
39 #[inline]
44 pub const fn new(value: f32) -> Self {
45 Self(value)
46 }
47
48 #[inline]
53 pub fn from_unnormalized(value: f32) -> Self {
54 Self(value / 100.0)
55 }
56
57 #[inline]
59 pub const fn get(self) -> f32 {
60 self.0
61 }
62
63 #[inline]
68 pub fn resolve(self, containing_block_size: f32) -> f32 {
69 self.0 * containing_block_size
70 }
71}
72
73impl fmt::Display for NormalizedPercentage {
74 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75 write!(f, "{}%", self.0 * 100.0)
76 }
77}
78
79#[derive(Debug, Copy, Clone, PartialEq)]
81#[repr(C)]
82pub struct CssLogicalSize {
83 pub inline_size: f32,
85 pub block_size: f32,
87}
88
89impl CssLogicalSize {
90 #[inline]
91 pub const fn new(inline_size: f32, block_size: f32) -> Self {
92 Self {
93 inline_size,
94 block_size,
95 }
96 }
97
98 #[inline]
100 pub const fn to_physical(self) -> PhysicalSize {
101 PhysicalSize {
102 width: self.inline_size,
103 height: self.block_size,
104 }
105 }
106}
107
108#[derive(Debug, Copy, Clone, PartialEq)]
110#[repr(C)]
111pub struct PhysicalSize {
112 pub width: f32,
113 pub height: f32,
114}
115
116impl PhysicalSize {
117 #[inline]
118 pub const fn new(width: f32, height: f32) -> Self {
119 Self { width, height }
120 }
121
122 #[inline]
124 pub const fn to_logical(self) -> CssLogicalSize {
125 CssLogicalSize {
126 inline_size: self.width,
127 block_size: self.height,
128 }
129 }
130}
131
132#[derive(Debug, Copy, Clone)]
148pub struct ResolutionContext {
149 pub element_font_size: f32,
151
152 pub parent_font_size: f32,
154
155 pub root_font_size: f32,
157
158 pub containing_block_size: PhysicalSize,
160
161 pub element_size: Option<PhysicalSize>,
164
165 pub viewport_size: PhysicalSize,
168}
169
170impl Default for ResolutionContext {
171 fn default() -> Self {
172 Self {
173 element_font_size: 16.0,
174 parent_font_size: 16.0,
175 root_font_size: 16.0,
176 containing_block_size: PhysicalSize::new(0.0, 0.0),
177 element_size: None,
178 viewport_size: PhysicalSize::new(0.0, 0.0),
179 }
180 }
181}
182
183impl ResolutionContext {
184 #[inline]
186 pub const fn default_const() -> Self {
187 Self {
188 element_font_size: 16.0,
189 parent_font_size: 16.0,
190 root_font_size: 16.0,
191 containing_block_size: PhysicalSize {
192 width: 0.0,
193 height: 0.0,
194 },
195 element_size: None,
196 viewport_size: PhysicalSize {
197 width: 0.0,
198 height: 0.0,
199 },
200 }
201 }
202
203 #[inline]
205 pub const fn for_fonts(
206 element_font_size: f32,
207 parent_font_size: f32,
208 root_font_size: f32,
209 ) -> Self {
210 Self {
211 element_font_size,
212 parent_font_size,
213 root_font_size,
214 containing_block_size: PhysicalSize {
215 width: 0.0,
216 height: 0.0,
217 },
218 element_size: None,
219 viewport_size: PhysicalSize {
220 width: 0.0,
221 height: 0.0,
222 },
223 }
224 }
225
226 #[inline]
228 pub const fn with_containing_block(mut self, containing_block_size: PhysicalSize) -> Self {
229 self.containing_block_size = containing_block_size;
230 self
231 }
232
233 #[inline]
235 pub const fn with_element_size(mut self, element_size: PhysicalSize) -> Self {
236 self.element_size = Some(element_size);
237 self
238 }
239
240 #[inline]
242 pub const fn with_viewport_size(mut self, viewport_size: PhysicalSize) -> Self {
243 self.viewport_size = viewport_size;
244 self
245 }
246}
247
248#[derive(Debug, Copy, Clone, PartialEq, Eq)]
250pub enum PropertyContext {
251 FontSize,
253 Margin,
255 Padding,
257 Width,
259 Height,
261 BorderWidth,
263 BorderRadius,
265 Transform,
267 Other,
269}
270
271#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
272#[repr(C)]
273pub struct PixelValue {
274 pub metric: SizeMetric,
275 pub number: FloatValue,
276}
277
278impl PixelValue {
279 pub fn scale_for_dpi(&mut self, scale_factor: f32) {
280 self.number = FloatValue::new(self.number.get() * scale_factor);
281 }
282}
283
284impl FormatAsCssValue for PixelValue {
285 fn format_as_css_value(&self, f: &mut fmt::Formatter) -> fmt::Result {
286 write!(f, "{}{}", self.number, self.metric)
287 }
288}
289
290impl crate::css::PrintAsCssValue for PixelValue {
291 fn print_as_css_value(&self) -> String {
292 format!("{}{}", self.number, self.metric)
293 }
294}
295
296impl crate::format_rust_code::FormatAsRustCode for PixelValue {
297 fn format_as_rust_code(&self, _tabs: usize) -> String {
298 format!(
299 "PixelValue {{ metric: {:?}, number: FloatValue::new({}) }}",
300 self.metric,
301 self.number.get()
302 )
303 }
304}
305
306impl fmt::Debug for PixelValue {
307 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
308 write!(f, "{}{}", self.number, self.metric)
309 }
310}
311
312impl fmt::Display for PixelValue {
314 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
315 write!(f, "{}{}", self.number, self.metric)
316 }
317}
318
319impl PixelValue {
320 #[inline]
321 pub const fn zero() -> Self {
322 const ZERO_PX: PixelValue = PixelValue::const_px(0);
323 ZERO_PX
324 }
325
326 #[inline]
329 pub const fn const_px(value: isize) -> Self {
330 Self::const_from_metric(SizeMetric::Px, value)
331 }
332
333 #[inline]
336 pub const fn const_em(value: isize) -> Self {
337 Self::const_from_metric(SizeMetric::Em, value)
338 }
339
340 #[inline]
353 pub const fn const_em_fractional(pre_comma: isize, post_comma: isize) -> Self {
354 Self::const_from_metric_fractional(SizeMetric::Em, pre_comma, post_comma)
355 }
356
357 #[inline]
360 pub const fn const_pt(value: isize) -> Self {
361 Self::const_from_metric(SizeMetric::Pt, value)
362 }
363
364 #[inline]
366 pub const fn const_pt_fractional(pre_comma: isize, post_comma: isize) -> Self {
367 Self::const_from_metric_fractional(SizeMetric::Pt, pre_comma, post_comma)
368 }
369
370 #[inline]
373 pub const fn const_percent(value: isize) -> Self {
374 Self::const_from_metric(SizeMetric::Percent, value)
375 }
376
377 #[inline]
380 pub const fn const_in(value: isize) -> Self {
381 Self::const_from_metric(SizeMetric::In, value)
382 }
383
384 #[inline]
387 pub const fn const_cm(value: isize) -> Self {
388 Self::const_from_metric(SizeMetric::Cm, value)
389 }
390
391 #[inline]
394 pub const fn const_mm(value: isize) -> Self {
395 Self::const_from_metric(SizeMetric::Mm, value)
396 }
397
398 #[inline]
399 pub const fn const_from_metric(metric: SizeMetric, value: isize) -> Self {
400 Self {
401 metric,
402 number: FloatValue::const_new(value),
403 }
404 }
405
406 #[inline]
413 pub const fn const_from_metric_fractional(
414 metric: SizeMetric,
415 pre_comma: isize,
416 post_comma: isize,
417 ) -> Self {
418 Self {
419 metric,
420 number: FloatValue::const_new_fractional(pre_comma, post_comma),
421 }
422 }
423
424 #[inline]
425 pub fn px(value: f32) -> Self {
426 Self::from_metric(SizeMetric::Px, value)
427 }
428
429 #[inline]
430 pub fn em(value: f32) -> Self {
431 Self::from_metric(SizeMetric::Em, value)
432 }
433
434 #[inline]
435 pub fn inch(value: f32) -> Self {
436 Self::from_metric(SizeMetric::In, value)
437 }
438
439 #[inline]
440 pub fn cm(value: f32) -> Self {
441 Self::from_metric(SizeMetric::Cm, value)
442 }
443
444 #[inline]
445 pub fn mm(value: f32) -> Self {
446 Self::from_metric(SizeMetric::Mm, value)
447 }
448
449 #[inline]
450 pub fn pt(value: f32) -> Self {
451 Self::from_metric(SizeMetric::Pt, value)
452 }
453
454 #[inline]
455 pub fn percent(value: f32) -> Self {
456 Self::from_metric(SizeMetric::Percent, value)
457 }
458
459 #[inline]
460 pub fn rem(value: f32) -> Self {
461 Self::from_metric(SizeMetric::Rem, value)
462 }
463
464 #[inline]
465 pub fn from_metric(metric: SizeMetric, value: f32) -> Self {
466 Self {
467 metric,
468 number: FloatValue::new(value),
469 }
470 }
471
472 #[inline]
473 pub fn interpolate(&self, other: &Self, t: f32) -> Self {
474 if self.metric == other.metric {
475 Self {
476 metric: self.metric,
477 number: self.number.interpolate(&other.number, t),
478 }
479 } else {
480 let self_px_interp = self.to_pixels_internal(0.0, DEFAULT_FONT_SIZE);
483 let other_px_interp = other.to_pixels_internal(0.0, DEFAULT_FONT_SIZE);
484 Self::from_metric(
485 SizeMetric::Px,
486 self_px_interp + (other_px_interp - self_px_interp) * t,
487 )
488 }
489 }
490
491 #[inline]
497 pub fn to_percent(&self) -> Option<NormalizedPercentage> {
498 match self.metric {
499 SizeMetric::Percent => Some(NormalizedPercentage::from_unnormalized(self.number.get())),
500 _ => None,
501 }
502 }
503
504 #[doc(hidden)]
510 #[inline]
511 pub fn to_pixels_internal(&self, percent_resolve: f32, em_resolve: f32) -> f32 {
512 match self.metric {
513 SizeMetric::Px => self.number.get(),
514 SizeMetric::Pt => self.number.get() * PT_TO_PX,
515 SizeMetric::In => self.number.get() * 96.0,
516 SizeMetric::Cm => self.number.get() * 96.0 / 2.54,
517 SizeMetric::Mm => self.number.get() * 96.0 / 25.4,
518 SizeMetric::Em => self.number.get() * em_resolve,
519 SizeMetric::Rem => self.number.get() * em_resolve,
520 SizeMetric::Percent => {
521 NormalizedPercentage::from_unnormalized(self.number.get()).resolve(percent_resolve)
522 }
523 SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => 0.0,
526 }
527 }
528
529 #[inline]
542 pub fn resolve_with_context(
543 &self,
544 context: &ResolutionContext,
545 property_context: PropertyContext,
546 ) -> f32 {
547 match self.metric {
548 SizeMetric::Px => self.number.get(),
550 SizeMetric::Pt => self.number.get() * PT_TO_PX,
551 SizeMetric::In => self.number.get() * 96.0,
552 SizeMetric::Cm => self.number.get() * 96.0 / 2.54,
553 SizeMetric::Mm => self.number.get() * 96.0 / 25.4,
554
555 SizeMetric::Em => {
557 let reference_font_size = if property_context == PropertyContext::FontSize {
558 context.parent_font_size
560 } else {
561 context.element_font_size
563 };
564 self.number.get() * reference_font_size
565 }
566
567 SizeMetric::Rem => self.number.get() * context.root_font_size,
569
570 SizeMetric::Vw => self.number.get() * context.viewport_size.width / 100.0,
573 SizeMetric::Vh => self.number.get() * context.viewport_size.height / 100.0,
574 SizeMetric::Vmin => {
576 let min_dimension = context
577 .viewport_size
578 .width
579 .min(context.viewport_size.height);
580 self.number.get() * min_dimension / 100.0
581 }
582 SizeMetric::Vmax => {
584 let max_dimension = context
585 .viewport_size
586 .width
587 .max(context.viewport_size.height);
588 self.number.get() * max_dimension / 100.0
589 }
590
591 SizeMetric::Percent => {
593 let reference = match property_context {
594 PropertyContext::FontSize => context.parent_font_size,
596
597 PropertyContext::Width => context.containing_block_size.width,
599
600 PropertyContext::Height => context.containing_block_size.height,
602
603 PropertyContext::Margin | PropertyContext::Padding => {
606 context.containing_block_size.width
607 }
608
609 PropertyContext::BorderWidth => 0.0,
612
613 PropertyContext::BorderRadius => {
617 context.element_size.map(|s| s.width).unwrap_or(0.0)
618 }
619
620 PropertyContext::Transform => {
622 context.element_size.map(|s| s.width).unwrap_or(0.0)
623 }
624
625 PropertyContext::Other => context.containing_block_size.width,
627 };
628
629 NormalizedPercentage::from_unnormalized(self.number.get()).resolve(reference)
630 }
631 }
632 }
633}
634
635pub const THIN_BORDER_THICKNESS: PixelValue = PixelValue {
641 metric: SizeMetric::Px,
642 number: FloatValue { number: 1000 },
643};
644
645pub const MEDIUM_BORDER_THICKNESS: PixelValue = PixelValue {
647 metric: SizeMetric::Px,
648 number: FloatValue { number: 3000 },
649};
650
651pub const THICK_BORDER_THICKNESS: PixelValue = PixelValue {
653 metric: SizeMetric::Px,
654 number: FloatValue { number: 5000 },
655};
656
657#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
659#[repr(C)]
660pub struct PixelValueNoPercent {
661 pub inner: PixelValue,
662}
663
664impl PixelValueNoPercent {
665 pub fn scale_for_dpi(&mut self, scale_factor: f32) {
666 self.inner.scale_for_dpi(scale_factor);
667 }
668}
669
670impl_option!(
671 PixelValueNoPercent,
672 OptionPixelValueNoPercent,
673 [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
674);
675
676impl_option!(
677 PixelValue,
678 OptionPixelValue,
679 [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
680);
681
682impl fmt::Display for PixelValueNoPercent {
683 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
684 write!(f, "{}", self.inner)
685 }
686}
687
688impl ::core::fmt::Debug for PixelValueNoPercent {
689 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
690 write!(f, "{}", self)
691 }
692}
693
694impl PixelValueNoPercent {
695 #[doc(hidden)]
701 #[inline]
702 pub fn to_pixels_internal(&self, em_resolve: f32) -> f32 {
703 self.inner.to_pixels_internal(0.0, em_resolve)
704 }
705
706 #[inline]
707 pub const fn zero() -> Self {
708 const ZERO_PXNP: PixelValueNoPercent = PixelValueNoPercent {
709 inner: PixelValue::zero(),
710 };
711 ZERO_PXNP
712 }
713}
714impl From<PixelValue> for PixelValueNoPercent {
715 fn from(e: PixelValue) -> Self {
716 Self { inner: e }
717 }
718}
719
720#[derive(Clone, PartialEq)]
721pub enum CssPixelValueParseError<'a> {
722 EmptyString,
723 NoValueGiven(&'a str, SizeMetric),
724 ValueParseErr(ParseFloatError, &'a str),
725 InvalidPixelValue(&'a str),
726}
727
728impl_debug_as_display!(CssPixelValueParseError<'a>);
729
730impl_display! { CssPixelValueParseError<'a>, {
731 EmptyString => format!("Missing [px / pt / em / %] value"),
732 NoValueGiven(input, metric) => format!("Expected floating-point pixel value, got: \"{}{}\"", input, metric),
733 ValueParseErr(err, number_str) => format!("Could not parse \"{}\" as floating-point value: \"{}\"", number_str, err),
734 InvalidPixelValue(s) => format!("Invalid pixel value: \"{}\"", s),
735}}
736
737#[derive(Debug, Clone, PartialEq)]
739pub enum CssPixelValueParseErrorOwned {
740 EmptyString,
741 NoValueGiven(String, SizeMetric),
742 ValueParseErr(ParseFloatError, String),
743 InvalidPixelValue(String),
744}
745
746impl<'a> CssPixelValueParseError<'a> {
747 pub fn to_contained(&self) -> CssPixelValueParseErrorOwned {
748 match self {
749 CssPixelValueParseError::EmptyString => CssPixelValueParseErrorOwned::EmptyString,
750 CssPixelValueParseError::NoValueGiven(s, metric) => {
751 CssPixelValueParseErrorOwned::NoValueGiven(s.to_string(), *metric)
752 }
753 CssPixelValueParseError::ValueParseErr(err, s) => {
754 CssPixelValueParseErrorOwned::ValueParseErr(err.clone(), s.to_string())
755 }
756 CssPixelValueParseError::InvalidPixelValue(s) => {
757 CssPixelValueParseErrorOwned::InvalidPixelValue(s.to_string())
758 }
759 }
760 }
761}
762
763impl CssPixelValueParseErrorOwned {
764 pub fn to_shared<'a>(&'a self) -> CssPixelValueParseError<'a> {
765 match self {
766 CssPixelValueParseErrorOwned::EmptyString => CssPixelValueParseError::EmptyString,
767 CssPixelValueParseErrorOwned::NoValueGiven(s, metric) => {
768 CssPixelValueParseError::NoValueGiven(s.as_str(), *metric)
769 }
770 CssPixelValueParseErrorOwned::ValueParseErr(err, s) => {
771 CssPixelValueParseError::ValueParseErr(err.clone(), s.as_str())
772 }
773 CssPixelValueParseErrorOwned::InvalidPixelValue(s) => {
774 CssPixelValueParseError::InvalidPixelValue(s.as_str())
775 }
776 }
777 }
778}
779
780fn parse_pixel_value_inner<'a>(
782 input: &'a str,
783 match_values: &[(&'static str, SizeMetric)],
784) -> Result<PixelValue, CssPixelValueParseError<'a>> {
785 let input = input.trim();
786
787 if input.is_empty() {
788 return Err(CssPixelValueParseError::EmptyString);
789 }
790
791 for (match_val, metric) in match_values {
792 if input.ends_with(match_val) {
793 let value = &input[..input.len() - match_val.len()];
794 let value = value.trim();
795 if value.is_empty() {
796 return Err(CssPixelValueParseError::NoValueGiven(input, *metric));
797 }
798 match value.parse::<f32>() {
799 Ok(o) => {
800 return Ok(PixelValue::from_metric(*metric, o));
801 }
802 Err(e) => {
803 return Err(CssPixelValueParseError::ValueParseErr(e, value));
804 }
805 }
806 }
807 }
808
809 match input.trim().parse::<f32>() {
810 Ok(o) => Ok(PixelValue::px(o)),
811 Err(_) => Err(CssPixelValueParseError::InvalidPixelValue(input)),
812 }
813}
814
815pub fn parse_pixel_value<'a>(input: &'a str) -> Result<PixelValue, CssPixelValueParseError<'a>> {
816 parse_pixel_value_inner(
817 input,
818 &[
819 ("px", SizeMetric::Px),
820 ("rem", SizeMetric::Rem), ("em", SizeMetric::Em),
822 ("pt", SizeMetric::Pt),
823 ("in", SizeMetric::In),
824 ("mm", SizeMetric::Mm),
825 ("cm", SizeMetric::Cm),
826 ("vmax", SizeMetric::Vmax), ("vmin", SizeMetric::Vmin), ("vw", SizeMetric::Vw),
829 ("vh", SizeMetric::Vh),
830 ("%", SizeMetric::Percent),
831 ],
832 )
833}
834
835pub fn parse_pixel_value_no_percent<'a>(
836 input: &'a str,
837) -> Result<PixelValueNoPercent, CssPixelValueParseError<'a>> {
838 Ok(PixelValueNoPercent {
839 inner: parse_pixel_value_inner(
840 input,
841 &[
842 ("px", SizeMetric::Px),
843 ("rem", SizeMetric::Rem), ("em", SizeMetric::Em),
845 ("pt", SizeMetric::Pt),
846 ("in", SizeMetric::In),
847 ("mm", SizeMetric::Mm),
848 ("cm", SizeMetric::Cm),
849 ("vmax", SizeMetric::Vmax), ("vmin", SizeMetric::Vmin), ("vw", SizeMetric::Vw),
852 ("vh", SizeMetric::Vh),
853 ],
854 )?,
855 })
856}
857
858#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
859pub enum PixelValueWithAuto {
860 None,
861 Initial,
862 Inherit,
863 Auto,
864 Exact(PixelValue),
865}
866
867pub fn parse_pixel_value_with_auto<'a>(
869 input: &'a str,
870) -> Result<PixelValueWithAuto, CssPixelValueParseError<'a>> {
871 let input = input.trim();
872 match input {
873 "none" => Ok(PixelValueWithAuto::None),
874 "initial" => Ok(PixelValueWithAuto::Initial),
875 "inherit" => Ok(PixelValueWithAuto::Inherit),
876 "auto" => Ok(PixelValueWithAuto::Auto),
877 e => Ok(PixelValueWithAuto::Exact(parse_pixel_value(e)?)),
878 }
879}
880
881#[cfg(all(test, feature = "parser"))]
882mod tests {
883 use super::*;
884
885 #[test]
886 fn test_parse_pixel_value() {
887 assert_eq!(parse_pixel_value("10px").unwrap(), PixelValue::px(10.0));
888 assert_eq!(parse_pixel_value("1.5em").unwrap(), PixelValue::em(1.5));
889 assert_eq!(parse_pixel_value("2rem").unwrap(), PixelValue::rem(2.0));
890 assert_eq!(parse_pixel_value("-20pt").unwrap(), PixelValue::pt(-20.0));
891 assert_eq!(parse_pixel_value("50%").unwrap(), PixelValue::percent(50.0));
892 assert_eq!(parse_pixel_value("1in").unwrap(), PixelValue::inch(1.0));
893 assert_eq!(parse_pixel_value("2.54cm").unwrap(), PixelValue::cm(2.54));
894 assert_eq!(parse_pixel_value("10mm").unwrap(), PixelValue::mm(10.0));
895 assert_eq!(parse_pixel_value(" 0 ").unwrap(), PixelValue::px(0.0));
896 }
897
898 #[test]
899 fn test_resolve_with_context_em() {
900 let context = ResolutionContext {
902 element_font_size: 32.0,
903 parent_font_size: 16.0,
904 ..Default::default()
905 };
906
907 let margin = PixelValue::em(0.67);
909 assert!(
910 (margin.resolve_with_context(&context, PropertyContext::Margin) - 21.44).abs() < 0.01
911 );
912
913 let font_size = PixelValue::em(2.0);
915 assert_eq!(
916 font_size.resolve_with_context(&context, PropertyContext::FontSize),
917 32.0
918 );
919 }
920
921 #[test]
922 fn test_resolve_with_context_rem() {
923 let context = ResolutionContext {
925 element_font_size: 32.0,
926 parent_font_size: 16.0,
927 root_font_size: 18.0,
928 ..Default::default()
929 };
930
931 let margin = PixelValue::rem(2.0);
933 assert_eq!(
934 margin.resolve_with_context(&context, PropertyContext::Margin),
935 36.0
936 );
937
938 let font_size = PixelValue::rem(1.5);
939 assert_eq!(
940 font_size.resolve_with_context(&context, PropertyContext::FontSize),
941 27.0
942 );
943 }
944
945 #[test]
946 fn test_resolve_with_context_percent_margin() {
947 let context = ResolutionContext {
949 element_font_size: 16.0,
950 parent_font_size: 16.0,
951 root_font_size: 16.0,
952 containing_block_size: PhysicalSize::new(800.0, 600.0),
953 element_size: None,
954 viewport_size: PhysicalSize::new(1920.0, 1080.0),
955 };
956
957 let margin = PixelValue::percent(10.0); assert_eq!(
959 margin.resolve_with_context(&context, PropertyContext::Margin),
960 80.0
961 ); }
963
964 #[test]
965 fn test_parse_pixel_value_no_percent() {
966 assert_eq!(
967 parse_pixel_value_no_percent("10px").unwrap().inner,
968 PixelValue::px(10.0)
969 );
970 assert!(parse_pixel_value_no_percent("50%").is_err());
971 }
972
973 #[test]
974 fn test_parse_pixel_value_with_auto() {
975 assert_eq!(
976 parse_pixel_value_with_auto("10px").unwrap(),
977 PixelValueWithAuto::Exact(PixelValue::px(10.0))
978 );
979 assert_eq!(
980 parse_pixel_value_with_auto("auto").unwrap(),
981 PixelValueWithAuto::Auto
982 );
983 assert_eq!(
984 parse_pixel_value_with_auto("initial").unwrap(),
985 PixelValueWithAuto::Initial
986 );
987 assert_eq!(
988 parse_pixel_value_with_auto("inherit").unwrap(),
989 PixelValueWithAuto::Inherit
990 );
991 assert_eq!(
992 parse_pixel_value_with_auto("none").unwrap(),
993 PixelValueWithAuto::None
994 );
995 }
996
997 #[test]
998 fn test_parse_pixel_value_errors() {
999 assert!(parse_pixel_value("").is_err());
1000 assert!(parse_pixel_value("10").is_ok()); assert!(parse_pixel_value("10 px").is_ok()); assert!(parse_pixel_value("px").is_err());
1005 assert!(parse_pixel_value("ten-px").is_err());
1006 }
1007}