rule34/client/
post_list_query_builder.rs

1use crate::{
2    Client,
3    Error,
4    PostList,
5};
6use std::num::NonZeroU64;
7use url::Url;
8
9/// A builder for post list api queries
10#[derive(Debug, Copy, Clone)]
11pub struct PostListQueryBuilder<'a> {
12    /// The tags.
13    pub tags: Option<&'a str>,
14
15    /// The page #
16    ///
17    /// Starts at 0.
18    pub pid: Option<u64>,
19
20    /// The post id.
21    pub id: Option<NonZeroU64>,
22
23    /// The limit.
24    pub limit: Option<u16>,
25
26    /// The client ref.
27    client: &'a Client,
28}
29
30impl<'a> PostListQueryBuilder<'a> {
31    /// Make a new [`PostListQueryBuilder`].
32    pub fn new(client: &'a Client) -> Self {
33        Self {
34            tags: None,
35            pid: None,
36            id: None,
37            limit: None,
38
39            client,
40        }
41    }
42
43    /// Set the tags to list for.
44    ///
45    /// Querys are based on "tags".
46    /// Tags are seperated by spaces, while words are seperated by underscores.
47    /// Characters are automatically url-encoded.
48    pub fn tags(&mut self, tags: Option<&'a str>) -> &mut Self {
49        self.tags = tags;
50        self
51    }
52
53    /// Set the page number
54    pub fn pid(&mut self, pid: Option<u64>) -> &mut Self {
55        self.pid = pid;
56        self
57    }
58
59    /// Set the post id
60    pub fn id(&mut self, id: Option<NonZeroU64>) -> &mut Self {
61        self.id = id;
62        self
63    }
64
65    /// Set the post limit.
66    ///
67    /// This has a hard upper limit of `1000`.
68    pub fn limit(&mut self, limit: Option<u16>) -> &mut Self {
69        self.limit = limit;
70        self
71    }
72
73    /// Get the api url.
74    ///
75    /// # Errors
76    /// This fails if:
77    /// 1. The generated url is invalid
78    /// 2. `limit` is greater than `1000`
79    pub fn get_url(&self) -> Result<Url, Error> {
80        let mut pid_buffer = itoa::Buffer::new();
81        let mut id_buffer = itoa::Buffer::new();
82        let mut limit_buffer = itoa::Buffer::new();
83
84        let mut url = Url::parse_with_params(
85            crate::API_BASE_URL,
86            &[("page", "dapi"), ("s", "post"), ("q", "index")],
87        )?;
88
89        {
90            let mut query_pairs_mut = url.query_pairs_mut();
91
92            let auth = self.client.get_auth();
93            let auth = auth.as_ref().ok_or(Error::MissingAuth)?;
94            query_pairs_mut.append_pair("user_id", itoa::Buffer::new().format(auth.user_id));
95            query_pairs_mut.append_pair("api_key", &auth.api_key);
96
97            if let Some(tags) = self.tags {
98                query_pairs_mut.append_pair("tags", tags);
99            }
100
101            if let Some(pid) = self.pid {
102                query_pairs_mut.append_pair("pid", pid_buffer.format(pid));
103            }
104
105            if let Some(id) = self.id {
106                query_pairs_mut.append_pair("id", id_buffer.format(id.get()));
107            }
108
109            if let Some(limit) = self.limit {
110                if limit > crate::POST_LIST_LIMIT_MAX {
111                    return Err(Error::LimitTooLarge(limit));
112                }
113
114                query_pairs_mut.append_pair("limit", limit_buffer.format(limit));
115            }
116        }
117
118        Ok(url)
119    }
120
121    /// Execute the api query and get the results.
122    ///
123    /// # Returns
124    /// Returns an empty list if there are no results.
125    pub async fn execute(&self) -> Result<PostList, Error> {
126        let url = self.get_url()?;
127        self.client.get_xml(url.as_str()).await
128    }
129}