main
use std::future::Future;
use std::pin::Pin;
use std::sync::OnceLock;
use url::Url;
use crate::UnfurlError;
use crate::types::MessageEmbed;
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
#[derive(Debug, Clone)]
pub struct UnfurlOptions {
pub max_bytes: usize,
}
impl Default for UnfurlOptions {
fn default() -> Self {
Self {
max_bytes: 1_048_576,
}
}
}
pub struct ResolveContext<'a> {
pub url: Url,
pub original_url: Url,
pub options: &'a UnfurlOptions,
}
pub trait Resolver: Send + Sync {
fn matches(&self, url: &Url) -> bool;
fn resolve<'a>(
&'a self,
context: &'a ResolveContext<'_>,
) -> BoxFuture<'a, Result<Vec<MessageEmbed>, UnfurlError>>;
}
pub struct UnfurlEngine {
resolvers: Vec<Box<dyn Resolver>>,
}
impl UnfurlEngine {
pub fn new() -> Self {
Self {
resolvers: crate::resolvers::build_chain(),
}
}
pub async fn unfurl(&self, raw: &str) -> Result<Vec<MessageEmbed>, UnfurlError> {
let url = crate::net::parse(raw)?;
crate::net::validate_static(&url)?;
let options = UnfurlOptions::default();
let context = ResolveContext {
url: url.clone(),
original_url: url,
options: &options,
};
for resolver in &self.resolvers {
if resolver.matches(&context.url) {
return resolver.resolve(&context).await;
}
}
Ok(Vec::new())
}
}
impl Default for UnfurlEngine {
fn default() -> Self {
Self::new()
}
}
static ENGINE: OnceLock<UnfurlEngine> = OnceLock::new();
pub async fn unfurl(raw: &str) -> Result<Vec<MessageEmbed>, UnfurlError> {
ENGINE.get_or_init(UnfurlEngine::new).unfurl(raw).await
}