use litext::{
FromLit, LitBool, LitByte, LitByteStr, LitCStr, LitChar, LitFloat, LitInt, LitStr, ToTokens,
extract, litext,
};
use proc_macro2::{Literal, TokenStream, TokenTree};
use std::ffi::CString;
fn lit(s: &str) -> Literal {
s.parse::<proc_macro2::TokenStream>()
.unwrap()
.into_iter()
.next()
.and_then(|t| match t {
TokenTree::Literal(l) => Some(l),
_ => None,
})
.unwrap()
}
fn ident(s: &str) -> proc_macro2::Ident {
s.parse::<proc_macro2::TokenStream>()
.unwrap()
.into_iter()
.next()
.and_then(|t| match t {
TokenTree::Ident(i) => Some(i),
_ => None,
})
.unwrap()
}
fn ok<T: FromLit>(s: &str) -> T {
T::from_lit(lit(s)).expect("expected Ok")
}
fn ok_ident<T: FromLit>(s: &str) -> T {
T::from_ident(ident(s)).expect("expected Ok")
}
fn is_err<T: FromLit>(s: &str) -> bool {
T::from_lit(lit(s)).is_err()
}
fn err_msg<T: FromLit>(s: &str) -> String {
T::from_lit(lit(s))
.err()
.map(|ts| ts.to_string())
.expect("expected Err")
}
fn token_stream(s: &str) -> TokenStream {
s.parse().unwrap()
}
#[test]
fn extract_empty_token_stream() {
let ts = token_stream("");
assert!(extract::<String>(ts).is_err());
}
#[test]
fn extract_multiple_tokens() {
let ts = token_stream("42 43");
assert!(extract::<i32>(ts).is_err());
}
#[test]
fn extract_punctuation() {
let ts = token_stream("+");
assert!(extract::<String>(ts).is_err());
}
#[test]
fn extract_group() {
let ts = token_stream("(42)");
assert!(extract::<i32>(ts).is_err());
}
#[test]
fn string_line_continuation() {
let raw = "\"hello \\\n world\"";
let result = ok::<String>(raw);
assert_eq!(result, "hello world");
}
#[test]
fn string_mixed_unicode_and_escapes() {
assert_eq!(
ok::<String>(r#""\u{1F600} says \n hello""#),
"\u{1F600} says \n hello"
);
}
#[test]
fn litstr_with_escapes() {
let lit = ok::<LitStr>(r#""hello\nworld""#);
assert_eq!(lit.value(), "hello\nworld");
}
#[test]
fn litstr_span_preservation() {
let lit = ok::<LitStr>(r#""test""#);
let _ = lit.span();
}
#[test]
fn char_null_escape() {
assert_eq!(ok::<char>(r"'\0'"), '\0');
}
#[test]
fn char_hex_escape() {
assert_eq!(ok::<char>(r"'\x41'"), 'A');
}
#[test]
fn litchar_span_preservation() {
let lit = ok::<LitChar>("'a'");
let _ = lit.span();
}
#[test]
fn bool_lit_rejects_punctuation() {
let ts = token_stream("+");
let result: Result<bool, _> = extract(ts);
assert!(result.is_err());
}
#[test]
fn bool_lit_rejects_group() {
let ts = token_stream("(true)");
let result: Result<bool, _> = extract(ts);
assert!(result.is_err());
}
#[test]
fn litbool_from_lit_errors() {
assert!(LitBool::from_lit(lit("42")).is_err());
}
#[test]
fn isize_basic() {
assert_eq!(ok::<isize>("42"), 42isize);
}
#[test]
fn usize_basic() {
assert_eq!(ok::<usize>("42"), 42usize);
}
#[test]
fn isize_max() {
assert_eq!(ok::<isize>(&isize::MAX.to_string()), isize::MAX);
}
#[test]
fn usize_max() {
assert_eq!(ok::<usize>(&usize::MAX.to_string()), usize::MAX);
}
#[test]
fn integer_negative_via_lit() {
assert_eq!(ok::<i32>("42"), 42);
}
#[test]
fn integer_trailing_underscore() {
let _ = ok::<i32>("42_");
}
#[test]
fn litint_suffix_i32() {
let lit = ok::<LitInt<i32>>("42i32");
assert_eq!(lit.suffix(), Some("i32"));
}
#[test]
fn litint_suffix_u8() {
let lit = ok::<LitInt<u8>>("255u8");
assert_eq!(lit.suffix(), Some("u8"));
}
#[test]
fn litint_no_suffix() {
let lit = ok::<LitInt<i32>>("42");
assert_eq!(lit.suffix(), None);
}
#[test]
fn litint_hex_suffix() {
let lit = ok::<LitInt<u32>>("0xFFu32");
assert_eq!(*lit.value(), 255u32);
assert_eq!(lit.suffix(), Some("u32"));
}
#[test]
fn f32_without_suffix() {
assert_eq!(ok::<f32>("3.14"), 3.14f32);
}
#[test]
fn f64_scientific_positive_exp() {
assert_eq!(ok::<f64>("1e+10"), 1e10f64);
}
#[test]
fn f64_scientific_uppercase() {
assert_eq!(ok::<f64>("1E10"), 1e10f64);
}
#[test]
fn f64_dot_without_fraction() {
assert_eq!(ok::<f64>("3."), 3.0f64);
}
#[test]
fn litfloat_suffix_f32() {
let lit = ok::<LitFloat<f32>>("3.14f32");
assert_eq!(lit.suffix(), Some("f32"));
}
#[test]
fn litfloat_suffix_f64() {
let lit = ok::<LitFloat<f64>>("3.14f64");
assert_eq!(lit.suffix(), Some("f64"));
}
#[test]
fn litfloat_no_suffix() {
let lit = ok::<LitFloat<f64>>("3.14");
assert_eq!(lit.suffix(), None);
}
#[test]
fn litbyte_from_lit() {
let l = Literal::byte_character(b'a');
let lit: LitByte = FromLit::from_lit(l).expect("should parse");
assert_eq!(lit.value(), b'a');
}
#[test]
fn litbyte_has_span() {
let l = Literal::byte_character(b'a');
let lit: LitByte = FromLit::from_lit(l).expect("should parse");
let _ = lit.span();
}
#[test]
fn byte_rejects_string() {
assert!(is_err::<u8>(r#""a""#));
}
#[test]
fn byte_escape_newline() {
let l = Literal::byte_character(b'\n');
let val: u8 = FromLit::from_lit(l).expect("should parse");
assert_eq!(val, b'\n');
}
#[test]
fn byte_escape_tab() {
let l = Literal::byte_character(b'\t');
let val: u8 = FromLit::from_lit(l).expect("should parse");
assert_eq!(val, b'\t');
}
#[test]
fn byte_escape_backslash() {
let l = Literal::byte_character(b'\\');
let val: u8 = FromLit::from_lit(l).expect("should parse");
assert_eq!(val, b'\\');
}
#[test]
fn vec_u8_escape_hex() {
assert_eq!(ok::<Vec<u8>>(r#"b"\x41""#), vec![0x41u8]);
}
#[test]
fn vec_u8_escape_backslash() {
assert_eq!(ok::<Vec<u8>>(r#"b"\\""#), vec![b'\\']);
}
#[test]
fn litbytestr_escape() {
let lit = ok::<LitByteStr>(r#"b"\n""#);
assert_eq!(lit.value(), b"\n");
}
#[test]
fn cstring_escape_hex() {
let result = ok::<CString>(r#"c"\x41""#);
assert_eq!(result.as_bytes(), b"A");
}
#[test]
fn cstring_raw_with_escapes() {
let result = ok::<CString>(r##"cr#"\n"#"##);
assert_eq!(result.as_bytes(), b"\\n");
}
#[test]
fn litcstr_raw() {
let lit = ok::<LitCStr>(r##"cr#"hello"#"##);
assert_eq!(lit.value().to_bytes(), b"hello");
}
#[test]
fn to_tokens_litstr_roundtrip() {
let original = ok::<LitStr>(r#""hello""#);
let tokens = original.to_token_stream();
let extracted: LitStr = extract(tokens).expect("round-trip failed");
assert_eq!(extracted.value(), "hello");
}
#[test]
fn to_tokens_litint_roundtrip() {
let original = ok::<LitInt<i32>>("42i32");
let tokens = original.to_token_stream();
let extracted: LitInt<i32> = extract(tokens).expect("round-trip failed");
assert_eq!(*extracted.value(), 42);
assert_eq!(extracted.suffix(), Some("i32"));
}
#[test]
fn to_tokens_litfloat_roundtrip() {
let original = ok::<LitFloat<f64>>("3.14");
let tokens = original.to_token_stream();
let extracted: LitFloat<f64> = extract(tokens).expect("round-trip failed");
assert_eq!(*extracted.value(), 3.14);
}
#[test]
fn to_tokens_litbool_roundtrip() {
let original = ok_ident::<LitBool>("true");
let tokens = original.to_token_stream();
let extracted: LitBool = extract(tokens).expect("round-trip failed");
assert!(extracted.value());
}
#[test]
fn to_tokens_litchar_roundtrip() {
let original = ok::<LitChar>("'a'");
let tokens = original.to_token_stream();
let extracted: LitChar = extract(tokens).expect("round-trip failed");
assert_eq!(extracted.value(), 'a');
}
#[test]
fn to_tokens_litbyte_roundtrip() {
let original = ok::<LitByte>(r"b'a'");
let tokens = original.to_token_stream();
let extracted: LitByte = extract(tokens).expect("round-trip failed");
assert_eq!(extracted.value(), b'a');
}
#[test]
fn to_tokens_litbytestr_roundtrip() {
let original = ok::<LitByteStr>(r#"b"hello""#);
let tokens = original.to_token_stream();
let extracted: LitByteStr = extract(tokens).expect("round-trip failed");
assert_eq!(extracted.value(), b"hello");
}
#[test]
fn to_tokens_litcstr_roundtrip() {
let original = ok::<LitCStr>(r#"c"hello""#);
let tokens = original.to_token_stream();
let extracted: LitCStr = extract(tokens).expect("round-trip failed");
assert_eq!(extracted.value().to_bytes(), b"hello");
}
#[test]
fn error_int_out_of_range_message() {
let msg = err_msg::<i8>("128");
assert!(msg.contains("compile_error"));
assert!(msg.contains("out of range") || msg.contains("overflow"));
}
#[test]
fn error_float_malformed() {
let msg = err_msg::<f64>(r#""not_a_float""#);
assert!(msg.contains("compile_error"));
}
#[test]
fn error_char_wrong_type() {
let msg = err_msg::<char>(r#""hello""#);
assert!(msg.contains("compile_error"));
}
#[test]
fn error_byte_wrong_type() {
let msg = err_msg::<u8>(r#""a""#);
assert!(msg.contains("compile_error"));
}
#[test]
fn error_cstring_wrong_type() {
let msg = err_msg::<CString>(r#""hello""#);
assert!(msg.contains("compile_error"));
}
#[test]
fn string_basic() {
assert_eq!(ok::<String>(r#""hello""#), "hello");
}
#[test]
fn string_empty() {
assert_eq!(ok::<String>(r#""""#), "");
}
#[test]
fn string_escape_newline() {
assert_eq!(ok::<String>(r#""\n""#), "\n");
}
#[test]
fn string_escape_tab() {
assert_eq!(ok::<String>(r#""\t""#), "\t");
}
#[test]
fn string_escape_carriage_return() {
assert_eq!(ok::<String>(r#""\r""#), "\r");
}
#[test]
fn string_escape_backslash() {
assert_eq!(ok::<String>(r#""\\""#), "\\");
}
#[test]
fn string_escape_double_quote() {
assert_eq!(ok::<String>(r#""\"""#), "\"");
}
#[test]
fn string_escape_null() {
assert_eq!(ok::<String>(r#""\0""#), "\0");
}
#[test]
fn string_escape_hex() {
assert_eq!(ok::<String>(r#""\x41""#), "A");
}
#[test]
fn string_escape_unicode() {
assert_eq!(ok::<String>(r#""\u{1F600}""#), "\u{1F600}");
}
#[test]
fn string_escape_unicode_crab() {
assert_eq!(ok::<String>(r#""\u{1F980}""#), "🦀");
}
#[test]
fn string_raw_basic() {
assert_eq!(ok::<String>(r##"r#"hello"#"##), "hello");
}
#[test]
fn string_raw_empty() {
assert_eq!(ok::<String>(r##"r#""#"##), "");
}
#[test]
fn string_raw_with_inner_quotes() {
assert_eq!(ok::<String>(r##"r#"say "hi""#"##), r#"say "hi""#);
}
#[test]
fn string_raw_double_hash() {
assert_eq!(ok::<String>(r###"r##"hello # world"##"###), "hello # world");
}
#[test]
fn string_raw_no_escape_processing() {
assert_eq!(ok::<String>(r##"r#"\n"#"##), r"\n");
}
#[test]
fn string_unicode_content() {
assert_eq!(ok::<String>(r#""hello 世界""#), "hello 世界");
}
#[test]
fn string_rejects_byte_string() {
assert!(is_err::<String>(r#"b"hello""#));
}
#[test]
fn string_error_contains_message() {
let msg = err_msg::<String>(r#"b"hello""#);
assert!(msg.contains("compile_error"));
}
#[test]
fn litstr_value() {
let lit = ok::<LitStr>(r#""hello""#);
assert_eq!(lit.value(), "hello");
}
#[test]
fn litstr_has_span() {
let lit = ok::<LitStr>(r#""hello""#);
let _ = lit.span();
}
#[test]
fn litstr_raw() {
let lit = ok::<LitStr>(r##"r#"hello"#"##);
assert_eq!(lit.value(), "hello");
}
#[test]
fn litstr_empty() {
let lit = ok::<LitStr>(r#""""#);
assert_eq!(lit.value(), "");
}
#[test]
fn char_basic() {
assert_eq!(ok::<char>("'a'"), 'a');
}
#[test]
fn char_escape_newline() {
assert_eq!(ok::<char>(r"'\n'"), '\n');
}
#[test]
fn char_escape_tab() {
assert_eq!(ok::<char>(r"'\t'"), '\t');
}
#[test]
fn char_escape_backslash() {
assert_eq!(ok::<char>(r"'\\'"), '\\');
}
#[test]
fn char_unicode() {
assert_eq!(ok::<char>(r"'\u{1F980}'"), '🦀');
}
#[test]
fn char_unicode_basic() {
assert_eq!(ok::<char>(r"'\u{41}'"), 'A');
}
#[test]
fn litchar_value() {
let lit = ok::<LitChar>("'z'");
assert_eq!(lit.value(), 'z');
}
#[test]
fn litchar_has_span() {
let lit = ok::<LitChar>("'z'");
let _ = lit.span();
}
#[test]
fn char_rejects_string() {
assert!(is_err::<char>(r#""hello""#));
}
#[test]
fn bool_true() {
assert!(ok_ident::<bool>("true"));
}
#[test]
fn bool_false() {
assert!(!ok_ident::<bool>("false"));
}
#[test]
fn bool_rejects_literal() {
assert!(is_err::<bool>(r#""true""#));
}
#[test]
fn bool_rejects_other_ident() {
assert!(bool::from_ident(ident("yes")).is_err());
}
#[test]
fn litbool_true_value() {
let lit = ok_ident::<LitBool>("true");
assert!(lit.value());
}
#[test]
fn litbool_false_value() {
let lit = ok_ident::<LitBool>("false");
assert!(!lit.value());
}
#[test]
fn litbool_has_span() {
let lit = ok_ident::<LitBool>("true");
let _ = lit.span();
}
#[test]
fn i32_basic() {
assert_eq!(ok::<i32>("42"), 42);
}
#[test]
fn i32_negative() {
assert_eq!(ok::<i32>("0"), 0);
}
#[test]
fn i32_with_suffix() {
assert_eq!(ok::<i32>("42i32"), 42);
}
#[test]
fn u8_basic() {
assert_eq!(ok::<u8>("255"), 255u8);
}
#[test]
fn u8_with_suffix() {
assert_eq!(ok::<u8>("42u8"), 42u8);
}
#[test]
fn u8_overflow() {
assert!(is_err::<u8>("256"));
}
#[test]
fn i8_overflow() {
assert!(is_err::<i8>("128"));
}
#[test]
fn i64_large() {
assert_eq!(ok::<i64>("9223372036854775807"), i64::MAX);
}
#[test]
fn u64_large() {
assert_eq!(ok::<u64>("18446744073709551615"), u64::MAX);
}
#[test]
fn i128_large() {
assert_eq!(
ok::<i128>("170141183460469231731687303715884105727"),
i128::MAX
);
}
#[test]
fn u128_large() {
assert_eq!(
ok::<u128>("340282366920938463463374607431768211455"),
u128::MAX
);
}
#[test]
fn integer_hex() {
assert_eq!(ok::<u32>("0xFF"), 255u32);
}
#[test]
fn integer_hex_uppercase() {
assert_eq!(ok::<u32>("0XFF"), 255u32);
}
#[test]
fn integer_hex_mixed_case() {
assert_eq!(ok::<u32>("0xDeAdBeEf"), 0xDeAdBeEfu32);
}
#[test]
fn integer_octal() {
assert_eq!(ok::<u32>("0o77"), 63u32);
}
#[test]
fn integer_octal_uppercase() {
assert_eq!(ok::<u32>("0O77"), 63u32);
}
#[test]
fn integer_binary() {
assert_eq!(ok::<u32>("0b1010"), 10u32);
}
#[test]
fn integer_binary_uppercase() {
assert_eq!(ok::<u32>("0B1010"), 10u32);
}
#[test]
fn integer_underscores() {
assert_eq!(ok::<u32>("1_000_000"), 1_000_000u32);
}
#[test]
fn integer_hex_underscores() {
assert_eq!(ok::<u32>("0xFF_FF"), 0xFFFFu32);
}
#[test]
fn integer_binary_underscores() {
assert_eq!(ok::<u32>("0b1111_0000"), 0b1111_0000u32);
}
#[test]
fn integer_zero() {
assert_eq!(ok::<u32>("0"), 0u32);
}
#[test]
fn litint_value() {
let lit = ok::<LitInt<i32>>("42i32");
assert_eq!(*lit.value(), 42i32);
}
#[test]
fn litint_has_span() {
let lit = ok::<LitInt<u8>>("42u8");
let _ = lit.span();
}
#[test]
fn litint_u8() {
let lit = ok::<LitInt<u8>>("255");
assert_eq!(*lit.value(), 255u8);
}
#[test]
fn litint_default_is_i32() {
let lit = ok::<LitInt>("42");
assert_eq!(*lit.value(), 42i32);
}
#[test]
fn f64_basic() {
assert_eq!(ok::<f64>("3.14"), 3.14f64);
}
#[test]
fn f32_basic() {
assert_eq!(ok::<f32>("3.14f32"), 3.14f32);
}
#[test]
fn f64_with_suffix() {
assert_eq!(ok::<f64>("3.14f64"), 3.14f64);
}
#[test]
fn f64_zero() {
assert_eq!(ok::<f64>("0.0"), 0.0f64);
}
#[test]
fn f64_underscores() {
assert_eq!(ok::<f64>("1_000.0"), 1000.0f64);
}
#[test]
fn f64_scientific() {
assert_eq!(ok::<f64>("1e10"), 1e10f64);
}
#[test]
fn f64_scientific_negative_exp() {
assert_eq!(ok::<f64>("1e-10"), 1e-10f64);
}
#[test]
fn litfloat_value() {
let lit = ok::<LitFloat<f64>>("3.14");
assert_eq!(*lit.value(), 3.14f64);
}
#[test]
fn litfloat_has_span() {
let lit = ok::<LitFloat<f64>>("3.14");
let _ = lit.span();
}
#[test]
fn litfloat_default_is_f64() {
let lit = ok::<LitFloat>("3.14");
assert_eq!(*lit.value(), 3.14f64);
}
#[test]
fn litfloat_f32() {
let lit = ok::<LitFloat<f32>>("1.0f32");
assert_eq!(*lit.value(), 1.0f32);
}
#[test]
fn vec_u8_basic() {
assert_eq!(ok::<Vec<u8>>(r#"b"hello""#), b"hello");
}
#[test]
fn vec_u8_empty() {
assert_eq!(ok::<Vec<u8>>(r#"b"""#), b"");
}
#[test]
fn vec_u8_escape_newline() {
assert_eq!(ok::<Vec<u8>>(r#"b"\n""#), b"\n");
}
#[test]
fn vec_u8_raw() {
assert_eq!(ok::<Vec<u8>>(r##"br#"hello"#"##), b"hello");
}
#[test]
fn vec_u8_raw_with_quotes() {
assert_eq!(ok::<Vec<u8>>(r##"br#"say "hi""#"##), b"say \"hi\"");
}
#[test]
fn litbytestr_value() {
let lit = ok::<LitByteStr>(r#"b"hello""#);
assert_eq!(lit.value(), b"hello");
}
#[test]
fn litbytestr_has_span() {
let lit = ok::<LitByteStr>(r#"b"hello""#);
let _ = lit.span();
}
#[test]
fn litbytestr_rejects_string() {
assert!(is_err::<LitByteStr>(r#""hello""#));
}
#[test]
fn litbytestr_raw_no_escape() {
assert_eq!(ok::<Vec<u8>>(r##"br#"\n"#"##), b"\\n");
}
#[test]
fn cstring_basic() {
assert_eq!(ok::<CString>(r#"c"hello""#), CString::new("hello").unwrap());
}
#[test]
fn cstring_empty() {
assert_eq!(ok::<CString>(r#"c"""#), CString::new("").unwrap());
}
#[test]
fn cstring_escape_newline() {
assert_eq!(ok::<CString>(r#"c"\n""#), CString::new("\n").unwrap());
}
#[test]
fn cstring_raw() {
assert_eq!(
ok::<CString>(r##"cr#"hello"#"##),
CString::new("hello").unwrap()
);
}
#[test]
fn litcstr_value() {
let lit = ok::<LitCStr>(r#"c"hello""#);
assert_eq!(lit.value(), CString::new("hello").unwrap().as_c_str());
}
#[test]
fn litcstr_has_span() {
let lit = ok::<LitCStr>(r#"c"hello""#);
let _ = lit.span();
}
#[test]
fn litcstr_rejects_string() {
assert!(is_err::<LitCStr>(r#""hello""#));
}
#[test]
fn litcstr_rejects_byte_string() {
assert!(is_err::<LitCStr>(r#"b"hello""#));
}
#[test]
fn error_contains_compile_error() {
let msg = err_msg::<String>(r#"b"hello""#);
assert!(msg.contains("compile_error"));
}
#[test]
fn error_byte_string_message() {
let msg = err_msg::<String>(r#"b"hello""#);
assert!(msg.contains("byte string"));
}
#[test]
fn error_u8_overflow_message() {
let msg = err_msg::<u8>("256");
assert!(msg.contains("compile_error"));
}
#[test]
fn error_wrong_type_for_char() {
let msg = err_msg::<char>(r#""hello""#);
assert!(msg.contains("compile_error"));
}
#[test]
fn litext_try_string() {
let ts = token_stream(r#""hello""#);
let result: Result<String, _> = litext!(try ts);
assert_eq!(result.unwrap(), "hello");
}
#[test]
fn litext_try_string_explicit() {
let ts = token_stream(r#""world""#);
let result: Result<String, _> = litext!(try ts as String);
assert_eq!(result.unwrap(), "world");
}
#[test]
fn litext_try_i32() {
let ts = token_stream("99");
let result: Result<i32, _> = litext!(try ts as i32);
assert_eq!(result.unwrap(), 99);
}
#[test]
fn litext_try_f64() {
let ts = token_stream("2.718");
let result: Result<f64, _> = litext!(try ts as f64);
assert!((result.unwrap() - 2.718f64).abs() < 1e-9);
}
#[test]
fn litext_try_bool_true() {
let ts = token_stream("true");
let result: Result<bool, _> = litext!(try ts as bool);
assert!(result.unwrap());
}
#[test]
fn litext_try_char() {
let ts = token_stream("'z'");
let result: Result<char, _> = litext!(try ts as char);
assert_eq!(result.unwrap(), 'z');
}
#[test]
fn litext_try_error_empty() {
let ts = token_stream("");
let result: Result<String, _> = litext!(try ts);
assert!(result.is_err());
}
#[test]
fn litext_try_error_wrong_type() {
let ts = token_stream("42");
let result: Result<String, _> = litext!(try ts);
assert!(result.is_err());
}
#[test]
fn litext_try_tuple_string_i32() {
let ts: TokenStream = r#""hello" , 42"#.parse().unwrap();
let result: Result<(String, i32), _> = litext!(try ts as (String , i32));
let (s, n) = result.unwrap();
assert_eq!(s, "hello");
assert_eq!(n, 42);
}
#[test]
fn litext_try_tuple_string_f64() {
let ts: TokenStream = r#""pi" , 3.14"#.parse().unwrap();
let result: Result<(String, f64), _> = litext!(try ts as (String , f64));
let (s, f) = result.unwrap();
assert_eq!(s, "pi");
assert!((f - 3.14f64).abs() < 1e-9);
}
#[test]
fn litext_try_tuple_three() {
let ts: TokenStream = r#""hello" , 42 , true"#.parse().unwrap();
let result: Result<(String, i32, bool), _> = litext!(try ts as (String , i32 , bool));
let (s, n, b) = result.unwrap();
assert_eq!(s, "hello");
assert_eq!(n, 42);
assert!(b);
}
#[test]
fn litext_try_tuple_wrong_type_errors() {
let ts: TokenStream = r#""hello" , "not_an_int""#.parse().unwrap();
let result: Result<(String, i32), _> = litext!(try ts as (String , i32));
assert!(result.is_err());
}
#[test]
fn litext_try_tuple_missing_sep_errors() {
let ts: TokenStream = r#""hello" "world""#.parse().unwrap();
let result: Result<(String, String), _> = litext!(try ts as (String , String));
assert!(result.is_err());
}
#[test]
fn litext_try_tuple_wrong_sep_errors() {
let ts: TokenStream = r#""hello" ; "world""#.parse().unwrap();
let result: Result<(String, String), _> = litext!(try ts as (String , String));
assert!(result.is_err());
}
#[test]
fn litext_try_tuple_semicolon_sep() {
let ts: TokenStream = r#""hello" ; 42"#.parse().unwrap();
let result: Result<(String, i32), _> = litext!(try ts as (String ; i32));
let (s, n) = result.unwrap();
assert_eq!(s, "hello");
assert_eq!(n, 42);
}
#[test]
fn litext_try_tuple_four() {
let ts: TokenStream = r#""a" , 1 , 2.0 , false"#.parse().unwrap();
let result: Result<(String, i32, f64, bool), _> =
litext!(try ts as (String , i32 , f64 , bool));
let (s, n, f, b) = result.unwrap();
assert_eq!(s, "a");
assert_eq!(n, 1);
assert!((f - 2.0f64).abs() < 1e-9);
assert!(!b);
}
#[test]
fn string_escape_hex_max_ascii() {
assert_eq!(ok::<String>(r#""\x7F""#), "\x7F");
}
#[test]
fn string_escape_unicode_null() {
assert_eq!(ok::<String>(r#""\u{0}""#), "\0");
}
#[test]
fn string_escape_unicode_max_codepoint() {
assert_eq!(ok::<String>(r#""\u{10FFFF}""#), "\u{10FFFF}");
}
#[test]
fn string_raw_zero_hashes() {
assert_eq!(ok::<String>(r#"r"plain""#), "plain");
}
#[test]
fn i16_max() {
assert_eq!(ok::<i16>("32767"), i16::MAX);
}
#[test]
fn i16_overflow() {
assert!(is_err::<i16>("32768"));
}
#[test]
fn u16_max() {
assert_eq!(ok::<u16>("65535"), u16::MAX);
}
#[test]
fn u16_overflow() {
assert!(is_err::<u16>("65536"));
}
#[test]
fn i32_max() {
assert_eq!(ok::<i32>("2147483647"), i32::MAX);
}
#[test]
fn i32_overflow() {
assert!(is_err::<i32>("2147483648"));
}
#[test]
fn u32_max() {
assert_eq!(ok::<u32>("4294967295"), u32::MAX);
}
#[test]
fn i64_overflow() {
assert!(is_err::<i64>("9223372036854775808"));
}
#[test]
fn integer_binary_all_ones_u32() {
assert_eq!(ok::<u32>("0b11111111_11111111_11111111_11111111"), u32::MAX);
}
#[test]
fn integer_hex_u64_max() {
assert_eq!(ok::<u64>("0xFFFF_FFFF_FFFF_FFFF"), u64::MAX);
}
#[test]
fn integer_zero_hex() {
assert_eq!(ok::<u32>("0x0"), 0u32);
}
#[test]
fn integer_zero_binary() {
assert_eq!(ok::<u32>("0b0"), 0u32);
}
#[test]
fn integer_zero_octal() {
assert_eq!(ok::<u32>("0o0"), 0u32);
}
#[test]
fn litint_isize_basic() {
let lit = ok::<LitInt<isize>>("42");
assert_eq!(*lit.value(), 42isize);
}
#[test]
fn litint_usize_basic() {
let lit = ok::<LitInt<usize>>("100");
assert_eq!(*lit.value(), 100usize);
}
#[test]
fn to_tokens_litint_isize_roundtrip() {
let original = ok::<LitInt<isize>>("999");
let tokens = original.to_token_stream();
let extracted: LitInt<isize> = extract(tokens).expect("round-trip failed");
assert_eq!(*extracted.value(), 999isize);
}
#[test]
fn to_tokens_litint_usize_roundtrip() {
let original = ok::<LitInt<usize>>("42");
let tokens = original.to_token_stream();
let extracted: LitInt<usize> = extract(tokens).expect("round-trip failed");
assert_eq!(*extracted.value(), 42usize);
}
#[test]
fn litint_i128_max() {
let lit = ok::<LitInt<i128>>("170141183460469231731687303715884105727");
assert_eq!(*lit.value(), i128::MAX);
}
#[test]
fn f64_integer_token_parses() {
assert_eq!(ok::<f64>("42"), 42.0f64);
}
#[test]
fn f64_leading_dot() {
assert_eq!(ok::<f64>("0.5"), 0.5f64);
}
#[test]
fn f32_very_small() {
assert!((ok::<f32>("0.001f32") - 0.001f32).abs() < f32::EPSILON);
}
#[test]
fn litfloat_isize_suffix_none() {
let lit = ok::<LitFloat<f64>>("1.0");
assert_eq!(lit.suffix(), None);
}
#[test]
fn char_escape_double_quote() {
assert_eq!(ok::<char>(r#"'\"'"#), '"');
}
#[test]
fn char_rejects_integer() {
assert!(is_err::<char>("42"));
}
#[test]
fn litchar_from_string_errors() {
let msg = err_msg::<char>(r#""a""#);
assert!(msg.contains("compile_error"));
}
#[test]
fn litbool_from_number_errors() {
assert!(LitBool::from_lit(lit("1")).is_err());
}
#[test]
fn litbool_from_unknown_ident_errors() {
assert!(LitBool::from_ident(ident("maybe")).is_err());
}
#[test]
fn byte_escape_high_hex() {
let l = Literal::byte_character(0xFFu8);
let val: u8 = FromLit::from_lit(l).expect("b'\\xff' should parse");
assert_eq!(val, 0xFF);
}
#[test]
fn byte_escape_0x80() {
let l = Literal::byte_character(0x80u8);
let val: u8 = FromLit::from_lit(l).expect("b'\\x80' should parse");
assert_eq!(val, 0x80);
}
#[test]
fn litbyte_high_hex() {
let l = Literal::byte_character(0xFEu8);
let lit: LitByte = FromLit::from_lit(l).expect("b'\\xFE' should parse");
assert_eq!(lit.value(), 0xFE);
}
#[test]
fn byte_null() {
let l = Literal::byte_character(0u8);
let val: u8 = FromLit::from_lit(l).expect("b'\\0' should parse");
assert_eq!(val, 0);
}
#[test]
fn byte_max() {
let l = Literal::byte_character(255u8);
let val: u8 = FromLit::from_lit(l).expect("b'\\xff' should parse");
assert_eq!(val, 255);
}
#[test]
fn vec_u8_high_hex() {
assert_eq!(ok::<Vec<u8>>(r#"b"\xff""#), vec![0xFFu8]);
}
#[test]
fn vec_u8_all_low_bytes() {
assert_eq!(ok::<Vec<u8>>(r#"b"\x00\x01\x02""#), vec![0u8, 1, 2]);
}
#[test]
fn litbytestr_high_byte() {
let lit = ok::<LitByteStr>(r#"b"\x80\xff""#);
assert_eq!(lit.value(), &[0x80u8, 0xFF]);
}
#[test]
fn vec_u8_mixed_escapes() {
assert_eq!(ok::<Vec<u8>>(r#"b"\n\t\r\\""#), b"\n\t\r\\");
}
#[test]
fn cstring_with_backslash() {
let result = ok::<CString>(r#"c"a\\b""#);
assert_eq!(result.as_bytes(), b"a\\b");
}
#[test]
fn litcstr_empty_rejects_string_lit() {
assert!(is_err::<LitCStr>(r#""""#));
}
#[test]
fn error_empty_stream_says_nothing() {
let ts = token_stream("");
let msg = extract::<String>(ts).err().unwrap().to_string();
assert!(msg.contains("compile_error"));
assert!(msg.contains("nothing"));
}
#[test]
fn error_multiple_tokens_says_exactly_one() {
let ts = token_stream("42 43");
let msg = extract::<i32>(ts).err().unwrap().to_string();
assert!(msg.contains("compile_error"));
assert!(msg.contains("exactly one"));
}
#[test]
fn error_i8_overflow_says_out_of_range() {
let msg = err_msg::<i8>("200");
assert!(msg.contains("compile_error"));
assert!(msg.contains("out of range") || msg.contains("overflow"));
}
#[test]
fn error_byte_string_rejected_as_string() {
let msg = err_msg::<String>(r#"b"test""#);
assert!(msg.contains("compile_error"));
assert!(msg.contains("byte string") || msg.contains("string"));
}
#[test]
fn to_tokens_litbool_false_roundtrip() {
let original = ok_ident::<LitBool>("false");
let tokens = original.to_token_stream();
let extracted: LitBool = extract(tokens).expect("round-trip failed");
assert!(!extracted.value());
}
#[test]
fn to_tokens_litint_i8_roundtrip() {
let original = ok::<LitInt<i8>>("127i8");
let tokens = original.to_token_stream();
let extracted: LitInt<i8> = extract(tokens).expect("round-trip failed");
assert_eq!(*extracted.value(), 127i8);
assert_eq!(extracted.suffix(), Some("i8"));
}
#[test]
fn to_tokens_litint_u64_roundtrip() {
let original = ok::<LitInt<u64>>("18446744073709551615");
let tokens = original.to_token_stream();
let extracted: LitInt<u64> = extract(tokens).expect("round-trip failed");
assert_eq!(*extracted.value(), u64::MAX);
}
#[test]
fn to_tokens_litfloat_f32_roundtrip() {
let original = ok::<LitFloat<f32>>("1.0f32");
let tokens = original.to_token_stream();
let extracted: LitFloat<f32> = extract(tokens).expect("round-trip failed");
assert_eq!(*extracted.value(), 1.0f32);
assert_eq!(extracted.suffix(), Some("f32"));
}
#[test]
fn to_tokens_litstr_empty_roundtrip() {
let original = ok::<LitStr>(r#""""#);
let tokens = original.to_token_stream();
let extracted: LitStr = extract(tokens).expect("round-trip failed");
assert_eq!(extracted.value(), "");
}
#[test]
fn to_tokens_litchar_unicode_roundtrip() {
let original = ok::<LitChar>(r"'\u{1F600}'");
let tokens = original.to_token_stream();
let extracted: LitChar = extract(tokens).expect("round-trip failed");
assert_eq!(extracted.value(), '\u{1F600}');
}
#[test]
fn to_tokens_litbytestr_high_bytes_roundtrip() {
let original = ok::<LitByteStr>(r#"b"\xff\x80""#);
let tokens = original.to_token_stream();
let extracted: LitByteStr = extract(tokens).expect("round-trip failed");
assert_eq!(extracted.value(), &[0xFF, 0x80]);
}
struct UpperString(String);
impl FromLit for UpperString {
fn from_lit(lit: proc_macro2::Literal) -> Result<Self, TokenStream> {
let s = String::from_lit(lit)?;
Ok(UpperString(s.to_uppercase()))
}
}
#[test]
fn custom_fromlit_impl() {
let lit_val = ok::<UpperString>(r#""hello""#);
assert_eq!(lit_val.0, "HELLO");
}
#[test]
fn isize_hex() {
assert_eq!(ok::<isize>("0xFF"), 255isize);
}
#[test]
fn usize_hex() {
assert_eq!(ok::<usize>("0xFF"), 255usize);
}
#[test]
fn usize_max_value() {
assert_eq!(ok::<usize>(&usize::MAX.to_string()), usize::MAX);
}
#[test]
fn tuple_no_space_comma() {
let ts: TokenStream = r#""hello", 42"#.parse().unwrap();
let result: Result<(String, u32), _> = litext!(try ts as (String, u32));
let (s, n) = result.unwrap();
assert_eq!(s, "hello");
assert_eq!(n, 42);
}
#[test]
fn negative_i8_basic() {
let ts = token_stream("-42");
let result: Result<i8, _> = extract(ts);
assert_eq!(result.unwrap(), -42);
}
#[test]
fn negative_i16_basic() {
let ts = token_stream("-1000");
let result: Result<i16, _> = extract(ts);
assert_eq!(result.unwrap(), -1000);
}
#[test]
fn negative_i32_basic() {
let ts = token_stream("-42");
let result: Result<i32, _> = extract(ts);
assert_eq!(result.unwrap(), -42);
}
#[test]
fn negative_i64_basic() {
let ts = token_stream("-42");
let result: Result<i64, _> = extract(ts);
assert_eq!(result.unwrap(), -42);
}
#[test]
fn negative_i128_basic() {
let ts = token_stream("-42");
let result: Result<i128, _> = extract(ts);
assert_eq!(result.unwrap(), -42);
}
#[test]
fn negative_isize_basic() {
let ts = token_stream("-42");
let result: Result<isize, _> = extract(ts);
assert_eq!(result.unwrap(), -42);
}
#[test]
fn negative_i32_zero() {
let ts = token_stream("-0");
let result: Result<i32, _> = extract(ts);
assert_eq!(result.unwrap(), 0);
}
#[test]
fn negative_i32_min() {
let ts = token_stream(&format!("-{}", i32::MAX as i64 + 1));
let result: Result<i32, _> = extract(ts);
assert_eq!(result.unwrap(), i32::MIN);
}
#[test]
fn negative_i8_min() {
let ts = token_stream("-128");
let result: Result<i8, _> = extract(ts);
assert_eq!(result.unwrap(), i8::MIN);
}
#[test]
fn negative_i16_min() {
let ts = token_stream("-32768");
let result: Result<i16, _> = extract(ts);
assert_eq!(result.unwrap(), i16::MIN);
}
#[test]
fn negative_i64_min() {
let ts = token_stream(&i64::MIN.to_string());
let result: Result<i64, _> = extract(ts);
assert_eq!(result.unwrap(), i64::MIN);
}
#[test]
fn negative_i128_min() {
let ts = token_stream(&i128::MIN.to_string());
let result: Result<i128, _> = extract(ts);
assert_eq!(result.unwrap(), i128::MIN);
}
#[test]
fn negative_u8_errors() {
let ts = token_stream("-42");
let result: Result<u8, _> = extract(ts);
assert!(result.is_err());
}
#[test]
fn negative_u16_errors() {
let ts = token_stream("-42");
let result: Result<u16, _> = extract(ts);
assert!(result.is_err());
}
#[test]
fn negative_u32_errors() {
let ts = token_stream("-42");
let result: Result<u32, _> = extract(ts);
assert!(result.is_err());
}
#[test]
fn negative_u64_errors() {
let ts = token_stream("-42");
let result: Result<u64, _> = extract(ts);
assert!(result.is_err());
}
#[test]
fn negative_u128_errors() {
let ts = token_stream("-42");
let result: Result<u128, _> = extract(ts);
assert!(result.is_err());
}
#[test]
fn negative_usize_errors() {
let ts = token_stream("-42");
let result: Result<usize, _> = extract(ts);
assert!(result.is_err());
}
#[test]
fn negative_u8_error_message() {
let ts = token_stream("-42");
let result: Result<u8, _> = extract(ts);
let msg = result.err().unwrap().to_string();
assert!(msg.contains("compile_error"));
assert!(msg.contains("unsigned") || msg.contains("negate"));
}
#[test]
fn negative_f32_basic() {
let ts = token_stream("-3.14");
let result: Result<f32, _> = extract(ts);
assert!((result.unwrap() - (-3.14f32)).abs() < f32::EPSILON);
}
#[test]
fn negative_f64_basic() {
let ts = token_stream("-3.14");
let result: Result<f64, _> = extract(ts);
assert!((result.unwrap() - (-3.14f64)).abs() < f64::EPSILON);
}
#[test]
fn negative_f64_zero() {
let ts = token_stream("-0.0");
let result: Result<f64, _> = extract(ts);
assert_eq!(result.unwrap(), -0.0);
}
#[test]
fn negative_f64_scientific() {
let ts = token_stream("-1e10");
let result: Result<f64, _> = extract(ts);
assert_eq!(result.unwrap(), -1e10f64);
}
#[test]
fn negative_f32_with_suffix() {
let ts = token_stream("-3.14f32");
let result: Result<f32, _> = extract(ts);
assert!((result.unwrap() - (-3.14f32)).abs() < f32::EPSILON);
}
#[test]
fn negative_f64_with_suffix() {
let ts = token_stream("-3.14f64");
let result: Result<f64, _> = extract(ts);
assert!((result.unwrap() - (-3.14f64)).abs() < f64::EPSILON);
}
#[test]
fn negative_litint_i32() {
let ts = token_stream("-42");
let result: Result<LitInt<i32>, _> = extract(ts);
let lit = result.unwrap();
assert_eq!(*lit.value(), -42);
}
#[test]
fn negative_litint_i64() {
let ts = token_stream("-100");
let result: Result<LitInt<i64>, _> = extract(ts);
let lit = result.unwrap();
assert_eq!(*lit.value(), -100);
}
#[test]
fn negative_litint_i32_min() {
let ts = token_stream(&format!("-{}", i32::MAX as i64 + 1));
let result: Result<LitInt<i32>, _> = extract(ts);
let lit = result.unwrap();
assert_eq!(*lit.value(), i32::MIN);
}
#[test]
fn negative_litint_has_span() {
let ts = token_stream("-42");
let result: Result<LitInt<i32>, _> = extract(ts);
let lit = result.unwrap();
let _ = lit.span();
}
#[test]
fn negative_litfloat_f64() {
let ts = token_stream("-3.14");
let result: Result<LitFloat<f64>, _> = extract(ts);
let lit = result.unwrap();
assert!((*lit.value() - (-3.14f64)).abs() < f64::EPSILON);
}
#[test]
fn negative_litfloat_f32() {
let ts = token_stream("-2.5");
let result: Result<LitFloat<f32>, _> = extract(ts);
let lit = result.unwrap();
assert!((*lit.value() - (-2.5f32)).abs() < f32::EPSILON);
}
#[test]
fn negative_litfloat_has_span() {
let ts = token_stream("-1.0");
let result: Result<LitFloat<f64>, _> = extract(ts);
let lit = result.unwrap();
let _ = lit.span();
}
#[test]
fn negative_dash_only_errors() {
let ts = token_stream("-");
let result: Result<i32, _> = extract(ts);
assert!(result.is_err());
}
#[test]
fn negative_dash_non_literal_errors() {
let ts: TokenStream = "- hello".parse().unwrap();
let result: Result<i32, _> = extract(ts);
assert!(result.is_err());
}
#[test]
fn negative_dash_then_ident_errors() {
let ts: TokenStream = "- true".parse().unwrap();
let result: Result<i32, _> = extract(ts);
assert!(result.is_err());
}
#[test]
fn negative_error_message_dash_only() {
let ts = token_stream("-");
let result: Result<i32, _> = extract(ts);
let msg = result.err().unwrap().to_string();
assert!(msg.contains("compile_error"));
assert!(msg.contains("nothing") || msg.contains("expected"));
}
#[test]
fn negative_error_message_non_literal() {
let ts: TokenStream = "- hello".parse().unwrap();
let result: Result<i32, _> = extract(ts);
let msg = result.err().unwrap().to_string();
assert!(msg.contains("compile_error"));
assert!(msg.contains("literal"));
}
#[test]
fn litext_try_negative_i32() {
let ts = token_stream("-42");
let result: Result<i32, _> = litext!(try ts as i32);
assert_eq!(result.unwrap(), -42);
}
#[test]
fn litext_try_negative_f64() {
let ts = token_stream("-3.14");
let result: Result<f64, _> = litext!(try ts as f64);
assert!((result.unwrap() - (-3.14f64)).abs() < 1e-9);
}
#[test]
fn litext_try_negative_u32_errors() {
let ts = token_stream("-42");
let result: Result<u32, _> = litext!(try ts as u32);
assert!(result.is_err());
}
#[test]
fn litext_tuple_negative_first() {
let ts: TokenStream = r#"-42 , "hello""#.parse().unwrap();
let result: Result<(i32, String), _> = litext!(try ts as (i32 , String));
let (n, s) = result.unwrap();
assert_eq!(n, -42);
assert_eq!(s, "hello");
}
#[test]
fn litext_tuple_negative_second() {
let ts: TokenStream = r#""hello" , -42"#.parse().unwrap();
let result: Result<(String, i32), _> = litext!(try ts as (String , i32));
let (s, n) = result.unwrap();
assert_eq!(s, "hello");
assert_eq!(n, -42);
}
#[test]
fn litext_tuple_both_negative() {
let ts: TokenStream = r#"-1 , -2"#.parse().unwrap();
let result: Result<(i32, i32), _> = litext!(try ts as (i32 , i32));
let (a, b) = result.unwrap();
assert_eq!(a, -1);
assert_eq!(b, -2);
}
#[test]
fn negative_i32_max_negated() {
let ts = token_stream(&format!("-{}", i32::MAX as i64 + 1));
let result: Result<i32, _> = extract(ts);
assert_eq!(result.unwrap(), i32::MIN);
}
#[test]
fn negative_with_underscores() {
let ts = token_stream("-1_000");
let result: Result<i32, _> = extract(ts);
assert_eq!(result.unwrap(), -1000);
}
#[test]
fn negative_hex_not_supported() {
let ts = token_stream("-0xFF");
let result: Result<i32, _> = extract(ts);
assert_eq!(result.unwrap(), -255);
}
#[test]
fn negative_overflow_i8() {
let ts = token_stream("-129");
let result: Result<i8, _> = extract(ts);
assert!(result.is_err());
}
#[test]
fn negative_litint_roundtrip() {
let original_ts = token_stream("-42");
let original: LitInt<i32> = extract(original_ts).expect("should parse");
let tokens = original.to_token_stream();
let extracted: LitInt<i32> = extract(tokens).expect("round-trip failed");
assert_eq!(*extracted.value(), -42);
}
#[test]
fn negative_litfloat_roundtrip() {
let original_ts = token_stream("-3.14");
let original: LitFloat<f64> = extract(original_ts).expect("should parse");
let tokens = original.to_token_stream();
let extracted: LitFloat<f64> = extract(tokens).expect("round-trip failed");
assert!((*extracted.value() - (-3.14f64)).abs() < f64::EPSILON);
}
#[test]
fn from_negative_lit_i32() {
let lit = Literal::i32_unsuffixed(42);
let result = i32::from_negative_lit(lit);
assert_eq!(result.unwrap(), -42);
}
#[test]
fn from_negative_lit_u32_errors() {
let lit = Literal::u32_unsuffixed(42);
let result = u32::from_negative_lit(lit);
assert!(result.is_err());
}
#[test]
fn from_negative_lit_f64() {
let lit = Literal::f64_unsuffixed(3.14);
let result = f64::from_negative_lit(lit);
assert!((result.unwrap() - (-3.14f64)).abs() < f64::EPSILON);
}
#[test]
fn from_negative_lit_string_errors() {
let lit = Literal::string("hello");
let result = String::from_negative_lit(lit);
assert!(result.is_err());
}