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 + 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, UnfurlError>>; } pub struct UnfurlEngine { resolvers: Vec>, } impl UnfurlEngine { pub fn new() -> Self { Self { resolvers: crate::resolvers::build_chain(), } } pub async fn unfurl(&self, raw: &str) -> Result, 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 = OnceLock::new(); pub async fn unfurl(raw: &str) -> Result, UnfurlError> { ENGINE.get_or_init(UnfurlEngine::new).unfurl(raw).await }