use macro_rules_rt::{Matcher, Rule, Transcriber};
use proc_macro2::TokenStream;
use syn::{parse::Parser, parse2, parse_str};
#[track_caller]
fn check(from: &str, to: &str, input: &str, expect: &str) {
check_as(from, to, input, expect, false);
}
#[track_caller]
fn check_as(from: &str, to: &str, input: &str, expect: &str, nest: bool) {
let msg = format!(
"
from = {from},
to = {to}
input = {input}"
);
{
let from: Matcher = from.parse().unwrap();
let to: Transcriber = to.parse().unwrap();
let rule = Rule::new(from, to).unwrap();
let rule = rule.nest(nest);
let actual = rule.replace_all(input).unwrap();
assert_eq!(actual, expect, "replace_all {msg}");
}
{
let from: Matcher = from.parse().unwrap();
let to: TokenStream = parse_str(to).unwrap();
let to = Transcriber::parse.parse2(to).unwrap();
let rule = Rule::new(from, to).unwrap();
let rule = rule.nest(nest);
let input: TokenStream = input.parse().unwrap();
let expect: TokenStream = expect.parse().unwrap();
let actual = rule.replace_all_tokens(input);
assert_eq!(
actual.to_string(),
expect.to_string(),
"replace_all tokens_transcriber {msg}"
);
}
{
let from: TokenStream = parse_str(from).unwrap();
let from: Matcher = parse2(from).unwrap();
let to: TokenStream = parse_str(to).unwrap();
let to = Transcriber::parse.parse2(to).unwrap();
let rule = Rule::new(from, to).unwrap();
let rule = rule.nest(nest);
let actual = rule.replace_all(input).unwrap();
assert_eq!(actual, expect, "replace_all_tokens {msg}");
let input: TokenStream = input.parse().unwrap();
let expect: TokenStream = expect.parse().unwrap();
let actual = rule.replace_all_tokens(input);
assert_eq!(
actual.to_string(),
expect.to_string(),
"replace_all_tokens {msg}"
);
}
}
#[test]
fn single_line_1() {
check("+", "-", "1 + 2 + 3", "1 - 2 - 3");
check("+", "-", "+2+3", "- 2 - 3");
}
#[test]
fn single_line_2() {
check(
"$($e:ident)*",
"$($e @)*",
"1+2 a b c 3+4",
"1+2 a @ b @ c @ 3+4",
);
}
#[test]
fn var() {
check("$x:ident", "b", "a", "b");
}
#[test]
fn var_to() {
check("$x:ident", "@ $x @", "a", "@ a @");
check("$x:ident", "@ $x @", "1 a", "1 @ a @");
}
#[test]
fn lf() {
check("+", "-", "1\n2+3;\n4", "1\n2 - 3;\n4");
}
#[test]
fn cr() {
check("+", "-", "1\r2+3;\r4", "1\r2 - 3;\r4");
}
#[test]
fn cr_lf() {
check("+", "-", "1\r\n2+3\r\n4", "1\r\n2 - 3\r\n4");
}
#[test]
fn non_ascii() {
check("+", "-", "\"あ\"+2+3", "\"あ\" - 2 - 3");
}
#[test]
fn replace_middle() {
check("+", "-", "1+2", "1 - 2");
}
#[test]
fn replace_end() {
check("+", "-", "1+", "1 -");
}
#[test]
fn var_expr() {
check("$e:expr", "$e @", "1", "1 @");
check("$e:expr", "$e @", "1 + 2", "1 + 2 @");
check("$e:expr", "$e @", "(1 + 2)", "(1 + 2) @");
check(
"$e:expr",
"$e @",
"(1 + 2) + (3 + 4)",
"(1 + 2) + (3 + 4) @",
);
}
#[test]
fn tt() {
check("$x:tt", "a", "1 2", "a a");
}
#[test]
fn doc_comment_no_panic() {
check("$x:tt", "", "///\nstruct X;", "");
}
#[test]
fn doc_comment_save() {
check("a", "a", "/// xxx\nstruct X;", "/// xxx\nstruct X;");
}
#[test]
fn doc_comment_attr() {
check("doc =", "xxx =", "/// abc", "# [ xxx = \" abc\" ]");
}
#[test]
fn group() {
check("(a)", "a @@", "(a)", "a @@");
check("[a]", "a @@", "[a]", "a @@");
check("{a}", "a @@", "{a}", "a @@");
}
#[test]
fn group_mismatch_delimiter() {
check("(a)", "a @@", "[a]", "[a]");
check("[a]", "a @@", "(a)", "(a)");
}
#[test]
fn group_with_var() {
check("($x:ident)", "$x @@", "(a)", "a @@");
check("[$x:ident]", "$x @@", "[a]", "a @@");
check("{$x:ident}", "$x @@", "{a}", "a @@");
}
#[test]
fn group_with_var_mismatch_delimiter() {
check("($x:ident)", "$x @@", "[a]", "[a]");
check("[$x:ident]", "$x @@", "(a)", "(a)");
}
#[test]
fn in_group() {
check("a", "@@", "[a]", "[ @@ ]");
}
#[test]
fn in_group_with_var() {
check("$x:ident", "$x @@", "[a]", "[ a @@ ]");
}
#[test]
fn keep_spacing_no_match() {
check("a", "b", "1+2 a 3 + 4", "1+2 b 3 + 4");
}
#[test]
fn keep_spacing_var() {
check("$e:expr", "$e @@", "1+2", "1+2 @@");
}
#[test]
fn spacing_mismatch() {
check("=", "@@", "==", "==");
}
#[test]
fn spacing_match() {
check("==", "@@", "==", "@@");
}
#[test]
fn tt_joint() {
check("$x:tt", "@@", "==", "@@");
}
#[test]
fn repeat_separator() {
check("$(a),*", "@", "a,a,a", "@");
}
#[test]
fn repeat_separator_joint() {
check("$(a)==*", "@", "a==a==a", "@");
}
#[test]
fn rep_empty_content_and_some_sep() {
check("$(),*", "@", ",,", "@");
}
#[test]
fn keyword_self() {
check("self", "this", "self", "this");
}
#[test]
fn nest_no() {
check_as(
"a + $e:expr",
"b + $e",
"a + ( a + x )",
"b + ( a + x )",
false,
);
}
#[test]
fn nest_yes() {
check_as(
"a + $e:expr",
"b + $e",
"a + ( a + x )",
"b + ( b + x )",
true,
);
}
#[test]
fn to_long_punct() {
check("x", "..=", "x", "..=");
}
#[test]
fn to_long_punct_tt() {
check("$t:tt", "$t", "..=", "..=");
}