use litext::{extract, FromLit, LitBool, LitByte, LitByteStr, LitCStr, LitChar, LitFloat, LitInt, LitStr, ToTokens};
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"));
}