@@ -5,7 +5,7 @@ use crate::error::{ParserError, PrinterError};
55use crate :: macros:: enum_property;
66use crate :: printer:: Printer ;
77use crate :: traits:: private:: AddInternal ;
8- use crate :: traits:: { Parse , Sign , ToCss , TryMap , TryOp , TrySign } ;
8+ use crate :: traits:: { Parse , Sign , ToCss , TryMap , TryOp , TrySign , Zero } ;
99use cssparser:: * ;
1010
1111use super :: angle:: Angle ;
@@ -43,6 +43,8 @@ pub enum MathFunction<V> {
4343 Abs ( Calc < V > ) ,
4444 /// The [`sign()`](https://drafts.csswg.org/css-values-4/#funcdef-sign) function.
4545 Sign ( Calc < V > ) ,
46+ /// The [`hypot()`](https://drafts.csswg.org/css-values-4/#funcdef-hypot) function.
47+ Hypot ( Vec < Calc < V > > ) ,
4648}
4749
4850enum_property ! {
@@ -176,6 +178,19 @@ impl<V: ToCss + std::ops::Mul<f32, Output = V> + TrySign + Clone + std::fmt::Deb
176178 v. to_css ( dest) ?;
177179 dest. write_char ( ')' )
178180 }
181+ MathFunction :: Hypot ( args) => {
182+ dest. write_str ( "hypot(" ) ?;
183+ let mut first = true ;
184+ for arg in args {
185+ if first {
186+ first = false ;
187+ } else {
188+ dest. delim ( ',' , false ) ?;
189+ }
190+ arg. to_css ( dest) ?;
191+ }
192+ dest. write_char ( ')' )
193+ }
179194 }
180195 }
181196}
@@ -394,20 +409,24 @@ impl<
394409 } ,
395410 "sqrt" => Self :: parse_numeric_fn( input, f32 :: sqrt) ,
396411 "exp" => Self :: parse_numeric_fn( input, f32 :: exp) ,
412+ "hypot" => {
413+ input. parse_nested_block( |input| {
414+ let args: Vec <Self > = input. parse_comma_separated( Self :: parse_sum) ?;
415+ Self :: parse_hypot( & args) ?
416+ . map_or_else(
417+ || Ok ( Calc :: Function ( Box :: new( MathFunction :: Hypot ( args) ) ) ) ,
418+ |v| Ok ( v)
419+ )
420+ } )
421+ } ,
397422 "abs" => {
398423 input. parse_nested_block( |input| {
399424 let v: Calc <V > = Self :: parse_sum( input) ?;
400- match & v {
401- Calc :: Number ( n) => return Ok ( Calc :: Number ( n. abs( ) ) ) ,
402- Calc :: Value ( v) => {
403- if let Some ( v) = v. try_map( f32 :: abs) {
404- return Ok ( Calc :: Value ( Box :: new( v) ) ) ;
405- }
406- }
407- _ => { }
408- }
409-
410- Ok ( Calc :: Function ( Box :: new( MathFunction :: Abs ( v) ) ) )
425+ Self :: apply_map( & v, f32 :: abs)
426+ . map_or_else(
427+ || Ok ( Calc :: Function ( Box :: new( MathFunction :: Abs ( v) ) ) ) ,
428+ |v| Ok ( v)
429+ )
411430 } )
412431 } ,
413432 "sign" => {
@@ -599,17 +618,35 @@ impl<
599618 input. expect_comma ( ) ?;
600619 let b: Calc < V > = Calc :: parse_sum ( input) ?;
601620
602- match ( & a, & b) {
621+ Ok ( Self :: apply_op ( & a, & b, op) . unwrap_or_else ( || Calc :: Function ( Box :: new ( fallback ( a, b) ) ) ) )
622+ }
623+
624+ fn apply_op < ' t , O : FnOnce ( f32 , f32 ) -> f32 > ( a : & Calc < V > , b : & Calc < V > , op : O ) -> Option < Self > {
625+ match ( a, b) {
603626 ( Calc :: Value ( a) , Calc :: Value ( b) ) => {
604627 if let Some ( v) = a. try_op ( & * * b, op) {
605- return Ok ( Calc :: Value ( Box :: new ( v) ) ) ;
628+ return Some ( Calc :: Value ( Box :: new ( v) ) ) ;
629+ }
630+ }
631+ ( Calc :: Number ( a) , Calc :: Number ( b) ) => return Some ( Calc :: Number ( op ( * a, * b) ) ) ,
632+ _ => { }
633+ }
634+
635+ None
636+ }
637+
638+ fn apply_map < ' t , O : FnOnce ( f32 ) -> f32 > ( v : & Calc < V > , op : O ) -> Option < Self > {
639+ match v {
640+ Calc :: Number ( n) => return Some ( Calc :: Number ( op ( * n) ) ) ,
641+ Calc :: Value ( v) => {
642+ if let Some ( v) = v. try_map ( op) {
643+ return Some ( Calc :: Value ( Box :: new ( v) ) ) ;
606644 }
607645 }
608- ( Calc :: Number ( a) , Calc :: Number ( b) ) => return Ok ( Calc :: Number ( op ( * a, * b) ) ) ,
609646 _ => { }
610647 }
611648
612- Ok ( Calc :: Function ( Box :: new ( fallback ( a , b ) ) ) )
649+ None
613650 }
614651
615652 fn parse_trig < ' t , F : FnOnce ( f32 ) -> f32 > (
@@ -698,6 +735,32 @@ impl<
698735 // This will fall back to an unparsed property, leaving the atan2() function intact.
699736 Err ( input. new_custom_error ( ParserError :: InvalidValue ) )
700737 }
738+
739+ fn parse_hypot < ' t > ( args : & Vec < Self > ) -> Result < Option < Self > , ParseError < ' i , ParserError < ' i > > > {
740+ if args. len ( ) == 1 {
741+ return Ok ( Some ( args[ 0 ] . clone ( ) ) ) ;
742+ }
743+
744+ if args. len ( ) == 2 {
745+ return Ok ( Self :: apply_op ( & args[ 0 ] , & args[ 1 ] , f32:: hypot) ) ;
746+ }
747+
748+ let mut iter = args. iter ( ) ;
749+ let first = match Self :: apply_map ( & iter. next ( ) . unwrap ( ) , |v| v. powi ( 2 ) ) {
750+ Some ( v) => v,
751+ None => return Ok ( None ) ,
752+ } ;
753+ let sum = iter. try_fold ( first, |acc, arg| {
754+ Self :: apply_op ( & acc, & arg, |a, b| a + b. powi ( 2 ) ) . map_or_else ( || Err ( ( ) ) , |v| Ok ( v) )
755+ } ) ;
756+
757+ let sum = match sum {
758+ Ok ( s) => s,
759+ Err ( _) => return Ok ( None ) ,
760+ } ;
761+
762+ Ok ( Self :: apply_map ( & sum, f32:: sqrt) )
763+ }
701764}
702765
703766impl < V : std:: ops:: Mul < f32 , Output = V > > std:: ops:: Mul < f32 > for Calc < V > {
0 commit comments