calc.rs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. //! The `calc` functionality.
  2. use crate::LengthPercentage;
  3. use ::{
  4. proc_macro2::TokenStream,
  5. quote::{quote, ToTokens},
  6. std::fmt,
  7. syn::{
  8. custom_keyword, parenthesized,
  9. parse::{Parse, ParseStream},
  10. Token,
  11. },
  12. };
  13. /// Values that can be a calculaion (currently restricted to length & percentages)
  14. #[derive(Debug, Clone, PartialEq)]
  15. pub enum Calc {
  16. Calculated(CalcSum),
  17. Normal(LengthPercentage),
  18. }
  19. impl fmt::Display for Calc {
  20. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  21. match self {
  22. Calc::Calculated(inner) => write!(f, "calc({})", inner),
  23. Calc::Normal(inner) => write!(f, "{}", inner),
  24. }
  25. }
  26. }
  27. impl Parse for Calc {
  28. fn parse(s: ParseStream) -> syn::Result<Self> {
  29. custom_keyword!(calc);
  30. if s.peek(calc) {
  31. s.parse::<calc>()?;
  32. let content;
  33. parenthesized!(content in s);
  34. Ok(Calc::Calculated(content.parse()?))
  35. } else {
  36. Ok(Calc::Normal(s.parse()?))
  37. }
  38. }
  39. }
  40. impl ToTokens for Calc {
  41. fn to_tokens(&self, tokens: &mut TokenStream) {
  42. tokens.extend(match self {
  43. Calc::Calculated(inner) => quote!(style::Calc::Calculated(#inner)),
  44. Calc::Normal(inner) => quote!(style::Calc::Normal(#inner)),
  45. });
  46. }
  47. }
  48. #[test]
  49. fn test_calc() {
  50. for (input, output) in vec![
  51. ("calc(10% - 20\"em\")", "calc(10% - 20em)"),
  52. ("calc(100% + 5px)", "calc(100% + 5px)"),
  53. ("calc(100% - 60px)", "calc(100% - 60px)"),
  54. ] {
  55. assert_eq!(&syn::parse_str::<Calc>(input).unwrap().to_string(), output);
  56. }
  57. }
  58. #[derive(Debug, Clone, PartialEq)]
  59. pub struct CalcSum {
  60. pub first: CalcProduct,
  61. pub rest: Vec<SumOp>,
  62. }
  63. impl fmt::Display for CalcSum {
  64. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  65. write!(f, "{}", self.first)?;
  66. for op in self.rest.iter() {
  67. write!(f, "{}", op)?;
  68. }
  69. Ok(())
  70. }
  71. }
  72. impl Parse for CalcSum {
  73. fn parse(s: ParseStream) -> syn::Result<Self> {
  74. let first: CalcProduct = s.parse()?;
  75. let mut rest: Vec<SumOp> = vec![];
  76. while SumOp::peek(s) {
  77. rest.push(s.parse()?);
  78. }
  79. Ok(CalcSum { first, rest })
  80. }
  81. }
  82. impl ToTokens for CalcSum {
  83. fn to_tokens(&self, tokens: &mut TokenStream) {
  84. let first = &self.first;
  85. let rest = self.rest.iter();
  86. tokens.extend(quote! {
  87. style::calc::CalcSum {
  88. first: #first,
  89. rest: vec![#(#rest,)*]
  90. }
  91. });
  92. }
  93. }
  94. #[derive(Debug, Clone, PartialEq)]
  95. pub enum SumOp {
  96. Add(CalcProduct),
  97. Sub(CalcProduct),
  98. }
  99. impl SumOp {
  100. fn peek(s: ParseStream) -> bool {
  101. s.peek(Token![+]) || s.peek(Token![-])
  102. }
  103. }
  104. impl fmt::Display for SumOp {
  105. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  106. match self {
  107. SumOp::Add(inner) => write!(f, " + {}", inner),
  108. SumOp::Sub(inner) => write!(f, " - {}", inner),
  109. }
  110. }
  111. }
  112. impl Parse for SumOp {
  113. fn parse(s: ParseStream) -> syn::Result<Self> {
  114. let lookahead = s.lookahead1();
  115. if lookahead.peek(Token![+]) {
  116. s.parse::<Token![+]>()?;
  117. Ok(SumOp::Add(s.parse()?))
  118. } else if lookahead.peek(Token![-]) {
  119. s.parse::<Token![-]>()?;
  120. Ok(SumOp::Sub(s.parse()?))
  121. } else {
  122. Err(lookahead.error())
  123. }
  124. }
  125. }
  126. impl ToTokens for SumOp {
  127. fn to_tokens(&self, tokens: &mut TokenStream) {
  128. tokens.extend(match self {
  129. SumOp::Add(inner) => quote!(style::SumOp::Add(#inner)),
  130. SumOp::Sub(inner) => quote!(style::SumOp::Sub(#inner)),
  131. });
  132. }
  133. }
  134. #[derive(Debug, Clone, PartialEq)]
  135. pub struct CalcProduct {
  136. pub first: CalcValue,
  137. pub rest: Vec<ProductOp>,
  138. }
  139. impl fmt::Display for CalcProduct {
  140. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  141. write!(f, "{}", self.first)?;
  142. for op in self.rest.iter() {
  143. write!(f, "{}", op)?;
  144. }
  145. Ok(())
  146. }
  147. }
  148. impl Parse for CalcProduct {
  149. fn parse(s: ParseStream) -> syn::Result<Self> {
  150. let first: CalcValue = s.parse()?;
  151. let mut rest: Vec<ProductOp> = vec![];
  152. while ProductOp::peek(s) {
  153. rest.push(s.parse()?);
  154. }
  155. Ok(CalcProduct { first, rest })
  156. }
  157. }
  158. impl ToTokens for CalcProduct {
  159. fn to_tokens(&self, tokens: &mut TokenStream) {
  160. let first = &self.first;
  161. let rest = self.rest.iter();
  162. tokens.extend(quote! {
  163. style::calc::CalcProduct {
  164. first: #first,
  165. rest: vec![#(#rest,)*]
  166. }
  167. });
  168. }
  169. }
  170. #[derive(Debug, Clone, PartialEq)]
  171. pub enum ProductOp {
  172. Mul(CalcValue),
  173. // todo Div(Number),
  174. }
  175. impl ProductOp {
  176. pub fn peek(s: ParseStream) -> bool {
  177. s.peek(Token![*]) // || s.peek(Token[/])
  178. }
  179. }
  180. impl fmt::Display for ProductOp {
  181. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  182. match self {
  183. ProductOp::Mul(inner) => write!(f, "*{}", inner),
  184. //ProductOp::Div(inner) => write!(f, "/{}", inner),
  185. }
  186. }
  187. }
  188. impl Parse for ProductOp {
  189. fn parse(s: ParseStream) -> syn::Result<Self> {
  190. let lookahead = s.lookahead1();
  191. if lookahead.peek(Token![*]) {
  192. s.parse::<Token![*]>()?;
  193. Ok(ProductOp::Mul(s.parse()?))
  194. /*
  195. } else if lookahead.peek(Token![/]) {
  196. s.parse::<Token![/]>()?;
  197. Ok(ProductOp::Div(s.parse()?))
  198. */
  199. } else {
  200. Err(lookahead.error())
  201. }
  202. }
  203. }
  204. impl ToTokens for ProductOp {
  205. fn to_tokens(&self, tokens: &mut TokenStream) {
  206. tokens.extend(match self {
  207. ProductOp::Mul(inner) => quote!(style::ProductOp::Mul(#inner)),
  208. //ProductOp::Div(inner) => quote!(style::ProductOp::Div(#inner)),
  209. });
  210. }
  211. }
  212. #[derive(Debug, Clone, PartialEq)]
  213. pub enum CalcValue {
  214. LengthPercentage(LengthPercentage),
  215. // todo more variants
  216. }
  217. impl Parse for CalcValue {
  218. fn parse(s: ParseStream) -> syn::Result<Self> {
  219. Ok(CalcValue::LengthPercentage(s.parse()?))
  220. }
  221. }
  222. impl fmt::Display for CalcValue {
  223. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  224. match self {
  225. CalcValue::LengthPercentage(inner) => write!(f, "{}", inner),
  226. }
  227. }
  228. }
  229. impl ToTokens for CalcValue {
  230. fn to_tokens(&self, tokens: &mut TokenStream) {
  231. tokens.extend(match self {
  232. CalcValue::LengthPercentage(inner) => {
  233. quote!(style::CalcValue::LengthPercentage(#inner))
  234. }
  235. });
  236. }
  237. }