url_preview/
preview_generator.rs

1use 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// For Twitter url and Normal url
47#[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}