/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
usecrate::match_byte; use dtoa_short::Notation; use std::fmt::{self, Write}; use std::str;
usesuper::Token;
/// Trait for things the can serialize themselves in CSS syntax. pubtrait ToCss { /// Serialize `self` in CSS syntax, writing to `dest`. fn to_css<W>(&self, dest: &mut W) -> fmt::Result where
W: fmt::Write;
/// Serialize `self` in CSS syntax and return a string. /// /// (This is a convenience wrapper for `to_css` and probably should not be overridden.) #[inline] fn to_css_string(&self) -> String { letmut s = String::new(); self.to_css(&mut s).unwrap();
s
}
}
#[inline] fn write_numeric<W>(value: f32, int_value: Option<i32>, has_sign: bool, dest: &mut W) -> fmt::Result where
W: fmt::Write,
{ // `value.value >= 0` is true for negative 0. if has_sign && value.is_sign_positive() {
dest.write_str("+")?;
}
let notation = if value == 0.0 && value.is_sign_negative() { // Negative zero. Work around #20596.
dest.write_str("-0")?;
Notation {
decimal_point: false,
scientific: false,
}
} else {
dtoa_short::write(dest, value)?
};
Token::BadUrl(ref contents) => {
dest.write_str("url(")?;
dest.write_str(contents)?;
dest.write_char(')')?;
}
Token::BadString(ref value) => { // During tokenization, an unescaped newline after a quote causes // the token to be a BadString instead of a QuotedString. // The BadString token ends just before the newline // (which is in a separate WhiteSpace token), // and therefore does not have a closing quote.
dest.write_char('"')?;
CssStringWriter::new(dest).write_str(value)?;
}
Token::CloseParenthesis => dest.write_str(")")?,
Token::CloseSquareBracket => dest.write_str("]")?,
Token::CloseCurlyBracket => dest.write_str("}")?,
}
Ok(())
}
}
fn hex_escape<W>(ascii_byte: u8, dest: &mut W) -> fmt::Result where
W: fmt::Write,
{ static HEX_DIGITS: &[u8; 16] = b"0123456789abcdef"; let b3; let b4; let bytes = if ascii_byte > 0x0F { let high = (ascii_byte >> 4) as usize; let low = (ascii_byte & 0x0F) as usize;
b4 = [b'\\', HEX_DIGITS[high], HEX_DIGITS[low], b' '];
&b4[..]
} else {
b3 = [b'\\', HEX_DIGITS[ascii_byte as usize], b' '];
&b3[..]
};
dest.write_str(unsafe { str::from_utf8_unchecked(bytes) })
}
/// Write a CSS name, like a custom property name. /// /// You should only use this when you know what you're doing, when in doubt, /// consider using `serialize_identifier`. pubfn serialize_name<W>(value: &str, dest: &mut W) -> fmt::Result where
W: fmt::Write,
{ letmut chunk_start = 0; for (i, b) in value.bytes().enumerate() { let escaped = match_byte! { b,
b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' | b'_' | b'-' => continue,
b'\0' => Some("\u{FFFD}"),
b => { if !b.is_ascii() { continue;
}
None
},
};
dest.write_str(&value[chunk_start..i])?; iflet Some(escaped) = escaped {
dest.write_str(escaped)?;
} elseif (b'\x01'..=b'\x1F').contains(&b) || b == b'\x7F' {
hex_escape(b, dest)?;
} else {
char_escape(b, dest)?;
}
chunk_start = i + 1;
}
dest.write_str(&value[chunk_start..])
}
fn serialize_unquoted_url<W>(value: &str, dest: &mut W) -> fmt::Result where
W: fmt::Write,
{ letmut chunk_start = 0; for (i, b) in value.bytes().enumerate() { let hex = match_byte! { b,
b'\0'..=b' ' | b'\x7F' => true,
b'(' | b')' | b'"' | b'\'' | b'\\' => false,
_ => continue,
};
dest.write_str(&value[chunk_start..i])?; if hex {
hex_escape(b, dest)?;
} else {
char_escape(b, dest)?;
}
chunk_start = i + 1;
}
dest.write_str(&value[chunk_start..])
}
/// Write a double-quoted CSS string token, escaping content as necessary. pubfn serialize_string<W>(value: &str, dest: &mut W) -> fmt::Result where
W: fmt::Write,
{
dest.write_str("\"")?;
CssStringWriter::new(dest).write_str(value)?;
dest.write_str("\"")?;
Ok(())
}
/// A `fmt::Write` adapter that escapes text for writing as a double-quoted CSS string. /// Quotes are not included. /// /// Typical usage: /// /// ```{rust,ignore} /// fn write_foo<W>(foo: &Foo, dest: &mut W) -> fmt::Result where W: fmt::Write { /// dest.write_str("\"")?; /// { /// let mut string_dest = CssStringWriter::new(dest); /// // Write into string_dest... /// } /// dest.write_str("\"")?; /// Ok(()) /// } /// ``` pubstruct CssStringWriter<'a, W> {
inner: &'a mut W,
}
impl<'a, W> CssStringWriter<'a, W> where
W: fmt::Write,
{ /// Wrap a text writer to create a `CssStringWriter`. pubfn new(inner: &'a mut W) -> CssStringWriter<'a, W> {
CssStringWriter { inner }
}
}
impl<'a, W> fmt::Write for CssStringWriter<'a, W> where
W: fmt::Write,
{ fn write_str(&mutself, s: &str) -> fmt::Result { letmut chunk_start = 0; for (i, b) in s.bytes().enumerate() { let escaped = match_byte! { b,
b'"' => Some("\\\""),
b'\\' => Some("\\\\"),
b'\0' => Some("\u{FFFD}"),
b'\x01'..=b'\x1F' | b'\x7F' => None,
_ => continue,
}; self.inner.write_str(&s[chunk_start..i])?; match escaped {
Some(x) => self.inner.write_str(x)?,
None => hex_escape(b, self.inner)?,
};
chunk_start = i + 1;
} self.inner.write_str(&s[chunk_start..])
}
}
/// A category of token. See the `needs_separator_when_before` method. #[derive(Copy, Clone, Eq, PartialEq, Debug, Default)] pubenum TokenSerializationType { /// No token serialization type. #[default]
Nothing,
/// The `.` or `+` `<delim-token>` type.
DelimDotOrPlus,
/// The `-` `<delim-token>` type.
DelimMinus,
/// The `?` `<delim-token>` type.
DelimQuestion,
/// The `$`, `^`, or `~` `<delim-token>` type.
DelimAssorted,
/// The `=` `<delim-token>` type.
DelimEquals,
/// The `|` `<delim-token>` type.
DelimBar,
/// The `/` `<delim-token>` type.
DelimSlash,
/// The `*` `<delim-token>` type.
DelimAsterisk,
/// The `%` `<delim-token>` type.
DelimPercent,
/// A type indicating any other token.
Other,
}
impl TokenSerializationType { /// Return a value that represents the absence of a token, e.g. before the start of the input. #[deprecated(
since = "0.32.1",
note = "use TokenSerializationType::Nothing or TokenSerializationType::default() instead"
)] pubfn nothing() -> TokenSerializationType {
Default::default()
}
/// If this value is `TokenSerializationType::Nothing`, set it to the given value instead. pubfn set_if_nothing(&mutself, new_value: TokenSerializationType) { if matches!(self, TokenSerializationType::Nothing) {
*self = new_value
}
}
/// Return true if, when a token of category `self` is serialized just before /// a token of category `other` with no whitespace in between, /// an empty comment `/**/` needs to be inserted between them /// so that they are not re-parsed as a single token. /// /// See https://drafts.csswg.org/css-syntax/#serialization /// /// See https://github.com/w3c/csswg-drafts/issues/4088 for the /// `DelimPercent` bits. pubfn needs_separator_when_before(self, other: TokenSerializationType) -> bool { useself::TokenSerializationType::*; matchself {
Ident => matches!(
other,
Ident
| Function
| UrlOrBadUrl
| DelimMinus
| Number
| Percentage
| Dimension
| CDC
| OpenParen
),
AtKeywordOrHash | Dimension => matches!(
other,
Ident | Function | UrlOrBadUrl | DelimMinus | Number | Percentage | Dimension | CDC
),
DelimHash | DelimMinus => matches!(
other,
Ident | Function | UrlOrBadUrl | DelimMinus | Number | Percentage | Dimension
),
Number => matches!(
other,
Ident
| Function
| UrlOrBadUrl
| DelimMinus
| Number
| Percentage
| DelimPercent
| Dimension
),
DelimAt => matches!(other, Ident | Function | UrlOrBadUrl | DelimMinus),
DelimDotOrPlus => matches!(other, Number | Percentage | Dimension),
DelimAssorted | DelimAsterisk => matches!(other, DelimEquals),
DelimBar => matches!(other, DelimEquals | DelimBar | DashMatch),
DelimSlash => matches!(other, DelimAsterisk | SubstringMatch),
Nothing | WhiteSpace | Percentage | UrlOrBadUrl | Function | CDC | OpenParen
| DashMatch | SubstringMatch | DelimQuestion | DelimEquals | DelimPercent | Other => { false
}
}
}
}
impl<'a> Token<'a> { /// Categorize a token into a type that determines when `/**/` needs to be inserted /// between two tokens when serialized next to each other without whitespace in between. /// /// See the `TokenSerializationType::needs_separator_when_before` method. pubfn serialization_type(&self) -> TokenSerializationType { useself::TokenSerializationType::*; matchself {
Token::Ident(_) => Ident,
Token::AtKeyword(_) | Token::Hash(_) | Token::IDHash(_) => AtKeywordOrHash,
Token::UnquotedUrl(_) | Token::BadUrl(_) => UrlOrBadUrl,
Token::Delim('#') => DelimHash,
Token::Delim('@') => DelimAt,
Token::Delim('.') | Token::Delim('+') => DelimDotOrPlus,
Token::Delim('-') => DelimMinus,
Token::Delim('?') => DelimQuestion,
Token::Delim('$') | Token::Delim('^') | Token::Delim('~') => DelimAssorted,
Token::Delim('%') => DelimPercent,
Token::Delim('=') => DelimEquals,
Token::Delim('|') => DelimBar,
Token::Delim('/') => DelimSlash,
Token::Delim('*') => DelimAsterisk,
Token::Number { .. } => Number,
Token::Percentage { .. } => Percentage,
Token::Dimension { .. } => Dimension,
Token::WhiteSpace(_) => WhiteSpace,
Token::Comment(_) => DelimSlash,
Token::DashMatch => DashMatch,
Token::SubstringMatch => SubstringMatch,
Token::CDC => CDC,
Token::Function(_) => Function,
Token::ParenthesisBlock => OpenParen,
Token::SquareBracketBlock
| Token::CurlyBracketBlock
| Token::CloseParenthesis
| Token::CloseSquareBracket
| Token::CloseCurlyBracket
| Token::QuotedString(_)
| Token::BadString(_)
| Token::Delim(_)
| Token::Colon
| Token::Semicolon
| Token::Comma
| Token::CDO
| Token::IncludeMatch
| Token::PrefixMatch
| Token::SuffixMatch => Other,
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.21 Sekunden
(vorverarbeitet am 2026-06-19)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.