use std::{borrow::Cow, fmt::Display};
use aho_corasick::AhoCorasick;
use url::Url;
use crate::db::RefreshError;
#[derive(Debug)]
pub(crate) struct DatabaseFilenameReplacer {
ac: AhoCorasick,
}
impl DatabaseFilenameReplacer {
const PATTERNS: &'static [&'static str] = &["_", "/", "+", "%3a", "%3A", "@"];
const REPLACE_WITH: &'static [&'static str] = &["%5f", "_", "%252b", ":", ":", "%40"];
pub fn new() -> Result<Self, RefreshError> {
Ok(Self {
ac: AhoCorasick::new(Self::PATTERNS)?,
})
}
pub fn replace(&self, url: &str) -> Result<String, RefreshError> {
let url_parsed = Url::parse(url).map_err(|_| RefreshError::InvalidUrl(url.to_string()))?;
let host = url_parsed.host_str();
let path = if let Some(host) = host {
url.split_once(host)
.ok_or_else(|| RefreshError::InvalidUrl(url.to_string()))?
.1
} else {
url.strip_prefix("file://")
.or_else(|| url.strip_prefix("file:"))
.ok_or_else(|| RefreshError::InvalidUrl(url.to_string()))?
};
let url = if let Some(host) = host {
Cow::Owned(format!("{host}{path}"))
} else {
Cow::Borrowed(path)
};
let mut wtr = vec![];
self.ac
.try_stream_replace_all(url.as_bytes(), &mut wtr, Self::REPLACE_WITH)
.map_err(RefreshError::ReplaceAll)?;
Ok(String::from_utf8_lossy(&wtr).to_string())
}
}
#[inline]
pub(crate) fn concat_url_only_check_once_slash(url: &str, path: impl Display) -> String {
if url.ends_with('/') {
format!("{url}{path}")
} else {
format!("{url}/{path}")
}
}