use perl_lexer::{PerlLexer, TokenType};
type R = Result<(), Box<dyn std::error::Error>>;
fn significant(input: &str) -> Vec<perl_lexer::Token> {
PerlLexer::new(input)
.collect_tokens()
.into_iter()
.filter(|t| {
!matches!(t.token_type, TokenType::Whitespace | TokenType::Newline | TokenType::EOF)
})
.collect()
}
#[test]
fn test_vstring_three_part_version() -> R {
let toks = significant("v5.26.0");
assert_eq!(toks.len(), 1, "expected single token, got: {:?}", toks);
assert!(
matches!(&toks[0].token_type, TokenType::Version(v) if v.as_ref() == "v5.26.0"),
"expected Version(v5.26.0), got: {:?}",
toks[0].token_type
);
assert_eq!(toks[0].text.as_ref(), "v5.26.0");
assert_eq!(toks[0].start, 0);
assert_eq!(toks[0].end, 7);
Ok(())
}
#[test]
fn test_vstring_two_part_version() -> R {
let toks = significant("v5.10");
assert_eq!(toks.len(), 1, "expected single token, got: {:?}", toks);
assert!(
matches!(&toks[0].token_type, TokenType::Version(v) if v.as_ref() == "v5.10"),
"expected Version(v5.10), got: {:?}",
toks[0].token_type
);
Ok(())
}
#[test]
fn test_vstring_four_part_version() -> R {
let toks = significant("v1.2.3.4");
assert_eq!(toks.len(), 1, "expected single token, got: {:?}", toks);
assert!(
matches!(&toks[0].token_type, TokenType::Version(v) if v.as_ref() == "v1.2.3.4"),
"expected Version(v1.2.3.4), got: {:?}",
toks[0].token_type
);
Ok(())
}
#[test]
fn test_vstring_in_use_statement() -> R {
let toks = significant("use v5.26;");
let version_tok = toks.iter().find(|t| matches!(&t.token_type, TokenType::Version(_)));
assert!(
version_tok.is_some(),
"expected a Version token in 'use v5.26;', got: {:?}",
toks.iter().map(|t| format!("{:?}", t.token_type)).collect::<Vec<_>>()
);
let vt = version_tok.ok_or("no version token")?;
assert!(
matches!(&vt.token_type, TokenType::Version(v) if v.as_ref() == "v5.26"),
"expected Version(v5.26), got: {:?}",
vt.token_type
);
Ok(())
}
#[test]
fn test_vstring_in_require_statement() -> R {
let toks = significant("require v5.10.0;");
let version_tok = toks.iter().find(|t| matches!(&t.token_type, TokenType::Version(_)));
assert!(
version_tok.is_some(),
"expected a Version token in 'require v5.10.0;', got: {:?}",
toks.iter().map(|t| format!("{:?}", t.token_type)).collect::<Vec<_>>()
);
Ok(())
}
#[test]
fn test_vstring_in_assignment() -> R {
let toks = significant("$VERSION = v1.2.3;");
let version_tok = toks.iter().find(|t| matches!(&t.token_type, TokenType::Version(_)));
assert!(
version_tok.is_some(),
"expected a Version token in '$VERSION = v1.2.3;', got: {:?}",
toks.iter().map(|t| format!("{:?}", t.token_type)).collect::<Vec<_>>()
);
Ok(())
}
#[test]
fn test_bare_v_is_identifier() -> R {
let toks = significant("v");
assert_eq!(toks.len(), 1);
assert!(
matches!(&toks[0].token_type, TokenType::Identifier(_) | TokenType::Keyword(_)),
"expected identifier for bare 'v', got: {:?}",
toks[0].token_type
);
Ok(())
}
#[test]
fn test_v_followed_by_alpha_is_identifier() -> R {
let toks = significant("var");
assert_eq!(toks.len(), 1);
assert!(
matches!(&toks[0].token_type, TokenType::Identifier(_) | TokenType::Keyword(_)),
"expected identifier for 'var', got: {:?}",
toks[0].token_type
);
Ok(())
}
#[test]
fn test_v_digits_no_dot_is_vstring() -> R {
let toks = significant("v5");
assert_eq!(toks.len(), 1, "expected single token, got: {:?}", toks);
assert!(
matches!(&toks[0].token_type, TokenType::Version(v) if v.as_ref() == "v5"),
"expected Version(v5) for bare v-string, got: {:?}",
toks[0].token_type
);
Ok(())
}
#[test]
fn test_v_digits_underscore_is_identifier() -> R {
let toks = significant("v5_test");
assert_eq!(toks.len(), 1);
assert!(
matches!(&toks[0].token_type, TokenType::Identifier(_) | TokenType::Keyword(_)),
"expected identifier for 'v5_test', got: {:?}",
toks[0].token_type
);
Ok(())
}
#[test]
fn test_v_digits_alpha_is_identifier() -> R {
let toks = significant("v5x");
assert_eq!(toks.len(), 1);
assert!(
matches!(&toks[0].token_type, TokenType::Identifier(_) | TokenType::Keyword(_)),
"expected identifier for 'v5x', got: {:?}",
toks[0].token_type
);
Ok(())
}
#[test]
fn test_vstring_span_in_statement() -> R {
let toks = significant("use v5.26.0;");
let version_tok = toks
.iter()
.find(|t| matches!(&t.token_type, TokenType::Version(_)))
.ok_or("no version token found")?;
assert_eq!(version_tok.start, 4, "v-string should start at byte 4");
assert_eq!(version_tok.end, 11, "v-string should end at byte 11");
Ok(())
}
#[test]
fn test_vstring_trailing_dot_not_consumed() -> R {
let toks = significant("v5.26.");
let version_tok = toks.iter().find(|t| matches!(&t.token_type, TokenType::Version(_)));
assert!(
version_tok.is_some(),
"expected Version token for 'v5.26.', got: {:?}",
toks.iter().map(|t| format!("{:?}", t.token_type)).collect::<Vec<_>>()
);
let vt = version_tok.ok_or("no version token")?;
assert!(
matches!(&vt.token_type, TokenType::Version(v) if v.as_ref() == "v5.26"),
"expected Version(v5.26), got: {:?}",
vt.token_type
);
Ok(())
}
#[test]
fn test_vstring_large_version_numbers() -> R {
let toks = significant("v536.100.200");
assert_eq!(toks.len(), 1);
assert!(
matches!(&toks[0].token_type, TokenType::Version(v) if v.as_ref() == "v536.100.200"),
"expected Version(v536.100.200), got: {:?}",
toks[0].token_type
);
Ok(())
}