1use super::{Property, PropertyId};
4use crate::context::PropertyHandlerContext;
5use crate::declaration::{DeclarationBlock, DeclarationList};
6use crate::error::{ParserError, PrinterError};
7use crate::macros::{define_shorthand, enum_property, shorthand_handler, shorthand_property};
8use crate::printer::Printer;
9use crate::targets::Browsers;
10use crate::traits::{FallbackValues, Parse, PropertyHandler, Shorthand, ToCss};
11use crate::values::string::CowArcStr;
12use crate::values::{ident::CustomIdent, image::Image};
13use cssparser::*;
14
15#[derive(Debug, Clone, PartialEq)]
17#[cfg_attr(
18 feature = "serde",
19 derive(serde::Serialize, serde::Deserialize),
20 serde(tag = "type", content = "value", rename_all = "kebab-case")
21)]
22pub enum ListStyleType<'i> {
23 None,
25 #[cfg_attr(feature = "serde", serde(borrow))]
27 String(CowArcStr<'i>),
28 CounterStyle(CounterStyle<'i>),
30}
31
32impl Default for ListStyleType<'_> {
33 fn default() -> Self {
34 ListStyleType::CounterStyle(CounterStyle::Predefined(PredefinedCounterStyle::Disc))
35 }
36}
37
38impl<'i> Parse<'i> for ListStyleType<'i> {
39 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
40 if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
41 return Ok(ListStyleType::None);
42 }
43
44 if let Ok(val) = input.try_parse(CounterStyle::parse) {
45 return Ok(ListStyleType::CounterStyle(val));
46 }
47
48 let s = input.expect_string_cloned()?;
49 Ok(ListStyleType::String(s.into()))
50 }
51}
52
53impl ToCss for ListStyleType<'_> {
54 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
55 where
56 W: std::fmt::Write,
57 {
58 match self {
59 ListStyleType::None => dest.write_str("none"),
60 ListStyleType::CounterStyle(style) => style.to_css(dest),
61 ListStyleType::String(s) => {
62 serialize_string(&s, dest)?;
63 Ok(())
64 }
65 }
66 }
67}
68
69#[derive(Debug, Clone, PartialEq)]
71#[cfg_attr(
72 feature = "serde",
73 derive(serde::Serialize, serde::Deserialize),
74 serde(tag = "type", content = "value", rename_all = "kebab-case")
75)]
76pub enum CounterStyle<'i> {
77 Predefined(PredefinedCounterStyle),
79 Name(CustomIdent<'i>),
81 Symbols(
83 SymbolsType,
84 #[cfg_attr(feature = "serde", serde(borrow))] Vec<Symbol<'i>>,
85 ),
86}
87
88enum_property! {
89 #[allow(missing_docs)]
91 pub enum PredefinedCounterStyle {
92 "decimal": Decimal,
94 "decimal-leading-zero": DecimalLeadingZero,
95 "arabic-indic": ArabicIndic,
96 "armenian": Armenian,
97 "upper-armenian": UpperArmenian,
98 "lower-armenian": LowerArmenian,
99 "bengali": Bengali,
100 "cambodian": Cambodian,
101 "khmer": Khmer,
102 "cjk-decimal": CjkDecimal,
103 "devanagari": Devanagari,
104 "georgian": Georgian,
105 "gujarati": Gujarati,
106 "gurmukhi": Gurmukhi,
107 "hebrew": Hebrew,
108 "kannada": Kannada,
109 "lao": Lao,
110 "malayalam": Malayalam,
111 "mongolian": Mongolian,
112 "myanmar": Myanmar,
113 "oriya": Oriya,
114 "persian": Persian,
115 "lower-roman": LowerRoman,
116 "upper-roman": UpperRoman,
117 "tamil": Tamil,
118 "telugu": Telugu,
119 "thai": Thai,
120 "tibetan": Tibetan,
121
122 "lower-alpha": LowerAlpha,
124 "lower-latin": LowerLatin,
125 "upper-alpha": UpperAlpha,
126 "upper-latin": UpperLatin,
127 "lower-greek": LowerGreek,
128 "hiragana": Hiragana,
129 "hiragana-iroha": HiraganaIroha,
130 "katakana": Katakana,
131 "katakana-iroha": KatakanaIroha,
132
133 "disc": Disc,
135 "circle": Circle,
136 "square": Square,
137 "disclosure-open": DisclosureOpen,
138 "disclosure-closed": DisclosureClosed,
139
140 "cjk-earthly-branch": CjkEarthlyBranch,
142 "cjk-heavenly-stem": CjkHeavenlyStem,
143
144 "japanese-informal": JapaneseInformal,
146 "japanese-formal": JapaneseFormal,
147 "korean-hangul-formal": KoreanHangulFormal,
148 "korean-hanja-informal": KoreanHanjaInformal,
149 "korean-hanja-formal": KoreanHanjaFormal,
150 "simp-chinese-informal": SimpChineseInformal,
151 "simp-chinese-formal": SimpChineseFormal,
152 "trad-chinese-informal": TradChineseInformal,
153 "trad-chinese-formal": TradChineseFormal,
154 "ethiopic-numeric": EthiopicNumeric,
155 }
156}
157
158impl<'i> Parse<'i> for CounterStyle<'i> {
159 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
160 if let Ok(predefined) = input.try_parse(PredefinedCounterStyle::parse) {
161 return Ok(CounterStyle::Predefined(predefined));
162 }
163
164 if input.try_parse(|input| input.expect_function_matching("symbols")).is_ok() {
165 return input.parse_nested_block(|input| {
166 let t = input.try_parse(SymbolsType::parse).unwrap_or(SymbolsType::Symbolic);
167
168 let mut symbols = Vec::new();
169 while let Ok(s) = input.try_parse(Symbol::parse) {
170 symbols.push(s);
171 }
172
173 Ok(CounterStyle::Symbols(t, symbols))
174 });
175 }
176
177 let name = CustomIdent::parse(input)?;
178 Ok(CounterStyle::Name(name))
179 }
180}
181
182impl ToCss for CounterStyle<'_> {
183 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
184 where
185 W: std::fmt::Write,
186 {
187 match self {
188 CounterStyle::Predefined(style) => style.to_css(dest),
189 CounterStyle::Name(name) => {
190 if let Some(css_module) = &mut dest.css_module {
191 css_module.reference(&name.0)
192 }
193 name.to_css(dest)
194 }
195 CounterStyle::Symbols(t, symbols) => {
196 dest.write_str("symbols(")?;
197 let mut needs_space = false;
198 if *t != SymbolsType::Symbolic {
199 t.to_css(dest)?;
200 needs_space = true;
201 }
202
203 for symbol in symbols {
204 if needs_space {
205 dest.write_char(' ')?;
206 }
207 symbol.to_css(dest)?;
208 needs_space = true;
209 }
210 dest.write_char(')')
211 }
212 }
213 }
214}
215
216enum_property! {
217 #[allow(missing_docs)]
222 pub enum SymbolsType {
223 Cyclic,
224 Numeric,
225 Alphabetic,
226 Symbolic,
227 Fixed,
228 }
229}
230
231#[derive(Debug, Clone, PartialEq)]
236#[cfg_attr(
237 feature = "serde",
238 derive(serde::Serialize, serde::Deserialize),
239 serde(tag = "type", content = "value", rename_all = "kebab-case")
240)]
241pub enum Symbol<'i> {
242 #[cfg_attr(feature = "serde", serde(borrow))]
244 String(CowArcStr<'i>),
245 Image(Image<'i>),
247}
248
249impl<'i> Parse<'i> for Symbol<'i> {
250 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
251 if let Ok(img) = input.try_parse(Image::parse) {
252 return Ok(Symbol::Image(img));
253 }
254
255 let s = input.expect_string_cloned()?;
256 Ok(Symbol::String(s.into()))
257 }
258}
259
260impl<'i> ToCss for Symbol<'i> {
261 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
262 where
263 W: std::fmt::Write,
264 {
265 match self {
266 Symbol::String(s) => {
267 serialize_string(&s, dest)?;
268 Ok(())
269 }
270 Symbol::Image(img) => img.to_css(dest),
271 }
272 }
273}
274
275enum_property! {
276 pub enum ListStylePosition {
278 Inside,
280 Outside,
282 }
283}
284
285impl Default for ListStylePosition {
286 fn default() -> ListStylePosition {
287 ListStylePosition::Outside
288 }
289}
290
291enum_property! {
292 #[allow(missing_docs)]
294 pub enum MarkerSide {
295 "match-self": MatchSelf,
296 "match-parent": MatchParent,
297 }
298}
299
300shorthand_property! {
301 pub struct ListStyle<'i> {
303 #[cfg_attr(feature = "serde", serde(borrow))]
305 list_style_type: ListStyleType(ListStyleType<'i>),
306 image: ListStyleImage(Image<'i>),
308 position: ListStylePosition(ListStylePosition),
310 }
311}
312
313impl<'i> FallbackValues for ListStyle<'i> {
314 fn get_fallbacks(&mut self, targets: Browsers) -> Vec<Self> {
315 self
316 .image
317 .get_fallbacks(targets)
318 .into_iter()
319 .map(|image| ListStyle { image, ..self.clone() })
320 .collect()
321 }
322}
323
324shorthand_handler!(ListStyleHandler -> ListStyle<'i> fallbacks: true {
325 image: ListStyleImage(Image<'i>, fallback: true, image: true),
326 list_style_type: ListStyleType(ListStyleType<'i>),
327 position: ListStylePosition(ListStylePosition),
328});