pub fn extract_between(s: &str, start: &str, end: &str) -> Option<String> {
let i = s.find(start)? + start.len();
let j = s[i..].find(end)? + i;
Some(s[i..j].to_string())
}
pub fn strip_time_and_tz(s: &str) -> String {
let trimmed = s.trim();
if let Ok(dt) = chrono::DateTime::parse_from_rfc2822(trimmed) {
return dt.format("%Y-%m-%d").to_string();
}
if let Ok(dt) = chrono::DateTime::parse_from_rfc3339(trimmed) {
return dt.format("%Y-%m-%d").to_string();
}
if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(trimmed, "%Y-%m-%dT%H:%M:%S") {
return dt.format("%Y-%m-%d").to_string();
}
if chrono::NaiveDate::parse_from_str(trimmed, "%Y-%m-%d").is_ok() {
return trimmed.to_string();
}
if let Some(date) = parse_partial_rfc2822(trimmed) {
return date;
}
let mut t = trimmed.to_string();
if let Some(pos) = t.rfind(" +") {
t.truncate(pos);
t = t.trim_end().to_string();
}
if t.len() >= 9 {
let n = t.len();
let time_part = &t[n - 8..n];
let looks_time = time_part.chars().enumerate().all(|(i, c)| match i {
2 | 5 => c == ':',
_ => c.is_ascii_digit(),
});
if looks_time && t.as_bytes()[n - 9] == b' ' {
t.truncate(n - 9);
}
}
t.trim_end().to_string()
}
fn parse_partial_rfc2822(s: &str) -> Option<String> {
let parts: Vec<&str> = s.split_whitespace().collect();
let (day_str, month_str, year_str) = if parts.len() >= 4 && parts[0].ends_with(',') {
(parts.get(1)?, parts.get(2)?, parts.get(3)?)
} else if parts.len() >= 3 {
(parts.first()?, parts.get(1)?, parts.get(2)?)
} else {
return None;
};
let day: u32 = day_str.parse().ok()?;
let month = match month_str.to_lowercase().as_str() {
"jan" => 1,
"feb" => 2,
"mar" => 3,
"apr" => 4,
"may" => 5,
"jun" => 6,
"jul" => 7,
"aug" => 8,
"sep" => 9,
"oct" => 10,
"nov" => 11,
"dec" => 12,
_ => return None,
};
let year: i32 = year_str.chars().take(4).collect::<String>().parse().ok()?;
if (1..=31).contains(&day) && (1970..=2100).contains(&year) {
Some(format!("{year:04}-{month:02}-{day:02}"))
} else {
None
}
}
pub fn is_archlinux_url(url: &str) -> bool {
url.starts_with("https://archlinux.org/") || url.starts_with("https://www.archlinux.org/")
}
pub fn resolve_href(href: &str, base_origin: Option<&str>) -> String {
if href.starts_with("http://") || href.starts_with("https://") {
return href.to_string();
}
if let Some(origin) = base_origin
&& href.starts_with('/')
{
return format!("{origin}{href}");
}
href.to_string()
}
pub fn extract_origin(url: &str) -> Option<String> {
let scheme_split = url.split_once("://")?;
let scheme = scheme_split.0;
let rest = scheme_split.1;
let host_end = rest.find('/').unwrap_or(rest.len());
if host_end == 0 {
return None;
}
let host = &rest[..host_end];
Some(format!("{scheme}://{host}"))
}
pub fn is_arch_package_url(url: &str) -> bool {
url.contains("://archlinux.org/packages/")
}