parcel_css/properties/
contain.rs1#![allow(non_upper_case_globals)]
4
5use bitflags::bitflags;
6use cssparser::*;
7use smallvec::SmallVec;
8
9use crate::{
10 context::PropertyHandlerContext,
11 declaration::{DeclarationBlock, DeclarationList},
12 error::{ParserError, PrinterError},
13 macros::{define_shorthand, shorthand_handler},
14 printer::Printer,
15 properties::{Property, PropertyId},
16 rules::container::ContainerName as ContainerIdent,
17 targets::Browsers,
18 traits::{Parse, PropertyHandler, Shorthand, ToCss},
19};
20
21bitflags! {
22 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27 pub struct ContainerType: u8 {
28 const Normal = 0b00000001;
31 const InlineSize = 0b00000010;
33 const Size = 0b00000100;
35 }
36}
37
38impl Default for ContainerType {
39 fn default() -> Self {
40 ContainerType::Normal
41 }
42}
43
44impl<'i> Parse<'i> for ContainerType {
45 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
46 let mut flags = ContainerType::empty();
47 while let Ok(ident) = input.try_parse(|input| input.expect_ident_cloned()) {
48 let location = input.current_source_location();
49 let flag = match_ignore_ascii_case! { &ident,
50 "normal" if flags.is_empty() => return Ok(ContainerType::Normal), "size" => ContainerType::Size,
52 "inline-size" => ContainerType::InlineSize,
53 _ => return Err(location.new_unexpected_token_error(
54 cssparser::Token::Ident(ident.clone())
55 ))
56 };
57 if flags.contains(flag) {
58 return Err(location.new_unexpected_token_error(cssparser::Token::Ident(ident.clone())));
59 }
60 flags |= flag;
61 }
62
63 if flags.is_empty() {
64 return Err(input.new_error_for_next_token());
65 } else {
66 return Ok(flags);
67 }
68 }
69}
70
71impl ToCss for ContainerType {
72 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
73 where
74 W: std::fmt::Write,
75 {
76 if self.contains(ContainerType::Normal) {
77 dest.write_str("normal")?;
78 } else if self.contains(ContainerType::Size) {
79 dest.write_str("size")?;
80 } else if self.contains(ContainerType::InlineSize) {
81 dest.write_str("inline-size")?;
82 }
83
84 Ok(())
85 }
86}
87
88#[derive(Debug, Clone, PartialEq)]
90#[cfg_attr(
91 feature = "serde",
92 derive(serde::Serialize, serde::Deserialize),
93 serde(tag = "type", content = "value", rename_all = "kebab-case")
94)]
95pub enum ContainerNameList<'i> {
96 None,
98 #[cfg_attr(feature = "serde", serde(borrow))]
100 Names(SmallVec<[ContainerIdent<'i>; 1]>),
101}
102
103impl<'i> Default for ContainerNameList<'i> {
104 fn default() -> Self {
105 ContainerNameList::None
106 }
107}
108
109impl<'i> Parse<'i> for ContainerNameList<'i> {
110 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
111 if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
112 return Ok(ContainerNameList::None);
113 }
114
115 let mut names = SmallVec::new();
116 while let Ok(name) = input.try_parse(ContainerIdent::parse) {
117 names.push(name);
118 }
119
120 if names.is_empty() {
121 return Err(input.new_error_for_next_token());
122 } else {
123 return Ok(ContainerNameList::Names(names));
124 }
125 }
126}
127
128impl<'i> ToCss for ContainerNameList<'i> {
129 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
130 where
131 W: std::fmt::Write,
132 {
133 match self {
134 ContainerNameList::None => dest.write_str("none"),
135 ContainerNameList::Names(names) => {
136 let mut first = true;
137 for name in names {
138 if first {
139 first = false;
140 } else {
141 dest.write_char(' ')?;
142 }
143 name.to_css(dest)?;
144 }
145 Ok(())
146 }
147 }
148 }
149}
150
151define_shorthand! {
152 pub struct Container<'i> {
154 #[cfg_attr(feature = "serde", serde(borrow))]
156 name: ContainerName(ContainerNameList<'i>),
157 container_type: ContainerType(ContainerType),
159 }
160}
161
162impl<'i> Parse<'i> for Container<'i> {
163 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
164 let name = ContainerNameList::parse(input)?;
165 let container_type = if input.try_parse(|input| input.expect_delim('/')).is_ok() {
166 ContainerType::parse(input)?
167 } else {
168 ContainerType::default()
169 };
170 Ok(Container { name, container_type })
171 }
172}
173
174impl<'i> ToCss for Container<'i> {
175 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
176 where
177 W: std::fmt::Write,
178 {
179 self.name.to_css(dest)?;
180 if self.container_type != ContainerType::default() {
181 dest.delim('/', true)?;
182 self.container_type.to_css(dest)?;
183 }
184 Ok(())
185 }
186}
187
188shorthand_handler!(ContainerHandler -> Container<'i> {
189 name: ContainerName(ContainerNameList<'i>),
190 container_type: ContainerType(ContainerType),
191});