rsass 0.29.2

Sass implementation in pure rust (not complete yet)
Documentation
//! These are from the `output_styles/compressed/basic` directory in the
//! sass specification.
use rsass::compile_scss;
use rsass::output::{Format, Style};

#[test]
fn t01_simple_css() {
    check(
        b"a {\n  \
            color: blue;\n\
            }",
        "a{color:blue}\n",
    )
}

#[test]
fn t02_simple_nesting() {
    check(
        b"div {\n  img {\n    border: 0px;\n  }\n}",
        "div img{border:0px}\n",
    )
}

#[test]
fn t03_simple_variable() {
    check(
        b"$color: red;\n\na {\n  color: $color;\n}",
        "a{color:red}\n",
    )
}

#[test]
fn t04_basic_variables() {
    check(
        b"$color: \"black\";\n$color: red;\n$background: \"blue\";\n\n\
            a {\n  color: $color;\n  background: $background;\n}\n\n\
            $y: before;\n\n\
            $x: 1 2 $y;\n\n\
            foo {\n  a: $x;\n}\n\n\
            $y: after;\n\n\
            foo {\n  a: $x;\n}",
        "a{color:red;background:\"blue\"}foo{a:1 2 before}\
         foo{a:1 2 before}\n",
    )
}

#[test]
fn t05_empty_levels() {
    check(
        b"div {\n  \
            span {\n    color: red;\n    background: blue;\n  }\n}\n\n\
            div {\n  color: gray;\n\
            empty {\n    \
            span {\n      color: red;\n      background: blue;\n    }\n  \
            }\n}\n\n\
            empty1 {\n  empty2 {\n    \
            div {\n      blah: blah;\n    }\n  }\n}\n\n\
            empty1 {\n  empty2 {\n    div {\n      bloo: blee;\n      \
            empty3 {\n        \
            span {\n          blah: blah;\n          blah: blah;\n        \
            }\n      }\n    }\n  }\n}\n",
        "div span{color:red;background:blue}div{color:gray}\
         div empty span{color:red;background:blue}\
         empty1 empty2 div{blah:blah}empty1 empty2 div{bloo:blee}\
         empty1 empty2 div empty3 span{blah:blah;blah:blah}\n",
    )
}

#[test]
fn t06_nesting_and_comments() {
    // No comments preserved in compressed output!
    check(
        b"$blah: bloo blee;\n$blip: \"a 'red' and \\\"blue\\\" value\";\n\n\
            /* top level comment -- should be preserved */\n\
            div {\n  /* another comment that should be preserved */\n  \
            color: red;\n  background: blue;\n  $blux: hux; // gone!\n  \
            span {\n    font-weight: bold;\n    \
            a {\n      \
            text-decoration: none; /* where will this comment go? */\n      \
            color: green;\n      \
            /* what about this comment? */ border: 1px $blah red;\n    \
            }\n    \
            /* yet another comment that should be preserved */\n    \
            display: inline-block;\n  }  // gone!\n  \
            /* the next selector should be indented two spaces */\n  \
            empty {\n    \
            not_empty {\n      blah: blah; // gone!\n      bloo: bloo;\n    \
            }\n  }\n  \
            p {\n    padding: 10px 8%;\n    -webkit-box-sizing: $blux;\n  }\n  \
            margin: 10px 5px;\n  h1 {\n    color: $blip;\n  }\n}\n\
            /* last comment, top level again --\n   \
            compare the indentation! */\n\n\
            div {\n\n\
            f: g;\n  \
            empty {\n    span {\n      a: b;\n    }\n  }\n  \
            empty_with_comment {\n    /* hey now */\n    \
            span {\n      c: d;\n    }\n  }\n}",
        "div{color:red;background:blue;margin:10px 5px}\
         div span{font-weight:bold;display:inline-block}\
         div span a{text-decoration:none;color:green;\
         border:1px bloo blee red}\
         div empty not_empty{blah:blah;bloo:bloo}\
         div p{padding:10px 8%;-webkit-box-sizing:hux}\
         div h1{color:\"a 'red' and \\\"blue\\\" value\"}\
         div{f:g}div empty span{a:b}div empty_with_comment span{c:d}\n",
    )
}

