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)]
7pub struct UrlPreviewGenerator {
8    pub cache: Cache,
9    pub fetcher: Fetcher,
10    extractor: MetadataExtractor,
11}
12
13impl UrlPreviewGenerator {
14    pub fn new(cache_capacity: usize) -> Self {
15        Self {
16            cache: Cache::new(cache_capacity),
17            fetcher: Fetcher::new(),
18            extractor: MetadataExtractor::new(),
19        }
20    }
21
22    pub fn new_with_fetcher(cache_capacity: usize, fetcher: Fetcher) -> Self {
23        Self {
24            cache: Cache::new(cache_capacity),
25            fetcher,
26            extractor: MetadataExtractor::new(),
27        }
28    }
29}
30
31// For Twitter url and Normal url
32#[async_trait]
33impl PreviewGenerator for UrlPreviewGenerator {
34    async fn generate_preview(&self, url: &str) -> Result<Preview, PreviewError> {
35        // Check Cache
36        if let Some(cached) = self.cache.get(url).await {
37            return Ok(cached);
38        }
39
40        let _ = Url::parse(url)?;
41        let content = self.fetcher.fetch(url).await?;
42
43        let mut preview = match content {
44            FetchResult::OEmbed(oembed) => self
45                .extractor
46                .extract_from_oembed(&oembed.html)
47                .ok_or_else(|| {
48                    PreviewError::ExtractError("Failed to extract from oEmbed".into())
49                })?,
50            FetchResult::Html(html) => self.extractor.extract(&html, url)?,
51        };
52        preview.url = url.to_string();
53        self.cache.set(url.to_string(), preview.clone()).await;
54        Ok(preview)
55    }
56}