#[macro_export]
macro_rules! t {
($name:ident, {$($define:tt)+}, $($eval:expr => $expect:expr,)+) => {
#[test]
fn $name() {
markup::define! {
$($define)+
}
$(
assert_eq!($eval.to_string(), $expect);
)+
}
};
}
t! {
t1,
{
A { }
B { 1 }
C { 2 3 }
D { 4 "5" 6 }
E { 7 " < " 8 }
F { 9 " ≤ " 10 }
},
A {} => "",
B {} => "1",
C {} => "23",
D {} => "456",
E {} => "7 < 8",
F {} => "9 ≤ 10",
}
t! {
t2,
{
A {
{1 + 2}
{'π'}
{if true { Some(3) } else { Some(4) }}
{if false { Some(5) } else { Some(6) }}
{"<>"}
{true}
{false}
}
},
A {} => "3π36<>truefalse",
}
t! {
t3,
{
A {
div {}
br;
}
},
A {} => "
",
}
t! {
t4,
{
A {
1
.foo {
2
.bar.baz {
3
#quux {
4
}
5
}
6
}
7
}
},
A {} => r#"17"#,
}
t! {
t5,
{
A {
foo #bar {
baz.quux #"foo".{1}.{2 + 3} {}
bar #{4}.{5 - 6} { 7 }
}
}
B {
foo "bar"
foo #bar "baz"
foo.bar[baz = true] "quux"
foo.bar[baz = true]; "quux"
}
C {
foo[fn = true, async = false, mod = true, r#move = true] {}
}
},
A {} => r#"7"#,
B {} => r#"barbazquuxquux"#,
C {} => r#""#,
}
t! {
t6,
{
A {
div [
a = 1,
b = "2",
c = true,
c2 = &true,
c3 = &&true,
c4,
d = false,
d2 = &false,
d3 = &&false,
"e-f" = 3,
{"g".to_string() + "-h"} = 4,
i = None::,
i2 = &None::,
i3 = &&None::,
j = Some(5),
j2 = &Some(5),
j3 = &&Some(5),
h = (6, ("7", String::from("8"), true, Some(false), None::)),
i = markup::raw("foo"),
] {}
br[k = 6];
}
},
A {} => r#"
"#,
}
t! {
t7,
{
A(foo: u32, bar: i32, baz: String) {
{foo} {bar} {*foo as i32 + bar} {baz}
}
},
A { foo: 1, bar: -2, baz: "3".into() } => "1-2-13",
}
t! {
t8,
{
A<'a, T: std::fmt::Debug, U, V: markup::Render>(
arg: T,
arg2: U,
str: &'a str,
box_str: Box,
v: V,
) where U: std::fmt::Display {
div {
{format!("{:?}", arg)}
{format!("{}", arg2)}
{str}
{box_str}
{v}
}
}
},
A {
arg: (1, 2),
arg2: "arg2",
str: "str",
box_str: "box_str".into(),
v: markup::new!(foo {})
} => "(1, 2)arg2strbox_str
",
}
t! {
t9,
{
A String>(foo: i32, bar: Bar, baz: B) {
@foo
@bar(*foo + 1)
@baz.foo
@format!("{} {} {}", foo, bar(*foo + 2), baz.foo + 3)
@B { foo: foo + 4 }
}
B(foo: i32) {
@foo @foo @foo
}
},
A {
foo: 1,
bar: |x| (x + 2).to_string(),
baz: B { foo: 3 },
} => "1431 5 6555",
}
t! {
t10,
{
A {
@fn foo() -> i32 { 1 }
@mod bar {
pub fn baz() -> i32 { 2 }
}
@const QUUX: i32 = 3;
@static FOUR: i32 = 4;
@foo()
@bar::baz()
@QUUX
@FOUR
@#[derive(Debug)] struct Int(i32);
@let Int(five) = Int(5);
@five
@{6}
@{6 + 1}
@{let eight = Int(8); eight.0}
@crate::add(10, -1)
@self::add(10, -2)
@impl A {
fn answer() -> &'static str {
"7"
}
}
@Self::answer()
}
},
A {} => "12345678987",
}
fn add(a: i32, b: i32) -> i32 {
a + b
}
t! {
t11,
{
A {
@let foos = [None, Some(1), Some(2), Some(3)];
@for (index, foo) in foos.iter().enumerate() {
"index=" @index " :: "
@B { foo: *foo } ";"
@C { foo: *foo } ";"
@D { foo: *foo } ";"
@E { foo: *foo } ";"
@F { foo: *foo } "\n"
}
}
B(foo: Option) {
@if foo.is_some() {
@let foo = foo.unwrap();
@if foo == 1 {
"ONE"
} else if foo == 2 {
"TWO"
} else {
"OTHER"
}
} else {
"NONE"
}
}
C(foo: Option) {
@if *foo == Some(1) {
"ONE"
} else if *foo == Some(2) {
"TWO"
} else if foo.is_some() {
"OTHER"
} else {
"NONE"
}
}
D(foo: Option) {
@if let Some(1) = foo {
"ONE"
} else if let | Some(2) | Some(3) = foo {
"TWO OR THREE"
} else if let Some(_) = foo {
"OTHER"
} else {
"NONE"
}
}
E(foo: Option) {
@match foo {
Some(1) => { "ONE" }
Some(n) if *n == 2 => { "TWO" }
Some(_) => { "OTHER" }
None => { "NONE" }
}
}
F(foo: Option) {
@match foo {
| Some(1) | Some(2) => { "ONE OR TWO" }
| Some(n) if *n == 3 => { "THREE" }
Some(_) | None => { "OTHER OR NONE" }
}
}
},
A {} => "\
index=0 :: NONE;NONE;NONE;NONE;OTHER OR NONE
index=1 :: ONE;ONE;ONE;ONE;ONE OR TWO
index=2 :: TWO;TWO;TWO OR THREE;TWO;ONE OR TWO
index=3 :: OTHER;OTHER;TWO OR THREE;OTHER;THREE
",
}
t! {
t12,
{
A(b: B) {
@b
@format!("{:?}", b)
}
#[derive(Clone, Debug)]
B(foo: i32, bar: char) {
@foo @bar
}
},
A { b: B { foo: 1, bar: '?' }.clone() } => "1?B { foo: 1, bar: '?' }",
}
t! {
t13,
{
A() {
$"foo-bar";
$"foo-bar" {}
$"foo-bar"[baz = "quux"] {}
}
B(name: &'static str) {
${name};
${name} {}
}
},
A {} => r#""#,
B { name: "foo-bar" } => r#""#,
}
mod inner {
super::t! {
t14,
{
A() {
@super::add(7, 7)
}
},
A {} => "14",
}
}
t! {
t15,
{
A() {
@for | Ok(x) | Err(x) in vec![Ok(1), Err(2), Ok(3)] {
@x
}
}
},
A {} => "123",
}
t! {
t16,
{
A() {
div[..Vec::<(String, String)>::new(), ..[("foo", "bar")]] {}
}
B<'a>(attrs: &'a [(&'static str, Option<&'static str>)]) {
div[id = "b", ..*attrs] {}
}
C(data: std::collections::BTreeMap<&'static str, &'static str>) {
div[..data.iter().map(|(k, v)| (("data-", k), v))] {}
}
},
A {} => r#""#,
B {
attrs: &[("foo", None), ("bar", Some("baz"))]
} => r#""#,
C {
data: [("foo", "bar"), ("baz", "quux")].iter().cloned().collect()
} => r#""#,
}