#[test]
fn t07_nested_simple_selector_groups() {
    check(
        b"a, b {\n  color: red;\n  background: blue;\n}\n\n\
            c, d {\n  color: gray;\n  \
            e, f {\n    background: blue;\n    padding: 10px 5px;\n  }\n  \
            g, h {\n    blah: blah;\n    bloo: bloo;\n  }\n  \
            i, j {\n    foo: goo;\n    k, l {\n      \
            m, n, o {\n        wow: we are far inside;\n        \
            but: it still works;\n      }\n      \
            hoo: boo;\n    }\n  }\n}",
        "a,b{color:red;background:blue}c,d{color:gray}\
         c e,c f,d e,d f{background:blue;padding:10px 5px}\
         c g,c h,d g,d h{blah:blah;bloo:bloo}c i,c j,d i,d j{foo:goo}\
         c i k,c i l,c j k,c j l,d i k,d i l,d j k,d j l{hoo:boo}\
         c i k m,c i k n,c i k o,c i l m,c i l n,c i l o,c j k m,c j k n,\
         c j k o,c j l m,c j l n,c j l o,d i k m,d i k n,d i k o,d i l m,\
         d i l n,d i l o,d j k m,d j k n,d j k o,d j l m,d j l n,d j l o\
         {wow:we are far inside;but:it still works}\n",
    )
}

#[test]
fn t08_selector_combinators() {
    check(
        b"a   +   b  >  c {\n  \
            d e {\n    color: blue;\n    background: white;\n  }\n  \
            color: red;\n  background: gray;\n}",
        "a+b>c{color:red;background:gray}\
         a+b>c d e{color:blue;background:white}\n",
    )
}

#[test]
fn t10_classes_and_ids() {
    check(
        b"a + b, .class {\n  blah: blah;\n  bleh: bleh;\n  \
            d #id, f ~ g.other + h, > i#grar {\n    bloo: bloo;\n    \
            blee: blee;\n  }\n}",
        "a+b,.class{blah:blah;bleh:bleh}\
         a+b d #id,a+b f~g.other+h,a+b>i#grar,.class d #id,\
         .class f~g.other+h,.class>i#grar{bloo:bloo;blee:blee}\n",
    )
}

#[test]
fn t15_arithmetic_and_lists() {
    check(
        b"$stuff: 1 2 3;\n\n\
            $three: 3;\n\n\
            div {\n  a: 1 + 2;\n  b: 3 + 3/4;\n  c: 1/2 + 1/2;\n  \
            /* shouldn't eval the following \"300\" */\n  d: 300;\n  \
            /* increasingly jacked-up edge cases that combine arithmetic \
            with lists */\n  e: 1 + (5/10 2 3);\n  \
            f: 1 + ((2+(3 4) 5) 6);\n  g: 1 + ((1+(14/7 8) 9) 6);\n  \
            /* shouldn't perform the following division */\n  \
            h: 15 / 3 / 5;\n  \
            /* should perform the following division now */\n  \
            i: (15 / 3 / 5);\n  /* this too */\n  j: (15 / 3) / 5;\n  \
            /* and this */\n  k: 15 / $three;\n  l: 15 / 5 / $three;\n  \
            m: 1/2, $stuff url(\"www.foo.com/blah.png\") blah blah;\n  \
            n: 1 2 3, $stuff 4 5 (6, 7 8 9);\n  o: 3px + 3px + 3px;\n  \
            p: 4 + 1px;\n  q: (20pt / 10pt);\n  r: 16em * 4;\n  \
            s: (5em / 2);\n  t: 1 + (2 + (3/4 + (4/5 6/7)));\n}",
        "div{a:3;b:3.75;c:1;d:300;e:15/10 2 3;f:123 4 5 6;g:1114/7 8 9 6;\
         h:15/3/5;i:1;j:1;k:5;l:1;\
         m:1/2,1 2 3 url(\"www.foo.com/blah.png\") blah blah;\
         n:1 2 3,1 2 3 4 5 6,7 8 9;o:9px;p:5px;q:2;r:64em;s:2.5em;\
         t:12.754/5 6/7}\n",
    )
}

