1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
use std::sync::Arc;

use synd_feed::{
    feed::{cache::FetchCachedFeed, parser::FetchFeedError},
    types::Feed,
};
use synd_o11y::metric;
use thiserror::Error;

use crate::{
    principal::Principal,
    repository::{self, SubscriptionRepository},
    usecase::{Input, Output},
};

use super::{authorize::Unauthorized, Usecase};

pub struct SubscribeFeed {
    pub repository: Arc<dyn SubscriptionRepository>,
    pub fetch_feed: Arc<dyn FetchCachedFeed>,
}

pub struct SubscribeFeedInput {
    pub url: String,
}

pub struct SubscribeFeedOutput {
    pub feed: Arc<Feed>,
}

#[derive(Error, Debug)]
pub enum SubscribeFeedError {
    #[error("fetch feed error: {0}")]
    FetchFeed(FetchFeedError),
}

impl Usecase for SubscribeFeed {
    type Input = SubscribeFeedInput;

    type Output = SubscribeFeedOutput;

    type Error = SubscribeFeedError;

    fn new(make: &super::MakeUsecase) -> Self {
        Self {
            repository: make.subscription_repo.clone(),
            fetch_feed: make.fetch_feed.clone(),
        }
    }

    async fn authorize(
        &self,
        principal: Principal,
        _: &SubscribeFeedInput,
    ) -> Result<Principal, Unauthorized> {
        Ok(principal)
    }

    async fn usecase(
        &self,
        Input {
            principal,
            input: SubscribeFeedInput { url },
            ..
        }: Input<Self::Input>,
    ) -> Result<Output<Self::Output>, super::Error<Self::Error>> {
        tracing::debug!("Subscribe feed: {url}");

        let feed = self
            .fetch_feed
            .fetch_feed(url.clone())
            .await
            .map_err(|err| super::Error::Usecase(SubscribeFeedError::FetchFeed(err)))?;

        tracing::debug!("{:?}", feed.meta());

        self.repository
            .put_feed_subscription(repository::types::FeedSubscription {
                user_id: principal.user_id().unwrap().to_owned(),
                url: feed.meta().url().to_owned(),
            })
            .await?;

        metric!(monotonic_counter.feed.subscription = 1);

        Ok(Output {
            output: SubscribeFeedOutput { feed },
        })
    }
}