use regex::Regex;
use std::sync::LazyLock;
static X_ARTICLE_RE: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"(?i)^https?://(?:www\.|mobile\.)?x\.com/i/article/\d+")
.expect("static X article URL regex is valid")
});
#[must_use]
pub fn is_x_article_url(url: &str) -> bool {
X_ARTICLE_RE.is_match(url)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn x_article_url_matches_apex_host() {
assert!(is_x_article_url("https://x.com/i/article/123"));
}
#[test]
fn x_article_url_matches_www_subdomain() {
assert!(is_x_article_url("https://www.x.com/i/article/9"));
}
#[test]
fn x_article_url_matches_mobile_subdomain() {
assert!(is_x_article_url("https://mobile.x.com/i/article/42"));
}
#[test]
fn x_article_url_is_case_insensitive_on_scheme_and_host() {
assert!(is_x_article_url("HTTPS://X.COM/i/article/7"));
}
#[test]
fn x_article_url_rejects_status_url() {
assert!(!is_x_article_url("https://x.com/u/status/123"));
}
#[test]
fn x_article_url_rejects_lookalike_mirror_host() {
assert!(!is_x_article_url("https://fxtwitter.com/i/article/1"));
}
#[test]
fn x_article_url_rejects_home_feed() {
assert!(!is_x_article_url("https://x.com/home"));
}
#[test]
fn x_article_url_rejects_article_path_without_numeric_id() {
assert!(!is_x_article_url("https://x.com/i/article/abc"));
}
#[test]
fn x_article_url_rejects_twitter_com_apex() {
assert!(!is_x_article_url("https://twitter.com/i/article/1"));
}
}