1#![deny(missing_docs)]
8
9use num_traits::ToPrimitive;
10use std::borrow::Cow;
11use std::iter::{Filter, Peekable};
12use std::str::Split;
13
14pub type StaticCharVec = &'static [char];
16
17pub type StaticStringVec = &'static [&'static str];
19
20pub static HTML_SPACE_CHARACTERS: StaticCharVec =
24 &['\u{0020}', '\u{0009}', '\u{000a}', '\u{000c}', '\u{000d}'];
25
26#[inline]
28pub fn char_is_whitespace(c: char) -> bool {
29 HTML_SPACE_CHARACTERS.contains(&c)
30}
31
32#[inline]
34pub fn is_whitespace(s: &str) -> bool {
35 s.chars().all(char_is_whitespace)
36}
37
38#[inline]
39fn not_empty(&split: &&str) -> bool {
40 !split.is_empty()
41}
42
43#[inline]
45pub fn split_html_space_chars<'a>(
46 s: &'a str,
47) -> Filter<Split<'a, StaticCharVec>, fn(&&str) -> bool> {
48 s.split(HTML_SPACE_CHARACTERS)
49 .filter(not_empty as fn(&&str) -> bool)
50}
51
52#[inline]
54pub fn split_commas<'a>(s: &'a str) -> Filter<Split<'a, char>, fn(&&str) -> bool> {
55 s.split(',').filter(not_empty as fn(&&str) -> bool)
56}
57
58pub fn is_ascii_digit(c: &char) -> bool {
60 match *c {
61 '0'..='9' => true,
62 _ => false,
63 }
64}
65
66fn is_decimal_point(c: char) -> bool {
67 c == '.'
68}
69
70fn is_exponent_char(c: char) -> bool {
71 match c {
72 'e' | 'E' => true,
73 _ => false,
74 }
75}
76
77pub fn read_numbers<I: Iterator<Item = char>>(mut iter: Peekable<I>) -> (Option<i64>, usize) {
79 match iter.peek() {
80 Some(c) if is_ascii_digit(c) => (),
81 _ => return (None, 0),
82 }
83
84 iter.take_while(is_ascii_digit)
85 .map(|d| d as i64 - '0' as i64)
86 .fold((Some(0i64), 0), |accumulator, d| {
87 let digits = accumulator
88 .0
89 .and_then(|accumulator| accumulator.checked_mul(10))
90 .and_then(|accumulator| accumulator.checked_add(d));
91 (digits, accumulator.1 + 1)
92 })
93}
94
95pub fn read_fraction<I: Iterator<Item = char>>(
97 mut iter: Peekable<I>,
98 mut divisor: f64,
99 value: f64,
100) -> (f64, usize) {
101 match iter.peek() {
102 Some(c) if is_decimal_point(*c) => (),
103 _ => return (value, 0),
104 }
105 iter.next();
106
107 iter.take_while(is_ascii_digit)
108 .map(|d| d as i64 - '0' as i64)
109 .fold((value, 1), |accumulator, d| {
110 divisor *= 10f64;
111 (accumulator.0 + d as f64 / divisor, accumulator.1 + 1)
112 })
113}
114
115pub fn read_exponent<I: Iterator<Item = char>>(mut iter: Peekable<I>) -> Option<i32> {
117 match iter.peek() {
118 Some(c) if is_exponent_char(*c) => (),
119 _ => return None,
120 }
121 iter.next();
122
123 match iter.peek() {
124 None => None,
125 Some(&'-') => {
126 iter.next();
127 read_numbers(iter).0.map(|exp| -exp.to_i32().unwrap_or(0))
128 },
129 Some(&'+') => {
130 iter.next();
131 read_numbers(iter).0.map(|exp| exp.to_i32().unwrap_or(0))
132 },
133 Some(_) => read_numbers(iter).0.map(|exp| exp.to_i32().unwrap_or(0)),
134 }
135}
136
137pub fn str_join<I, T>(strs: I, join: &str) -> String
139where
140 I: IntoIterator<Item = T>,
141 T: AsRef<str>,
142{
143 strs.into_iter()
144 .enumerate()
145 .fold(String::new(), |mut acc, (i, s)| {
146 if i > 0 {
147 acc.push_str(join);
148 }
149 acc.push_str(s.as_ref());
150 acc
151 })
152}
153
154pub fn starts_with_ignore_ascii_case(string: &str, prefix: &str) -> bool {
156 string.len() >= prefix.len()
157 && string.as_bytes()[0..prefix.len()].eq_ignore_ascii_case(prefix.as_bytes())
158}
159
160pub fn string_as_ascii_lowercase<'a>(input: &'a str) -> Cow<'a, str> {
162 if input.bytes().any(|c| matches!(c, b'A'..=b'Z')) {
163 input.to_ascii_lowercase().into()
164 } else {
165 Cow::Borrowed(input)
167 }
168}
169
170#[cfg(feature = "gecko")]
175pub type CssStringWriter = ::nsstring::nsACString;
176
177#[cfg(feature = "gecko")]
180pub type CssString = ::nsstring::nsCString;
181
182#[cfg(feature = "servo")]
184pub type CssStringWriter = String;
185
186#[cfg(feature = "servo")]
188pub type CssString = String;