support compressed lists and number values

This commit is contained in:
Connor Skees 2021-07-25 09:53:18 -04:00
parent 3ced8feed5
commit 94fe52a81d
28 changed files with 521 additions and 192 deletions

View File

@ -34,6 +34,7 @@ complex uses of @at-root
media queries with @import
/ as a separator in color functions, e.g. rgba(255, 255, 255 / 0)
Infinity and -Infinity
builtin meta function keywords
```
All known missing features and bugs are tracked in [#19](https://github.com/connorskees/grass/issues/19).

View File

@ -64,7 +64,7 @@ impl CallArgs {
CallArgs(HashMap::new(), span)
}
pub fn to_css_string(self) -> SassResult<Spanned<String>> {
pub fn to_css_string(self, is_compressed: bool) -> SassResult<Spanned<String>> {
let mut string = String::with_capacity(2 + self.len() * 10);
string.push('(');
let mut span = self.1;
@ -88,7 +88,7 @@ impl CallArgs {
.iter()
.map(|a| {
span = span.merge(a.span);
a.node.to_css_string(a.span)
a.node.to_css_string(a.span, is_compressed)
})
.collect::<SassResult<Vec<Cow<'static, str>>>>()?
.join(", "),

View File

@ -90,13 +90,17 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
let mut string = format!(
"{}({}, {}, {}",
name,
v.to_css_string(args.span())?,
saturation.to_css_string(args.span())?,
lightness.to_css_string(args.span())?
v.to_css_string(args.span(), parser.options.is_compressed())?,
saturation.to_css_string(args.span(), parser.options.is_compressed())?,
lightness.to_css_string(args.span(), parser.options.is_compressed())?
);
if !args.is_empty() {
string.push_str(", ");
string.push_str(&args.get_err(3, "alpha")?.to_css_string(args.span())?);
string.push_str(
&args
.get_err(3, "alpha")?
.to_css_string(args.span(), parser.options.is_compressed())?,
);
}
string.push(')');
return Ok(Value::String(string, QuoteKind::None));
@ -117,13 +121,17 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
let mut string = format!(
"{}({}, {}, {}",
name,
hue,
v.to_css_string(args.span())?,
lightness.to_css_string(args.span())?
hue.to_string(parser.options.is_compressed()),
v.to_css_string(args.span(), parser.options.is_compressed())?,
lightness.to_css_string(args.span(), parser.options.is_compressed())?
);
if !args.is_empty() {
string.push_str(", ");
string.push_str(&args.get_err(3, "alpha")?.to_css_string(args.span())?);
string.push_str(
&args
.get_err(3, "alpha")?
.to_css_string(args.span(), parser.options.is_compressed())?,
);
}
string.push(')');
return Ok(Value::String(string, QuoteKind::None));
@ -132,7 +140,7 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
return Err((
format!(
"$saturation: {} is not a number.",
v.to_css_string(args.span())?
v.to_css_string(args.span(), parser.options.is_compressed())?
),
args.span(),
)
@ -146,13 +154,17 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
let mut string = format!(
"{}({}, {}, {}",
name,
hue,
saturation,
v.to_css_string(args.span())?
hue.to_string(parser.options.is_compressed()),
saturation.to_string(parser.options.is_compressed()),
v.to_css_string(args.span(), parser.options.is_compressed())?
);
if !args.is_empty() {
string.push_str(", ");
string.push_str(&args.get_err(3, "alpha")?.to_css_string(args.span())?);
string.push_str(
&args
.get_err(3, "alpha")?
.to_css_string(args.span(), parser.options.is_compressed())?,
);
}
string.push(')');
return Ok(Value::String(string, QuoteKind::None));
@ -161,7 +173,7 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
return Err((
format!(
"$lightness: {} is not a number.",
v.to_css_string(args.span())?
v.to_css_string(args.span(), parser.options.is_compressed())?
),
args.span(),
)
@ -180,7 +192,7 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
return Err((
format!(
"$alpha: Expected {} to have no units or \"%\".",
v.to_css_string(args.span())?
v.to_css_string(args.span(), parser.options.is_compressed())?
),
args.span(),
)
@ -191,10 +203,10 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
format!(
"{}({}, {}, {}, {})",
name,
hue,
saturation,
lightness,
v.to_css_string(args.span())?
hue.to_string(parser.options.is_compressed()),
saturation.to_string(parser.options.is_compressed()),
lightness.to_string(parser.options.is_compressed()),
v.to_css_string(args.span(), parser.options.is_compressed())?
),
QuoteKind::None,
));
@ -276,7 +288,7 @@ pub(crate) fn adjust_hue(mut args: CallArgs, parser: &mut Parser) -> SassResult<
return Err((
format!(
"$degrees: {} is not a number.",
v.to_css_string(args.span())?
v.to_css_string(args.span(), parser.options.is_compressed())?
),
args.span(),
)
@ -305,7 +317,7 @@ fn lighten(mut args: CallArgs, parser: &mut Parser) -> SassResult<Value> {
return Err((
format!(
"$amount: {} is not a number.",
v.to_css_string(args.span())?
v.to_css_string(args.span(), false)?
),
args.span(),
)
@ -334,7 +346,7 @@ fn darken(mut args: CallArgs, parser: &mut Parser) -> SassResult<Value> {
return Err((
format!(
"$amount: {} is not a number.",
v.to_css_string(args.span())?
v.to_css_string(args.span(), false)?
),
args.span(),
)
@ -350,7 +362,8 @@ fn saturate(mut args: CallArgs, parser: &mut Parser) -> SassResult<Value> {
return Ok(Value::String(
format!(
"saturate({})",
args.get_err(0, "amount")?.to_css_string(args.span())?
args.get_err(0, "amount")?
.to_css_string(args.span(), false)?
),
QuoteKind::None,
));
@ -363,7 +376,7 @@ fn saturate(mut args: CallArgs, parser: &mut Parser) -> SassResult<Value> {
return Err((
format!(
"$amount: {} is not a number.",
v.to_css_string(args.span())?
v.to_css_string(args.span(), false)?
),
args.span(),
)
@ -374,7 +387,7 @@ fn saturate(mut args: CallArgs, parser: &mut Parser) -> SassResult<Value> {
Value::Color(c) => c,
Value::Dimension(Some(n), u, _) => {
return Ok(Value::String(
format!("saturate({}{})", n, u),
format!("saturate({}{})", n.inspect(), u),
QuoteKind::None,
))
}
@ -408,7 +421,7 @@ fn desaturate(mut args: CallArgs, parser: &mut Parser) -> SassResult<Value> {
return Err((
format!(
"$amount: {} is not a number.",
v.to_css_string(args.span())?
v.to_css_string(args.span(), parser.options.is_compressed())?
),
args.span(),
)
@ -424,7 +437,7 @@ pub(crate) fn grayscale(mut args: CallArgs, parser: &mut Parser) -> SassResult<V
Value::Color(c) => c,
Value::Dimension(Some(n), u, _) => {
return Ok(Value::String(
format!("grayscale({}{})", n, u),
format!("grayscale({}{})", n.inspect(), u),
QuoteKind::None,
))
}
@ -471,7 +484,7 @@ pub(crate) fn invert(mut args: CallArgs, parser: &mut Parser) -> SassResult<Valu
return Err((
format!(
"$weight: {} is not a number.",
v.to_css_string(args.span())?
v.to_css_string(args.span(), parser.options.is_compressed())?
),
args.span(),
)
@ -491,7 +504,7 @@ pub(crate) fn invert(mut args: CallArgs, parser: &mut Parser) -> SassResult<Valu
.into());
}
Ok(Value::String(
format!("invert({}{})", n, u),
format!("invert({}{})", n.inspect(), u),
QuoteKind::None,
))
}

View File

@ -74,7 +74,7 @@ pub(crate) fn opacity(mut args: CallArgs, parser: &mut Parser) -> SassResult<Val
match args.get_err(0, "color")? {
Value::Color(c) => Ok(Value::Dimension(Some(c.alpha()), Unit::None, true)),
Value::Dimension(Some(num), unit, _) => Ok(Value::String(
format!("opacity({}{})", num, unit),
format!("opacity({}{})", num.inspect(), unit),
QuoteKind::None,
)),
Value::Dimension(None, ..) => todo!(),

View File

@ -50,9 +50,9 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
format!(
"{}({}, {}, {})",
name,
red.to_css_string(args.span())?,
green.to_css_string(args.span())?,
v.to_css_string(args.span())?
red.to_css_string(args.span(), parser.options.is_compressed())?,
green.to_css_string(args.span(), parser.options.is_compressed())?,
v.to_css_string(args.span(), parser.options.is_compressed())?
),
QuoteKind::None,
));
@ -78,11 +78,16 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
Some(red) => format!(
"{}({}, {}, {})",
name,
red.to_css_string(args.span())?,
v.to_css_string(args.span())?,
blue
red.to_css_string(args.span(), parser.options.is_compressed())?,
v.to_css_string(args.span(), parser.options.is_compressed())?,
blue.to_string(parser.options.is_compressed())
),
None => format!(
"{}({} {})",
name,
v.to_css_string(args.span(), parser.options.is_compressed())?,
blue.to_string(parser.options.is_compressed())
),
None => format!("{}({} {})", name, v.to_css_string(args.span())?, blue),
};
return Ok(Value::String(string, QuoteKind::None));
}
@ -107,9 +112,9 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
format!(
"{}({}, {}, {})",
name,
v.to_css_string(args.span())?,
green,
blue
v.to_css_string(args.span(), parser.options.is_compressed())?,
green.to_string(parser.options.is_compressed()),
blue.to_string(parser.options.is_compressed())
),
QuoteKind::None,
));
@ -136,8 +141,8 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
format!(
"{}({}, {})",
name,
v.to_css_string(args.span())?,
alpha.to_css_string(args.span())?
v.to_css_string(args.span(), parser.options.is_compressed())?,
alpha.to_css_string(args.span(), parser.options.is_compressed())?
),
QuoteKind::None,
));
@ -158,7 +163,7 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
return Err((
format!(
"$alpha: Expected {} to have no units or \"%\".",
v.to_css_string(args.span())?
v.to_css_string(args.span(), parser.options.is_compressed())?
),
args.span(),
)
@ -169,10 +174,10 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
format!(
"{}({}, {}, {}, {})",
name,
color.red(),
color.green(),
color.blue(),
v.to_css_string(args.span())?
color.red().to_string(parser.options.is_compressed()),
color.green().to_string(parser.options.is_compressed()),
color.blue().to_string(parser.options.is_compressed()),
v.to_css_string(args.span(), parser.options.is_compressed())?
),
QuoteKind::None,
));
@ -197,7 +202,7 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
return Err((
format!(
"$red: Expected {} to have no units or \"%\".",
v.to_css_string(args.span())?
v.to_css_string(args.span(), parser.options.is_compressed())?
),
args.span(),
)
@ -209,13 +214,17 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
let mut string = format!(
"{}({}, {}, {}",
name,
v.to_css_string(args.span())?,
green.to_css_string(args.span())?,
blue.to_css_string(args.span())?
v.to_css_string(args.span(), parser.options.is_compressed())?,
green.to_css_string(args.span(), parser.options.is_compressed())?,
blue.to_css_string(args.span(), parser.options.is_compressed())?
);
if !args.is_empty() {
string.push_str(", ");
string.push_str(&args.get_err(3, "alpha")?.to_css_string(args.span())?);
string.push_str(
&args
.get_err(3, "alpha")?
.to_css_string(args.span(), parser.options.is_compressed())?,
);
}
string.push(')');
return Ok(Value::String(string, QuoteKind::None));
@ -238,7 +247,7 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
return Err((
format!(
"$green: Expected {} to have no units or \"%\".",
v.to_css_string(args.span())?
v.to_css_string(args.span(), parser.options.is_compressed())?
),
args.span(),
)
@ -249,13 +258,17 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
let mut string = format!(
"{}({}, {}, {}",
name,
red,
v.to_css_string(args.span())?,
blue.to_css_string(args.span())?
red.to_string(parser.options.is_compressed()),
v.to_css_string(args.span(), parser.options.is_compressed())?,
blue.to_css_string(args.span(), parser.options.is_compressed())?
);
if !args.is_empty() {
string.push_str(", ");
string.push_str(&args.get_err(3, "alpha")?.to_css_string(args.span())?);
string.push_str(
&args
.get_err(3, "alpha")?
.to_css_string(args.span(), parser.options.is_compressed())?,
);
}
string.push(')');
return Ok(Value::String(string, QuoteKind::None));
@ -278,7 +291,7 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
return Err((
format!(
"$blue: Expected {} to have no units or \"%\".",
v.to_css_string(args.span())?
v.to_css_string(args.span(), parser.options.is_compressed())?
),
args.span(),
)
@ -288,13 +301,17 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
let mut string = format!(
"{}({}, {}, {}",
name,
red,
green,
v.to_css_string(args.span())?
red.to_string(parser.options.is_compressed()),
green.to_string(parser.options.is_compressed()),
v.to_css_string(args.span(), parser.options.is_compressed())?
);
if !args.is_empty() {
string.push_str(", ");
string.push_str(&args.get_err(3, "alpha")?.to_css_string(args.span())?);
string.push_str(
&args
.get_err(3, "alpha")?
.to_css_string(args.span(), parser.options.is_compressed())?,
);
}
string.push(')');
return Ok(Value::String(string, QuoteKind::None));
@ -319,7 +336,7 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
return Err((
format!(
"$alpha: Expected {} to have no units or \"%\".",
v.to_css_string(args.span())?
v.to_css_string(args.span(), parser.options.is_compressed())?
),
args.span(),
)
@ -329,10 +346,10 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas
let string = format!(
"{}({}, {}, {}, {})",
name,
red,
green,
blue,
v.to_css_string(args.span())?
red.to_string(parser.options.is_compressed()),
green.to_string(parser.options.is_compressed()),
blue.to_string(parser.options.is_compressed()),
v.to_css_string(args.span(), parser.options.is_compressed())?
);
return Ok(Value::String(string, QuoteKind::None));
}
@ -429,7 +446,7 @@ pub(crate) fn mix(mut args: CallArgs, parser: &mut Parser) -> SassResult<Value>
return Err((
format!(
"$weight: {} is not a number.",
v.to_css_string(args.span())?
v.to_css_string(args.span(), parser.options.is_compressed())?
),
args.span(),
)

View File

@ -45,7 +45,7 @@ pub(crate) fn nth(mut args: CallArgs, parser: &mut Parser) -> SassResult<Value>
return Err((
format!(
"$n: Invalid index {}{} for a list with {} elements.",
n,
n.inspect(),
unit,
list.len()
),
@ -55,7 +55,7 @@ pub(crate) fn nth(mut args: CallArgs, parser: &mut Parser) -> SassResult<Value>
}
if n.is_decimal() {
return Err((format!("$n: {} is not an int.", n), args.span()).into());
return Err((format!("$n: {} is not an int.", n.inspect()), args.span()).into());
}
Ok(list.remove(if n.is_positive() {
@ -114,7 +114,9 @@ pub(crate) fn set_nth(mut args: CallArgs, parser: &mut Parser) -> SassResult<Val
return Err((
format!(
"$n: Invalid index {}{} for a list with {} elements.",
n, unit, len
n.inspect(),
unit,
len
),
args.span(),
)
@ -122,7 +124,7 @@ pub(crate) fn set_nth(mut args: CallArgs, parser: &mut Parser) -> SassResult<Val
}
if n.is_decimal() {
return Err((format!("$n: {} is not an int.", n), args.span()).into());
return Err((format!("$n: {} is not an int.", n.inspect()), args.span()).into());
}
let val = args.get_err(2, "value")?;

View File

@ -4,7 +4,13 @@ macro_rules! bound {
return Err((
format!(
"${}: Expected {}{} to be within {}{} and {}{}.",
$name, $arg, $unit, $low, $unit, $high, $unit,
$name,
$arg.inspect(),
$unit,
$low,
$unit,
$high,
$unit,
),
$args.span(),
)
@ -18,7 +24,13 @@ macro_rules! bound {
return Err((
format!(
"${}: Expected {}{} to be within {}{} and {}{}.",
$name, $arg, $unit, $low, $unit, $high, $unit,
$name,
$arg.inspect(),
$unit,
$low,
$unit,
$high,
$unit,
),
$args.span(),
)

View File

@ -149,12 +149,16 @@ pub(crate) fn random(mut args: CallArgs, parser: &mut Parser) -> SassResult<Valu
}
if limit.is_decimal() {
return Err((format!("$limit: {} is not an int.", limit), args.span()).into());
return Err((
format!("$limit: {} is not an int.", limit.inspect()),
args.span(),
)
.into());
}
if limit.is_zero() || limit.is_negative() {
return Err((
format!("$limit: Must be greater than 0, was {}.", limit),
format!("$limit: Must be greater than 0, was {}.", limit.inspect()),
args.span(),
)
.into());
@ -164,7 +168,10 @@ pub(crate) fn random(mut args: CallArgs, parser: &mut Parser) -> SassResult<Valu
Some(n) => n,
None => {
return Err((
format!("max must be in range 0 < max \u{2264} 2^32, was {}", limit),
format!(
"max must be in range 0 < max \u{2264} 2^32, was {}",
limit.inspect()
),
args.span(),
)
.into())

View File

@ -100,7 +100,7 @@ pub(crate) fn str_slice(mut args: CallArgs, parser: &mut Parser) -> SassResult<V
let str_len = string.chars().count();
let start = match args.get_err(1, "start-at")? {
Value::Dimension(Some(n), Unit::None, _) if n.is_decimal() => {
return Err((format!("{} is not an int.", n), args.span()).into())
return Err((format!("{} is not an int.", n.inspect()), args.span()).into())
}
Value::Dimension(Some(n), Unit::None, _) if n.is_positive() => {
n.to_integer().to_usize().unwrap_or(str_len + 1)
@ -133,7 +133,7 @@ pub(crate) fn str_slice(mut args: CallArgs, parser: &mut Parser) -> SassResult<V
};
let mut end = match args.default_arg(2, "end-at", Value::Null)? {
Value::Dimension(Some(n), Unit::None, _) if n.is_decimal() => {
return Err((format!("{} is not an int.", n), args.span()).into())
return Err((format!("{} is not an int.", n.inspect()), args.span()).into())
}
Value::Dimension(Some(n), Unit::None, _) if n.is_positive() => {
n.to_integer().to_usize().unwrap_or(str_len + 1)
@ -240,7 +240,11 @@ pub(crate) fn str_insert(mut args: CallArgs, parser: &mut Parser) -> SassResult<
let index = match args.get_err(2, "index")? {
Value::Dimension(Some(n), Unit::None, _) if n.is_decimal() => {
return Err((format!("$index: {} is not an int.", n), args.span()).into())
return Err((
format!("$index: {} is not an int.", n.inspect()),
args.span(),
)
.into())
}
Value::Dimension(Some(n), Unit::None, _) => n,
Value::Dimension(None, Unit::None, ..) => {

View File

@ -590,7 +590,14 @@ fn repr(red: &Number, green: &Number, blue: &Number, alpha: &Number) -> String {
let blue_u8 = into_u8(blue);
if alpha < &Number::one() {
format!("rgba({}, {}, {}, {})", red_u8, green_u8, blue_u8, alpha)
format!(
"rgba({}, {}, {}, {})",
red_u8,
green_u8,
blue_u8,
// todo: is_compressed
alpha.inspect()
)
} else if let Some(c) = NAMED_COLORS.get_by_rgba([red_u8, green_u8, blue_u8]) {
(*c).to_owned()
} else {

View File

@ -99,6 +99,13 @@ impl ListSeparator {
}
}
pub fn as_compressed_str(self) -> &'static str {
match self {
Self::Space => " ",
Self::Comma => ",",
}
}
pub fn name(self) -> &'static str {
match self {
Self::Space => "space",

View File

@ -260,6 +260,10 @@ impl<'a> Options<'a> {
self.unicode_error_messages = unicode_error_messages;
self
}
pub(crate) fn is_compressed(&self) -> bool {
matches!(self.style, OutputStyle::Compressed)
}
}
fn raw_to_parse_error(map: &CodeMap, err: Error, unicode: bool) -> Box<Error> {

View File

@ -511,7 +511,7 @@ impl Formatter for CompressedFormatter {
write!(buf, "}}")?;
}
Toplevel::Style(style) => {
let value = style.value.node.to_css_string(style.value.span)?;
let value = style.value.node.to_css_string(style.value.span, true)?;
write!(buf, "{}:{};", style.property, value)?;
}
}
@ -542,7 +542,7 @@ impl CompressedFormatter {
for style in &mut styles {
match style {
BlockEntry::Style(s) => {
let value = s.value.node.to_css_string(s.value.span)?;
let value = s.value.node.to_css_string(s.value.span, true)?;
write!(buf, "{}:{}", s.property, value)?;
break;
}
@ -554,7 +554,7 @@ impl CompressedFormatter {
for style in styles {
match style {
BlockEntry::Style(s) => {
let value = s.value.node.to_css_string(s.value.span)?;
let value = s.value.node.to_css_string(s.value.span, true)?;
write!(buf, ";{}:{}", s.property, value)?;
}

View File

@ -299,8 +299,11 @@ impl<'a, 'b> Parser<'a, 'b> {
let value = format!(
"{}={}",
left.node.to_css_string(left.span)?,
right.node.to_css_string(right.span)?
left.node
.to_css_string(left.span, self.options.is_compressed())?,
right
.node
.to_css_string(right.span, self.options.is_compressed())?
);
args.insert(

View File

@ -211,7 +211,7 @@ impl<'a, 'b> Parser<'a, 'b> {
let from = match from_val.node {
Value::Dimension(Some(n), ..) => match n.to_i32() {
Some(std::i32::MAX) | Some(std::i32::MIN) | None => {
return Err((format!("{} is not an int.", n), from_val.span).into())
return Err((format!("{} is not an int.", n.inspect()), from_val.span).into())
}
Some(v) => v,
},
@ -229,14 +229,17 @@ impl<'a, 'b> Parser<'a, 'b> {
let to = match to_val.node {
Value::Dimension(Some(n), ..) => match n.to_i32() {
Some(std::i32::MAX) | Some(std::i32::MIN) | None => {
return Err((format!("{} is not an int.", n), to_val.span).into())
return Err((format!("{} is not an int.", n.inspect()), to_val.span).into())
}
Some(v) => v,
},
Value::Dimension(None, ..) => return Err(("NaN is not an int.", from_val.span).into()),
v => {
return Err((
format!("{} is not a number.", v.to_css_string(to_val.span)?),
format!(
"{} is not a number.",
v.to_css_string(to_val.span, self.options.is_compressed())?
),
to_val.span,
)
.into())

View File

@ -64,7 +64,11 @@ impl<'a, 'b> Parser<'a, 'b> {
self.toks.next();
// TODO: if ident, interpolate literally
let interpolation = self.parse_interpolation()?;
buf.push_str(&interpolation.node.to_css_string(interpolation.span)?);
buf.push_str(
&interpolation
.node
.to_css_string(interpolation.span, self.options.is_compressed())?,
);
} else {
self.toks.reset_cursor();
break;
@ -179,7 +183,10 @@ impl<'a, 'b> Parser<'a, 'b> {
self.toks.next();
match self.parse_interpolation()?.node {
Value::String(ref s, ..) => text.push_str(s),
v => text.push_str(v.to_css_string(self.span_before)?.borrow()),
v => text.push_str(
v.to_css_string(self.span_before, self.options.is_compressed())?
.borrow(),
),
}
}
_ => return Err(("Expected identifier.", pos).into()),
@ -268,7 +275,10 @@ impl<'a, 'b> Parser<'a, 'b> {
let interpolation = self.parse_interpolation()?;
match interpolation.node {
Value::String(ref v, ..) => s.push_str(v),
v => s.push_str(v.to_css_string(interpolation.span)?.borrow()),
v => s.push_str(
v.to_css_string(interpolation.span, self.options.is_compressed())?
.borrow(),
),
};
continue;
}

View File

@ -126,7 +126,11 @@ impl<'a, 'b> Parser<'a, 'b> {
match tok.kind {
'#' => {
if self.consume_char_if_exists('{') {
string.push_str(&self.parse_interpolation()?.to_css_string(span)?);
string.push_str(
&self
.parse_interpolation()?
.to_css_string(span, self.options.is_compressed())?,
);
} else {
string.push('#');
}

View File

@ -51,7 +51,10 @@ impl<'a, 'b> Parser<'a, 'b> {
_ => false,
})?;
value.node.unquote().to_css_string(value.span)
value
.node
.unquote()
.to_css_string(value.span, self.options.is_compressed())
}
pub(super) fn parse_media_query_list(&mut self) -> SassResult<String> {
@ -92,7 +95,11 @@ impl<'a, 'b> Parser<'a, 'b> {
})?;
self.expect_char(')')?;
buf.push_str(&value.node.to_css_string(value.span)?);
buf.push_str(
&value
.node
.to_css_string(value.span, self.options.is_compressed())?,
);
self.whitespace_or_comment();
buf.push(')');

View File

@ -210,7 +210,7 @@ impl<'a, 'b> Parser<'a, 'b> {
self.consume_char_if_exists(';');
self.warn(&Spanned {
node: message.to_css_string(span)?,
node: message.to_css_string(span, false)?,
span,
});
}
@ -407,7 +407,11 @@ impl<'a, 'b> Parser<'a, 'b> {
match kind {
'#' => {
if self.consume_char_if_exists('{') {
string.push_str(&self.parse_interpolation()?.to_css_string(span)?);
string.push_str(
&self
.parse_interpolation()?
.to_css_string(span, self.options.is_compressed())?,
);
} else {
string.push('#');
}
@ -513,7 +517,11 @@ impl<'a, 'b> Parser<'a, 'b> {
}
('#', Some(Token { kind: '{', .. })) => {
self.toks.next();
comment.push_str(&self.parse_interpolation()?.to_css_string(span)?);
comment.push_str(
&self
.parse_interpolation()?
.to_css_string(span, self.options.is_compressed())?,
);
continue;
}
(..) => comment.push(tok.kind),
@ -541,7 +549,7 @@ impl<'a, 'b> Parser<'a, 'b> {
let interpolation = self.parse_interpolation()?;
Ok(match interpolation.node {
Value::String(v, ..) => Cow::owned(v),
v => v.to_css_string(interpolation.span)?,
v => v.to_css_string(interpolation.span, self.options.is_compressed())?,
})
}
@ -855,7 +863,7 @@ impl<'a, 'b> Parser<'a, 'b> {
return Err((
format!(
"compound selectors may no longer be extended.\nConsider `@extend {}` instead.\nSee http://bit.ly/ExtendCompound for details.\n",
compound.components.iter().map(|x| x.to_string()).collect::<Vec<String>>().join(", ")
compound.components.iter().map(ToString::to_string).collect::<Vec<String>>().join(", ")
)
, self.span_before).into());
}
@ -922,7 +930,11 @@ impl<'a, 'b> Parser<'a, 'b> {
self.toks.next();
self.span_before = pos;
let interpolation = self.parse_interpolation()?;
params.push_str(&interpolation.node.to_css_string(interpolation.span)?);
params.push_str(
&interpolation
.node
.to_css_string(interpolation.span, self.options.is_compressed())?,
);
continue;
}

View File

@ -197,7 +197,9 @@ impl<'a, 'b> Parser<'a, 'b> {
};
let Spanned { node: module, span } = self.parse_quoted_string(quote)?;
let module_name = module.unquote().to_css_string(span)?;
let module_name = module
.unquote()
.to_css_string(span, self.options.is_compressed())?;
self.whitespace_or_comment();

View File

@ -20,7 +20,11 @@ impl<'a, 'b> Parser<'a, 'b> {
self.span_before = pos;
self.toks.next();
let interpolation = self.parse_interpolation()?;
buf.push_str(&interpolation.node.to_css_string(interpolation.span)?);
buf.push_str(
&interpolation
.node
.to_css_string(interpolation.span, self.options.is_compressed())?,
);
} else {
buf.push('#');
}
@ -93,7 +97,10 @@ impl<'a, 'b> Parser<'a, 'b> {
let interpolation = self.parse_interpolation()?;
match interpolation.node {
Value::String(ref s, ..) => buf.push_str(s),
v => buf.push_str(v.to_css_string(interpolation.span)?.borrow()),
v => buf.push_str(
v.to_css_string(interpolation.span, self.options.is_compressed())?
.borrow(),
),
};
} else {
buf.push('#');
@ -142,7 +149,11 @@ impl<'a, 'b> Parser<'a, 'b> {
match kind {
'+' | '-' | '0'..='9' => {
let number = self.parse_dimension(&|_| false)?;
buf.push_str(&number.node.to_css_string(number.span)?);
buf.push_str(
&number
.node
.to_css_string(number.span, self.options.is_compressed())?,
);
}
'#' => {
self.toks.next();
@ -316,7 +327,7 @@ impl<'a, 'b> Parser<'a, 'b> {
q @ ('"' | '\'') => {
self.toks.next();
let s = self.parse_quoted_string(q)?;
buffer.push_str(&s.node.to_css_string(s.span)?);
buffer.push_str(&s.node.to_css_string(s.span, self.options.is_compressed())?);
wrote_newline = false;
}
'/' => {

View File

@ -122,14 +122,26 @@ impl<'a, 'b: 'a, 'c> ValueVisitor<'a, 'b, 'c> {
Value::Dimension(Some(-n), u, should_divide)
}
Value::Dimension(None, u, should_divide) => Value::Dimension(None, u, should_divide),
v => Value::String(format!("-{}", v.to_css_string(self.span)?), QuoteKind::None),
v => Value::String(
format!(
"-{}",
v.to_css_string(self.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
),
})
}
fn unary_plus(&self, val: Value) -> SassResult<Value> {
Ok(match val {
v @ Value::Dimension(..) => v,
v => Value::String(format!("+{}", v.to_css_string(self.span)?), QuoteKind::None),
v => Value::String(
format!(
"+{}",
v.to_css_string(self.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
),
})
}
@ -176,28 +188,36 @@ impl<'a, 'b: 'a, 'c> ValueVisitor<'a, 'b, 'c> {
}
Value::True | Value::False => match right {
Value::String(s, QuoteKind::Quoted) => Value::String(
format!("{}{}", left.to_css_string(self.span)?, s),
format!(
"{}{}",
left.to_css_string(self.span, self.parser.options.is_compressed())?,
s
),
QuoteKind::Quoted,
),
_ => Value::String(
format!(
"{}{}",
left.to_css_string(self.span)?,
right.to_css_string(self.span)?
left.to_css_string(self.span, self.parser.options.is_compressed())?,
right.to_css_string(self.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
),
},
Value::Important => match right {
Value::String(s, ..) => Value::String(
format!("{}{}", left.to_css_string(self.span)?, s),
format!(
"{}{}",
left.to_css_string(self.span, self.parser.options.is_compressed())?,
s
),
QuoteKind::None,
),
_ => Value::String(
format!(
"{}{}",
left.to_css_string(self.span)?,
right.to_css_string(self.span)?
left.to_css_string(self.span, self.parser.options.is_compressed())?,
right.to_css_string(self.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
),
@ -205,7 +225,9 @@ impl<'a, 'b: 'a, 'c> ValueVisitor<'a, 'b, 'c> {
Value::Null => match right {
Value::Null => Value::Null,
_ => Value::String(
right.to_css_string(self.span)?.into_owned(),
right
.to_css_string(self.span, self.parser.options.is_compressed())?
.into_owned(),
QuoteKind::None,
),
},
@ -230,14 +252,34 @@ impl<'a, 'b: 'a, 'c> ValueVisitor<'a, 'b, 'c> {
Value::Dimension(Some(num + num2.convert(&unit2, &unit)), unit, true)
}
}
Value::String(s, q) => Value::String(format!("{}{}{}", num, unit, s), q),
Value::Null => Value::String(format!("{}{}", num, unit), QuoteKind::None),
Value::String(s, q) => Value::String(
format!(
"{}{}{}",
num.to_string(self.parser.options.is_compressed()),
unit,
s
),
q,
),
Value::Null => Value::String(
format!(
"{}{}",
num.to_string(self.parser.options.is_compressed()),
unit
),
QuoteKind::None,
),
Value::True
| Value::False
| Value::List(..)
| Value::Important
| Value::ArgList(..) => Value::String(
format!("{}{}{}", num, unit, right.to_css_string(self.span)?),
format!(
"{}{}{}",
num.to_string(self.parser.options.is_compressed()),
unit,
right.to_css_string(self.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
),
Value::Map(..) | Value::FunctionRef(..) => {
@ -251,7 +293,7 @@ impl<'a, 'b: 'a, 'c> ValueVisitor<'a, 'b, 'c> {
return Err((
format!(
"Undefined operation \"{}{} + {}\".",
num,
num.inspect(),
unit,
right.inspect(self.span)?
),
@ -264,7 +306,11 @@ impl<'a, 'b: 'a, 'c> ValueVisitor<'a, 'b, 'c> {
Value::String(s, q) => Value::String(format!("{}{}", c, s), q),
Value::Null => Value::String(c.to_string(), QuoteKind::None),
Value::List(..) => Value::String(
format!("{}{}", c, right.to_css_string(self.span)?),
format!(
"{}{}",
c,
right.to_css_string(self.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
),
_ => {
@ -281,17 +327,25 @@ impl<'a, 'b: 'a, 'c> ValueVisitor<'a, 'b, 'c> {
},
Value::String(text, quotes) => match right {
Value::String(text2, ..) => Value::String(text + &text2, quotes),
_ => Value::String(text + &right.to_css_string(self.span)?, quotes),
_ => Value::String(
text + &right.to_css_string(self.span, self.parser.options.is_compressed())?,
quotes,
),
},
Value::List(..) | Value::ArgList(..) => match right {
Value::String(s, q) => {
Value::String(format!("{}{}", left.to_css_string(self.span)?, s), q)
}
Value::String(s, q) => Value::String(
format!(
"{}{}",
left.to_css_string(self.span, self.parser.options.is_compressed())?,
s
),
q,
),
_ => Value::String(
format!(
"{}{}",
left.to_css_string(self.span)?,
right.to_css_string(self.span)?
left.to_css_string(self.span, self.parser.options.is_compressed())?,
right.to_css_string(self.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
),
@ -314,7 +368,10 @@ impl<'a, 'b: 'a, 'c> ValueVisitor<'a, 'b, 'c> {
};
Ok(match left {
Value::Null => Value::String(
format!("-{}", right.to_css_string(self.span)?),
format!(
"-{}",
right.to_css_string(self.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
),
v @ Value::Dimension(None, ..) => v,
@ -344,7 +401,12 @@ impl<'a, 'b: 'a, 'c> ValueVisitor<'a, 'b, 'c> {
| Value::True
| Value::False
| Value::ArgList(..) => Value::String(
format!("{}{}-{}", num, unit, right.to_css_string(self.span)?),
format!(
"{}{}-{}",
num.to_string(self.parser.options.is_compressed()),
unit,
right.to_css_string(self.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
),
Value::Map(..) | Value::FunctionRef(..) => {
@ -358,7 +420,7 @@ impl<'a, 'b: 'a, 'c> ValueVisitor<'a, 'b, 'c> {
return Err((
format!(
"Undefined operation \"{}{} - {}\".",
num,
num.inspect(),
unit,
right.inspect(self.span)?
),
@ -366,7 +428,14 @@ impl<'a, 'b: 'a, 'c> ValueVisitor<'a, 'b, 'c> {
)
.into())
}
Value::Null => Value::String(format!("{}{}-", num, unit), QuoteKind::None),
Value::Null => Value::String(
format!(
"{}{}-",
num.to_string(self.parser.options.is_compressed()),
unit
),
QuoteKind::None,
),
},
Value::Color(c) => match right {
Value::String(s, q) => {
@ -385,32 +454,45 @@ impl<'a, 'b: 'a, 'c> ValueVisitor<'a, 'b, 'c> {
.into())
}
_ => Value::String(
format!("{}-{}", c, right.to_css_string(self.span)?),
format!(
"{}-{}",
c,
right.to_css_string(self.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
),
},
Value::String(..) => Value::String(
format!(
"{}-{}",
left.to_css_string(self.span)?,
right.to_css_string(self.span)?
left.to_css_string(self.span, self.parser.options.is_compressed())?,
right.to_css_string(self.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
),
_ => match right {
Value::String(s, q) => Value::String(
format!("{}-{}{}{}", left.to_css_string(self.span)?, q, s, q),
format!(
"{}-{}{}{}",
left.to_css_string(self.span, self.parser.options.is_compressed())?,
q,
s,
q
),
QuoteKind::None,
),
Value::Null => Value::String(
format!("{}-", left.to_css_string(self.span)?),
format!(
"{}-",
left.to_css_string(self.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
),
_ => Value::String(
format!(
"{}-{}",
left.to_css_string(self.span)?,
right.to_css_string(self.span)?
left.to_css_string(self.span, self.parser.options.is_compressed())?,
right.to_css_string(self.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
),
@ -448,7 +530,7 @@ impl<'a, 'b: 'a, 'c> ValueVisitor<'a, 'b, 'c> {
return Err((
format!(
"Undefined operation \"{}{} * {}\".",
num,
num.inspect(),
unit,
right.inspect(self.span)?
),
@ -487,7 +569,10 @@ impl<'a, 'b: 'a, 'c> ValueVisitor<'a, 'b, 'c> {
};
Ok(match left {
Value::Null => Value::String(
format!("/{}", right.to_css_string(self.span)?),
format!(
"/{}",
right.to_css_string(self.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
),
Value::Dimension(None, ..) => todo!(),
@ -537,24 +622,50 @@ impl<'a, 'b: 'a, 'c> ValueVisitor<'a, 'b, 'c> {
}
} else {
Value::String(
format!("{}{}/{}{}", num, unit, num2, unit2),
format!(
"{}{}/{}{}",
num.to_string(self.parser.options.is_compressed()),
unit,
num2.to_string(self.parser.options.is_compressed()),
unit2
),
QuoteKind::None,
)
}
}
Value::String(s, q) => {
Value::String(format!("{}{}/{}{}{}", num, unit, q, s, q), QuoteKind::None)
}
Value::String(s, q) => Value::String(
format!(
"{}{}/{}{}{}",
num.to_string(self.parser.options.is_compressed()),
unit,
q,
s,
q
),
QuoteKind::None,
),
Value::List(..)
| Value::True
| Value::False
| Value::Important
| Value::Color(..)
| Value::ArgList(..) => Value::String(
format!("{}{}/{}", num, unit, right.to_css_string(self.span)?),
format!(
"{}{}/{}",
num.to_string(self.parser.options.is_compressed()),
unit,
right.to_css_string(self.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
),
Value::Null => Value::String(
format!(
"{}{}/",
num.to_string(self.parser.options.is_compressed()),
unit
),
QuoteKind::None,
),
Value::Null => Value::String(format!("{}{}/", num, unit), QuoteKind::None),
Value::Map(..) | Value::FunctionRef(..) => {
return Err((
format!("{} isn't a valid CSS value.", right.inspect(self.span)?),
@ -580,7 +691,11 @@ impl<'a, 'b: 'a, 'c> ValueVisitor<'a, 'b, 'c> {
.into())
}
_ => Value::String(
format!("{}/{}", c, right.to_css_string(self.span)?),
format!(
"{}/{}",
c,
right.to_css_string(self.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
),
},
@ -596,7 +711,13 @@ impl<'a, 'b: 'a, 'c> ValueVisitor<'a, 'b, 'c> {
| Value::Color(..)
| Value::List(..)
| Value::ArgList(..) => Value::String(
format!("{}{}{}/{}", q1, s1, q1, right.to_css_string(self.span)?),
format!(
"{}{}{}/{}",
q1,
s1,
q1,
right.to_css_string(self.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
),
Value::Null => Value::String(format!("{}{}{}/", q1, s1, q1), QuoteKind::None),
@ -610,18 +731,27 @@ impl<'a, 'b: 'a, 'c> ValueVisitor<'a, 'b, 'c> {
},
_ => match right {
Value::String(s, q) => Value::String(
format!("{}/{}{}{}", left.to_css_string(self.span)?, q, s, q),
format!(
"{}/{}{}{}",
left.to_css_string(self.span, self.parser.options.is_compressed())?,
q,
s,
q
),
QuoteKind::None,
),
Value::Null => Value::String(
format!("{}/", left.to_css_string(self.span)?),
format!(
"{}/",
left.to_css_string(self.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
),
_ => Value::String(
format!(
"{}/{}",
left.to_css_string(self.span)?,
right.to_css_string(self.span)?
left.to_css_string(self.span, self.parser.options.is_compressed())?,
right.to_css_string(self.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
),

View File

@ -272,9 +272,17 @@ impl<'a, 'b> Parser<'a, 'b> {
}
"url" => match self.try_parse_url()? {
Some(val) => s = val,
None => s.push_str(&self.parse_call_args()?.to_css_string()?),
None => s.push_str(
&self
.parse_call_args()?
.to_css_string(self.options.is_compressed())?,
),
},
_ => s.push_str(&self.parse_call_args()?.to_css_string()?),
_ => s.push_str(
&self
.parse_call_args()?
.to_css_string(self.options.is_compressed())?,
),
}
return Ok(IntermediateValue::Value(HigherIntermediateValue::Literal(
@ -1142,7 +1150,10 @@ impl<'a, 'b: 'a, 'c> IntermediateValueIterator<'a, 'b, 'c> {
"/{}",
ValueVisitor::new(self.parser, right.span)
.eval(right.node, false)?
.to_css_string(right.span)?
.to_css_string(
right.span,
self.parser.options.is_compressed()
)?
),
QuoteKind::None,
)),
@ -1348,7 +1359,7 @@ impl<'a, 'b: 'a, 'c> IntermediateValueIterator<'a, 'b, 'c> {
"/{}",
ValueVisitor::new(self.parser, val.span)
.eval(val.node, false)?
.to_css_string(val.span)?
.to_css_string(val.span, self.parser.options.is_compressed())?
),
QuoteKind::None,
)),

View File

@ -189,7 +189,7 @@ impl Display for Attribute {
// (also avoids the clone because we can consume/modify self)
f.write_str(
&Value::String(self.value.clone(), QuoteKind::Quoted)
.to_css_string(self.span)
.to_css_string(self.span, false)
.unwrap(),
)?;
// todo: this space is not emitted when `compressed` output

View File

@ -14,7 +14,7 @@ impl Style {
Ok(format!(
"{}: {};",
self.property,
self.value.node.to_css_string(self.value.span)?
self.value.node.to_css_string(self.value.span, false)?
))
}
}

View File

@ -205,22 +205,28 @@ impl Value {
}
}
pub fn to_css_string(&self, span: Span) -> SassResult<Cow<'static, str>> {
pub fn to_css_string(&self, span: Span, is_compressed: bool) -> SassResult<Cow<'static, str>> {
Ok(match self {
Value::Important => Cow::const_str("!important"),
Value::Dimension(num, unit, _) => match unit {
Unit::Mul(..) | Unit::Div(..) => {
if let Some(num) = num {
return Err(
(format!("{}{} isn't a valid CSS value.", num, unit), span).into()
);
return Err((
format!(
"{}{} isn't a valid CSS value.",
num.to_string(is_compressed),
unit
),
span,
)
.into());
}
return Err((format!("NaN{} isn't a valid CSS value.", unit), span).into());
}
_ => {
if let Some(num) = num {
Cow::owned(format!("{}{}", num, unit))
Cow::owned(format!("{}{}", num.to_string(is_compressed), unit))
} else {
Cow::owned(format!("NaN{}", unit))
}
@ -237,17 +243,25 @@ impl Value {
Brackets::None => Cow::owned(
vals.iter()
.filter(|x| !x.is_null())
.map(|x| x.to_css_string(span))
.map(|x| x.to_css_string(span, is_compressed))
.collect::<SassResult<Vec<Cow<'static, str>>>>()?
.join(sep.as_str()),
.join(if is_compressed {
sep.as_compressed_str()
} else {
sep.as_str()
}),
),
Brackets::Bracketed => Cow::owned(format!(
"[{}]",
vals.iter()
.filter(|x| !x.is_null())
.map(|x| x.to_css_string(span))
.map(|x| x.to_css_string(span, is_compressed))
.collect::<SassResult<Vec<Cow<'static, str>>>>()?
.join(sep.as_str()),
.join(if is_compressed {
sep.as_compressed_str()
} else {
sep.as_str()
}),
)),
},
Value::Color(c) => Cow::owned(c.to_string()),
@ -287,9 +301,13 @@ impl Value {
Value::ArgList(args) => Cow::owned(
args.iter()
.filter(|x| !x.is_null())
.map(|a| a.node.to_css_string(span))
.map(|a| a.node.to_css_string(span, is_compressed))
.collect::<SassResult<Vec<Cow<'static, str>>>>()?
.join(", "),
.join(if is_compressed {
ListSeparator::Comma.as_compressed_str()
} else {
ListSeparator::Comma.as_str()
}),
),
})
}
@ -466,7 +484,9 @@ impl Value {
.collect::<SassResult<Vec<String>>>()?
.join(", ")
)),
Value::Dimension(Some(num), unit, _) => Cow::owned(format!("{}{}", num, unit)),
Value::Dimension(Some(num), unit, _) => {
Cow::owned(format!("{}{}", num.inspect(), unit))
}
Value::Dimension(None, unit, ..) => Cow::owned(format!("NaN{}", unit)),
Value::ArgList(args) if args.is_empty() => Cow::const_str("()"),
Value::ArgList(args) if args.len() == 1 => Cow::owned(format!(
@ -488,7 +508,7 @@ impl Value {
| Value::True
| Value::False
| Value::Color(..)
| Value::String(..) => self.to_css_string(span)?,
| Value::String(..) => self.to_css_string(span, false)?,
})
}

View File

@ -1,8 +1,7 @@
use std::{
cmp::Ordering,
convert::{From, TryFrom},
fmt::{self, Display, Write},
mem,
fmt, mem,
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign},
};
@ -313,8 +312,8 @@ from_smaller_integer!(u8);
impl fmt::Debug for Number {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Small(..) => write!(f, "Number::Small( {} )", self),
Self::Big(..) => write!(f, "Number::Big( {} )", self),
Self::Small(..) => write!(f, "Number::Small( {} )", self.to_string(false)),
Self::Big(..) => write!(f, "Number::Big( {} )", self.to_string(false)),
}
}
}
@ -355,16 +354,24 @@ impl ToPrimitive for Number {
}
}
impl Display for Number {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl Number {
pub(crate) fn inspect(&self) -> String {
self.to_string(false)
}
pub(crate) fn to_string(&self, is_compressed: bool) -> String {
let mut whole = self.to_integer().abs();
let has_decimal = self.is_decimal();
let mut frac = self.abs().fract();
let mut dec = String::with_capacity(if has_decimal { PRECISION } else { 0 });
let mut buf = String::new();
if has_decimal {
for _ in 0..(PRECISION - 1) {
frac *= 10_i64;
write!(dec, "{}", frac.to_integer())?;
dec.push_str(&frac.to_integer().to_string());
frac = frac.fract();
if frac.is_zero() {
break;
@ -398,20 +405,25 @@ impl Display for Number {
}
}
} else {
write!(dec, "{}", end)?;
dec.push_str(&end.to_string());
}
}
}
if self.is_negative() && (!whole.is_zero() || !dec.is_empty()) {
f.write_char('-')?;
buf.push('-');
}
write!(f, "{}", whole)?;
if !(whole.is_zero() && is_compressed) {
buf.push_str(&whole.to_string());
}
if !dec.is_empty() {
f.write_char('.')?;
write!(f, "{}", dec)?;
buf.push('.');
buf.push_str(&dec);
}
Ok(())
buf
}
}

View File

@ -13,13 +13,6 @@ test!(
"a{color:red;color:green;color:blue}",
grass::Options::default().style(grass::OutputStyle::Compressed)
);
test!(
#[ignore = "we don't support compressed values"]
strips_the_leading_zero,
"a {\n color: 0.5;\n}\n",
"a{color:.5}",
grass::Options::default().style(grass::OutputStyle::Compressed)
);
test!(
compresses_media_rule,
"@media foo {\n a {\n color: red;\n }\n}\n",
@ -86,3 +79,40 @@ test!(
"a{color:red}b{color:green}",
grass::Options::default().style(grass::OutputStyle::Compressed)
);
test!(
removes_spaces_in_comma_separated_list,
"a {\n color: a, b, c;\n}\n",
"a{color:a,b,c}",
grass::Options::default().style(grass::OutputStyle::Compressed)
);
test!(
removes_leading_zero_in_number_under_1,
"a {\n color: 0.5;\n}\n",
"a{color:.5}",
grass::Options::default().style(grass::OutputStyle::Compressed)
);
test!(
#[ignore = "we do not support compressed colors"]
removes_leading_zero_in_number_under_1_in_rgba_alpha_channel,
"a {\n color: rgba(1, 1, 1, 0.5);\n}\n",
"a{color:rgba(1,1,1,.5)}",
grass::Options::default().style(grass::OutputStyle::Compressed)
);
test!(
retains_leading_zero_in_opacity,
"a {\n color: opacity(0.5);\n}\n",
"a{color:opacity(0.5)}",
grass::Options::default().style(grass::OutputStyle::Compressed)
);
test!(
retains_leading_zero_in_saturate,
"a {\n color: saturate(0.5);\n}\n",
"a{color:saturate(0.5)}",
grass::Options::default().style(grass::OutputStyle::Compressed)
);
test!(
retains_leading_zero_in_grayscale,
"a {\n color: grayscale(0.5);\n}\n",
"a{color:grayscale(0.5)}",
grass::Options::default().style(grass::OutputStyle::Compressed)
);