diff options
author | Anthony Ramine <n.oxyde@gmail.com> | 2018-01-22 19:58:01 +0100 |
---|---|---|
committer | Anthony Ramine <n.oxyde@gmail.com> | 2018-01-23 10:41:42 +0100 |
commit | cd8f96cc9edd5debbb738ffa3124e3d46c5d8bcf (patch) | |
tree | 5c6f46a882b529a5de7b792d44b0ee432ee327bd /components/style_traits/values.rs | |
parent | 3672856efa31195aaa89b007f790df925474001a (diff) | |
download | servo-cd8f96cc9edd5debbb738ffa3124e3d46c5d8bcf.tar.gz servo-cd8f96cc9edd5debbb738ffa3124e3d46c5d8bcf.zip |
Change ToCss to take a CssWriter<W>
This more concrete wrapper type can write a prefix the very first time something
is written to it. This allows removing plenty of useless monomorphisations caused
by the former W/SequenceWriter<W> pair of types.
Diffstat (limited to 'components/style_traits/values.rs')
-rw-r--r-- | components/style_traits/values.rs | 201 |
1 files changed, 99 insertions, 102 deletions
diff --git a/components/style_traits/values.rs b/components/style_traits/values.rs index 3e951548e6c..56574ac161d 100644 --- a/components/style_traits/values.rs +++ b/components/style_traits/values.rs @@ -34,7 +34,7 @@ use std::fmt::{self, Write}; /// implement `Debug` by a single call to `ToCss::to_css`. pub trait ToCss { /// Serialize `self` in CSS syntax, writing to `dest`. - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write; + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write; /// Serialize `self` in CSS syntax and return a string. /// @@ -42,27 +42,27 @@ pub trait ToCss { #[inline] fn to_css_string(&self) -> String { let mut s = String::new(); - self.to_css(&mut s).unwrap(); + self.to_css(&mut CssWriter::new(&mut s)).unwrap(); s } } impl<'a, T> ToCss for &'a T where T: ToCss + ?Sized { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write { + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write { (*self).to_css(dest) } } impl ToCss for str { #[inline] - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write { + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write { serialize_string(self, dest) } } impl ToCss for String { #[inline] - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write { + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write { serialize_string(self, dest) } } @@ -72,11 +72,62 @@ where T: ToCss, { #[inline] - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write { + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write { self.as_ref().map_or(Ok(()), |value| value.to_css(dest)) } } +/// A writer tailored for serialising CSS. +/// +/// Coupled with SequenceWriter, this allows callers to transparently handle +/// things like comma-separated values etc. +pub struct CssWriter<'w, W: 'w> { + inner: &'w mut W, + prefix: Option<&'static str>, +} + +impl<'w, W> CssWriter<'w, W> +where + W: Write, +{ + /// Creates a new `CssWriter`. + #[inline] + pub fn new(inner: &'w mut W) -> Self { + Self { inner, prefix: Some("") } + } +} + +impl<'w, W> Write for CssWriter<'w, W> +where + W: Write, +{ + #[inline] + fn write_str(&mut self, s: &str) -> fmt::Result { + if s.is_empty() { + return Ok(()); + } + if let Some(prefix) = self.prefix.take() { + // We are going to write things, but first we need to write + // the prefix that was set by `SequenceWriter::item`. + if !prefix.is_empty() { + self.inner.write_str(prefix)?; + } + } + self.inner.write_str(s) + } + + #[inline] + fn write_char(&mut self, c: char) -> fmt::Result { + if let Some(prefix) = self.prefix.take() { + // See comment in `write_str`. + if !prefix.is_empty() { + self.inner.write_str(prefix)?; + } + } + self.inner.write_char(c) + } +} + #[macro_export] macro_rules! serialize_function { ($dest: expr, $name: ident($( $arg: expr, )+)) => { @@ -96,22 +147,23 @@ macro_rules! serialize_function { } /// Convenience wrapper to serialise CSS values separated by a given string. -pub struct SequenceWriter<'a, W> { - writer: TrackedWriter<W>, - separator: &'a str, +pub struct SequenceWriter<'a, 'b: 'a, W: 'b> { + inner: &'a mut CssWriter<'b, W>, + separator: &'static str, } -impl<'a, W> SequenceWriter<'a, W> +impl<'a, 'b, W> SequenceWriter<'a, 'b, W> where - W: Write, + W: Write + 'b, { /// Create a new sequence writer. #[inline] - pub fn new(writer: W, separator: &'a str) -> Self { - SequenceWriter { - writer: TrackedWriter::new(writer), - separator: separator, + pub fn new(inner: &'a mut CssWriter<'b, W>, separator: &'static str) -> Self { + if inner.prefix.is_none() { + // See comment in `item`. + inner.prefix = Some(""); } + Self { inner, separator } } /// Serialises a CSS value, writing any separator as necessary. @@ -125,89 +177,34 @@ where where T: ToCss, { - if self.writer.has_written { - item.to_css(&mut PrefixedWriter::new(&mut self.writer, self.separator)) - } else { - item.to_css(&mut self.writer) - } - } -} - -struct TrackedWriter<W> { - writer: W, - has_written: bool, -} - -impl<W> TrackedWriter<W> -where - W: Write, -{ - #[inline] - fn new(writer: W) -> Self { - TrackedWriter { - writer: writer, - has_written: false, + let old_prefix = self.inner.prefix; + if old_prefix.is_none() { + // If there is no prefix in the inner writer, a previous + // call to this method produced output, which means we need + // to write the separator next time we produce output again. + self.inner.prefix = Some(self.separator); } - } -} - -impl<W> Write for TrackedWriter<W> -where - W: Write, -{ - #[inline] - fn write_str(&mut self, s: &str) -> fmt::Result { - if !s.is_empty() { - self.has_written = true; - } - self.writer.write_str(s) - } - - #[inline] - fn write_char(&mut self, c: char) -> fmt::Result { - self.has_written = true; - self.writer.write_char(c) - } -} - -struct PrefixedWriter<'a, W> { - writer: W, - prefix: Option<&'a str>, -} - -impl<'a, W> PrefixedWriter<'a, W> -where - W: Write, -{ - #[inline] - fn new(writer: W, prefix: &'a str) -> Self { - PrefixedWriter { - writer: writer, - prefix: Some(prefix), - } - } -} - -impl<'a, W> Write for PrefixedWriter<'a, W> -where - W: Write, -{ - #[inline] - fn write_str(&mut self, s: &str) -> fmt::Result { - if !s.is_empty() { - if let Some(prefix) = self.prefix.take() { - self.writer.write_str(prefix)?; + item.to_css(&mut self.inner)?; + match (old_prefix, self.inner.prefix) { + (_, None) => { + // This call produced output and cleaned up after itself. + } + (None, Some(p)) => { + // Some previous call to `item` produced output, + // but this one did not, prefix should be the same as + // the one we set. + debug_assert_eq!(self.separator, p); + // We clean up here even though it's not necessary just + // to be able to do all these assertion checks. + self.inner.prefix = None; + } + (Some(old), Some(new)) => { + // No previous call to `item` produced output, and this one + // either. + debug_assert_eq!(old, new); } } - self.writer.write_str(s) - } - - #[inline] - fn write_char(&mut self, c: char) -> fmt::Result { - if let Some(prefix) = self.prefix.take() { - self.writer.write_str(prefix)?; - } - self.writer.write_char(c) + Ok(()) } } @@ -332,7 +329,7 @@ impl OneOrMoreSeparated for UnicodeRange { } impl<T> ToCss for Vec<T> where T: ToCss + OneOrMoreSeparated { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write { + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write { let mut iter = self.iter(); iter.next().unwrap().to_css(dest)?; for item in iter { @@ -344,7 +341,7 @@ impl<T> ToCss for Vec<T> where T: ToCss + OneOrMoreSeparated { } impl<T> ToCss for Box<T> where T: ?Sized + ToCss { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write, { (**self).to_css(dest) @@ -352,7 +349,7 @@ impl<T> ToCss for Box<T> where T: ?Sized + ToCss { } impl<T> ToCss for Arc<T> where T: ?Sized + ToCss { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write, { (**self).to_css(dest) @@ -360,7 +357,7 @@ impl<T> ToCss for Arc<T> where T: ?Sized + ToCss { } impl ToCss for Au { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write { + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write { self.to_f64_px().to_css(dest)?; dest.write_str("px") } @@ -369,7 +366,7 @@ impl ToCss for Au { macro_rules! impl_to_css_for_predefined_type { ($name: ty) => { impl<'a> ToCss for $name { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write { + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write { ::cssparser::ToCss::to_css(self, dest) } } @@ -479,11 +476,11 @@ macro_rules! __define_css_keyword_enum__actual { } impl $crate::ToCss for $name { - fn to_css<W>(&self, dest: &mut W) -> ::std::fmt::Result + fn to_css<W>(&self, dest: &mut $crate::CssWriter<W>) -> ::std::fmt::Result where W: ::std::fmt::Write { match *self { - $( $name::$variant => dest.write_str($css) ),+ + $( $name::$variant => ::std::fmt::Write::write_str(dest, $css) ),+ } } } |