#[test]
fn t19_full_mixin_craziness() {
    check(
        b"$x: global-x;\n$y: global-y;\n$z: global-z;\n\n\
            @mixin foo($x, $y) {\n  /* begin foo */\n  \
            margin: $x $y;\n  blip {\n    hey: now;\n  }\n  \
            /* end foo */\n}\n\n\
            @mixin foogoo($x, $y, $z) {\n  margin: $x $y $z;\n}\n\n\
            @mixin hux($y) {\n  /* begin hux */\n  color: $y;\n  \
            @include foo(called-from-hux, $y: $y);\n  /* end hux */\n}\n\n\
            div {\n  @include foo(1, 2);\n  @include foo(1, 3);\n  \
            @include foogoo(1, 2, $z: zee);\n  \
            @include foogoo(1, $y /* blah */ : kwd-y, $z: kwd-z);\n}\n\n\
            div {\n  @include hux($y: $y);\n}\n\n\
            $y: different-global-y;\n\n\
            div {\n  @include hux(calling-hux-again);\n}\n\n\
            @mixin bung() {\n  blah: original-bung;\n}\n\n\
            div {\n  @include bung();\n}\n\n\
            @mixin bung() {\n  blah: redefined-bung;\n}\n\n\
            div {\n  @include bung();\n}\n\n\
            div {\n  /* calls to nullary mixins may omit the empty argument \
            list */\n  @include bung;\n}\n\n\
            div {\n  @include foo($x: kwdarg1, $y: kwdarg2);\n}\n\n\
            @mixin ruleset() {\n  hoo {\n    color: boo;\n  }\n}\n\n\
            @include ruleset();\n\n\
            $da: default argument;\n\n\
            @mixin default_args($x, $y: $da) {\n  blah: $x $y;\n}\n\
            $da: some other default;\n\n\
            div {\n  @include default_args(boogoo);\n}\n\n\
            @mixin original() {\n  value: original;\n}\n\n\
            div {\n  @include original();\n}\n\n\
            @mixin original() {\n  value: no longer original;\n}\n\n\
            div {\n  @include original();\n}\n\n\
            @mixin set-x($x) {\n  $x: changed local x;\n  arg: $x;\n  \
            $y: changed global y !global;\n  blarg: $y;\n}\n\n\
            div {\n  @include set-x(blah);\n  a: $x;\n  b: $y;\n}\n",
        "div{margin:1 2;margin:1 3;margin:1 2 zee;margin:1 kwd-y kwd-z}\
         div blip{hey:now}div blip{hey:now}div{color:global-y;\
         margin:called-from-hux global-y}div blip{hey:now}\
         div{color:calling-hux-again;margin:called-from-hux \
         calling-hux-again}div blip{hey:now}div{blah:original-bung}\
         div{blah:redefined-bung}div{blah:redefined-bung}\
         div{margin:kwdarg1 kwdarg2}div blip{hey:now}hoo{color:boo}\
         div{blah:boogoo some other default}div{value:original}\
         div{value:no longer original}\
         div{arg:changed local x;blarg:changed global y;a:global-x;\
         b:changed global y}\n",
    )
}

#[test]
fn t27_media_queries() {
    check(
        b"a b c {\n  blee: blee;\n  \
            d e f {\n    blah: blah;\n    bloo: bloo;\n  }\n  \
            g h, i j {\n    \
            @media print and (foo: 1 2 3), (bar: 3px hux(muz)), \
            not screen {\n      hey: ho;\n      \
            k l m {\n        hee: fee;\n      }\n    }\n  }\n\
            blah: blah;\n}\n",
        "a b c{blee:blee;blah:blah}a b c d e f{blah:blah;bloo:bloo}\
         @media print and (foo: 1 2 3),(bar: 3px hux(muz)),not screen{\
         a b c g h,a b c i j{hey:ho}a b c g h k l m,a b c i j k l m{hee:fee}\
         }\n",
    )
}

#[test]
fn t49_interpolants_in_css_imports() {
    check(
        b"$google-protocol: \"http\"; // choose http or https\n\
            $google-webfont: \"Open+Sans:400italic,700italic,400,700|Oswald\"\
            ; // pull string after ?family= from step 3\n\n\
            @import url(\"#{$google-protocol}://fonts.googleapis.com/css?\
            family=#{$google-webfont}\");",
        "@import url(\"http://fonts.googleapis.com/css?family=\
           Open+Sans:400italic,700italic,400,700|Oswald\")
",
    )
}

#[test]
fn t50_wrapped_pseudo_selectors() {
    check(
        b"div {\n  \
            :-moz-any(ol p.blah, ul, menu, dir) \
            :-moz-any(ol span + h1, ul, menu, dir) ul {\n    \
            list-style-type: square;\n  }\n  \
            :-moz-any(ol span + h1, ul, menu, dir) ul {\n    \
            list-style-type: square;\n  }\n  \
            :foo(p div) {\n    hi: hi;\n  }\n  \
            :foo(ol) {\n    hi: hi;\n  }\n}\n",
        "div :-moz-any(ol p.blah,ul,menu,dir) \
         :-moz-any(ol span+h1,ul,menu,dir) ul{list-style-type:square}\
         div :-moz-any(ol span+h1,ul,menu,dir) ul{list-style-type:square}\
         div :foo(p div){hi:hi}div :foo(ol){hi:hi}\n",
    )
}

fn check(input: &[u8], expected: &str) {
    let format = Format {
        style: Style::Compressed,
        precision: 5,
    };
    assert_eq!(
        String::from_utf8(compile_scss(input, format).unwrap()).unwrap(),
        expected
    );
}