url_preview/
preview_generator.rs1use crate::fetcher::FetchResult;
2use crate::{Cache, Fetcher, MetadataExtractor, Preview, PreviewError, PreviewGenerator};
3use async_trait::async_trait;
4use url::Url;
5
6#[derive(Clone, Default)]
7pub enum CacheStrategy {
8 #[default]
9 UseCache,
10 NoCache,
11 ForceUpdate,
12}
13
14#[derive(Clone)]
15pub struct UrlPreviewGenerator {
16 pub cache: Cache,
17 pub cache_strategy: CacheStrategy,
18 pub fetcher: Fetcher,
19 extractor: MetadataExtractor,
20}
21
22impl UrlPreviewGenerator {
23 pub fn new(cache_capacity: usize, cache_strategy: CacheStrategy) -> Self {
24 Self {
25 cache: Cache::new(cache_capacity),
26 cache_strategy,
27 fetcher: Fetcher::new(),
28 extractor: MetadataExtractor::new(),
29 }
30 }
31
32 pub fn new_with_fetcher(
33 cache_capacity: usize,
34 cache_strategy: CacheStrategy,
35 fetcher: Fetcher,
36 ) -> Self {
37 Self {
38 cache: Cache::new(cache_capacity),
39 cache_strategy,
40 fetcher,
41 extractor: MetadataExtractor::new(),
42 }
43 }
44}
45
46#[async_trait]
48impl PreviewGenerator for UrlPreviewGenerator {
49 async fn generate_preview(&self, url: &str) -> Result<Preview, PreviewError> {
50 if let CacheStrategy::UseCache = self.cache_strategy {
51 if let Some(cached) = self.cache.get(url).await {
52 return Ok(cached);
53 };
54 };
55
56 let _ = Url::parse(url)?;
57 let content = self.fetcher.fetch(url).await?;
58
59 let mut preview = match content {
60 FetchResult::OEmbed(oembed) => self
61 .extractor
62 .extract_from_oembed(&oembed.html)
63 .ok_or_else(|| {
64 PreviewError::ExtractError("Failed to extract from oEmbed".into())
65 })?,
66 FetchResult::Html(html) => self.extractor.extract(&html, url)?,
67 };
68 preview.url = url.to_string();
69 if let CacheStrategy::UseCache = self.cache_strategy {
70 self.cache.set(url.to_string(), preview.clone()).await;
71 };
72 Ok(preview)
73 }
74}