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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use async_graphql::{
    connection::{Connection, Edge},
    Context, Object, Result,
};

use crate::{
    gql::{
        object::{self, id, Entry},
        run_usecase,
    },
    usecase::{
        FetchEntries, FetchEntriesError, FetchEntriesInput, FetchEntriesOutput,
        FetchSubscribedFeeds, FetchSubscribedFeedsError, FetchSubscribedFeedsInput,
        FetchSubscribedFeedsOutput, Output,
    },
};

struct Subscription;

#[Object]
impl Subscription {
    /// Return Subscribed feeds
    async fn feeds(
        &self,
        cx: &Context<'_>,
        after: Option<String>,
        #[graphql(default = 20)] first: Option<i32>,
    ) -> Result<Connection<String, object::Feed>> {
        #[allow(clippy::cast_sign_loss)]
        let first = first.unwrap_or(10).min(100) as usize;
        let has_prev = after.is_some();
        let input = FetchSubscribedFeedsInput {
            after,
            first: first + 1,
        };
        let Output {
            output: FetchSubscribedFeedsOutput { feeds },
        } = run_usecase!(
            FetchSubscribedFeeds,
            cx,
            input,
            |err: FetchSubscribedFeedsError| Err(async_graphql::ErrorExtensions::extend(&err))
        )?;

        let has_next = feeds.len() > first;
        let mut connection = Connection::new(has_prev, has_next);

        let edges = feeds
            .into_iter()
            .take(first)
            .map(|feed| (feed.meta().url().to_owned(), feed))
            .map(|(cursor, feed)| (cursor, object::Feed::from(feed)))
            .map(|(cursor, feed)| Edge::new(cursor, feed));

        connection.edges.extend(edges);

        Ok(connection)
    }

    /// Return subscribed latest entries order by published time.
    async fn entries<'cx>(
        &self,
        cx: &Context<'_>,
        after: Option<String>,
        #[graphql(default = 20)] first: Option<i32>,
    ) -> Result<Connection<id::EntryId, Entry<'cx>>> {
        #[allow(clippy::cast_sign_loss)]
        let first = first.unwrap_or(20).min(200) as usize;
        let has_prev = after.is_some();
        let input = FetchEntriesInput {
            after: after.map(Into::into),
            first: first + 1,
        };
        let Output {
            output: FetchEntriesOutput { entries, feeds },
        } = run_usecase!(FetchEntries, cx, input, |err: FetchEntriesError| Err(
            async_graphql::ErrorExtensions::extend(&err)
        ))?;

        let has_next = entries.len() > first;
        let mut connection = Connection::new(has_prev, has_next);

        let edges = entries
            .into_iter()
            .take(first)
            .map(move |(entry, feed_url)| {
                let meta = feeds
                    .get(&feed_url)
                    .expect("FeedMeta not found. this is a bug")
                    .clone();
                let cursor = entry.id().into();
                let node = Entry::new(meta, entry);
                Edge::new(cursor, node)
            });

        connection.edges.extend(edges);

        Ok(connection)
    }
}

pub struct Query;

#[Object]
impl Query {
    async fn subscription(&self) -> Subscription {
        Subscription {}
    }
}