Skip to main content

snoo/
endpoints.rs

1use reqwest::Url;
2use std::borrow::Cow;
3
4use std::io;
5
6macro_rules! endpoints {
7    ($($name:ident => $ep:tt),*) => {
8        $(pub const $name : EndpointBuilder = EndpointBuilder(Cow::Borrowed($ep));)*
9    };
10}
11
12macro_rules! uri_segments {
13    ($($name:ident),*) => {
14        $(
15        pub fn $name(&self, $name: &str) -> EndpointBuilder {
16            self.replace(concat!("#", stringify!($name)), $name)
17        }
18        )*
19    };
20}
21
22#[derive(Copy, Clone)]
23pub enum SearchSort {
24    Relevance,
25    Hot,
26    Top,
27    New,
28    Comments,
29}
30
31impl SearchSort {
32    pub fn to_str(&self) -> &'static str {
33        match self {
34            SearchSort::Relevance => "relevance",
35            SearchSort::Hot => "hot",
36            SearchSort::Top => "top",
37            SearchSort::New => "new",
38            SearchSort::Comments => "comments",
39        }
40    }
41}
42
43pub enum EndpointBase {
44    Regular,
45    OAuth,
46}
47impl EndpointBase {
48    pub fn get_str(&self) -> &str {
49        match self {
50            EndpointBase::Regular => "https://reddit.com",
51            EndpointBase::OAuth => "https://oauth.reddit.com",
52        }
53    }
54}
55
56/// Build an endpoint without the base attached.
57/// E.g. /r/rust/top
58pub struct EndpointBuilder(Cow<'static, str>);
59
60impl EndpointBuilder {
61    pub fn new(ep: &str) -> Self {
62        EndpointBuilder(Cow::Owned(ep.to_owned()))
63    }
64
65    fn replace(&self, needle: &str, haystack: &str) -> EndpointBuilder {
66        EndpointBuilder(self.0.as_ref().replace(needle, haystack).into())
67    }
68
69    uri_segments! {
70        subreddit,
71        id,
72        page,
73        user
74    }
75
76    pub fn regular(self) -> io::Result<Endpoint> {
77        Endpoint::new(EndpointBase::Regular, self)
78    }
79}
80
81#[derive(Clone)]
82pub struct Endpoint(Url);
83impl Endpoint {
84    pub fn new(base: EndpointBase, ep: EndpointBuilder) -> io::Result<Endpoint> {
85        let ep_url = Url::parse(base.get_str())
86            .unwrap()
87            .join(ep.0.as_ref())
88            .map_err(|_| {
89                io::Error::new(
90                    io::ErrorKind::InvalidInput,
91                    format!("Invalid url: {}", ep.0),
92                )
93            })?
94            .join(".json")
95            .unwrap();
96        Ok(Endpoint(ep_url))
97    }
98
99    pub fn build(ep_str: &str) -> EndpointBuilder {
100        EndpointBuilder::new(ep_str)
101    }
102
103    pub fn to_url(&self) -> Url {
104        self.0.clone()
105    }
106
107    pub fn filter(
108        mut self,
109        q: Option<&str>,
110        sort: SearchSort,
111        before: Option<&str>,
112        after: Option<&str>,
113    ) -> Endpoint {
114        {
115            let mut query = self.0.query_pairs_mut();
116            query.append_pair("restrict_sr", "on");
117            if let Some(search_string) = q {
118                query.append_pair("q", search_string);
119            }
120            query.append_pair("sort", sort.to_str());
121
122            if let Some(afer_thing) = after {
123                query.append_pair("after", afer_thing.as_ref());
124            }
125
126            if let Some(before_thing) = before {
127                query.append_pair("before", &before_thing.as_ref());
128            }
129        }
130        self
131    }
132}
133
134impl AsRef<str> for Endpoint {
135    fn as_ref(&self) -> &str {
136        self.0.as_ref()
137    }
138}
139
140endpoints! {
141    ABOUT_EDITED =>            "r/#subreddit/about/edited/",
142    ABOUT_LOG =>               "r/#subreddit/about/log/",
143    ABOUT_MODQUEUE =>          "r/#subreddit/about/modqueue/",
144    ABOUT_REPORTS =>           "r/#subreddit/about/reports/",
145    ABOUT_SPAM =>              "r/#subreddit/about/spam/",
146    ABOUT_STICKY =>            "r/#subreddit/about/sticky/",
147    ABOUT_STYLESHEET =>        "r/#subreddit/about/stylesheet/",
148    ABOUT_TRAFFIC =>           "r/#subreddit/about/traffic/",
149    ABOUT_UNMODERATED =>       "r/#subreddit/about/unmoderated/",
150    ACCEPT_MOD_INVITE =>       "r/#subreddit/api/accept_moderator_invite/",
151    ADD_SUBREDDIT_RULE =>      "api/add_subreddit_rule/",
152    APPROVE =>                 "api/approve/",
153    BLOCK =>                   "api/block/",
154    BLOCK_USER =>              "/api/block_user/",
155    BLOCKED =>                 "prefs/blocked/",
156    COLLAPSE =>                "api/collapse_message/",
157    COLLECTION =>              "api/v1/collections/collection/",
158    COLLECTION_ADD_POST =>     "api/v1/collections/add_post_to_collection/",
159    COLLECTION_CREATE =>       "api/v1/collections/create_collection/",
160    COLLECTION_DELETE =>       "api/v1/collections/delete_collection/",
161    COLLECTION_DESC =>         "api/v1/collections/update_collection_description/",
162    COLLECTION_FOLLOW =>       "api/v1/collections/follow_collection/",
163    COLLECTION_REMOVE_POST =>  "api/v1/collections/remove_post_in_collection/",
164    COLLECTION_REORDER =>      "api/v1/collections/reorder_collection/",
165    COLLECTION_SUBREDDIT =>    "api/v1/collections/subreddit_collections/",
166    COLLECTION_TITLE =>        "api/v1/collections/update_collection_title/",
167    COMMENT =>                 "api/comment/",
168    COMMENT_REPLIES =>         "message/comments/",
169    COMPOSE =>                 "api/compose/",
170    CONTEST_MODE =>            "api/set_contest_mode/",
171    DEL =>                     "api/del/",
172    DELETE_MESSAGE =>          "api/del_msg/",
173    DELETE_SR_BANNER =>        "r/#subreddit/api/delete_sr_banner/",
174    DELETE_SR_HEADER =>        "r/#subreddit/api/delete_sr_header/",
175    DELETE_SR_ICON =>          "r/#subreddit/api/delete_sr_icon/",
176    DELETE_SR_IMAGE =>         "r/#subreddit/api/delete_sr_img/",
177    DELETEFLAIR =>             "r/#subreddit/api/deleteflair/",
178    DISTINGUISH =>             "api/distinguish/",
179    DOMAIN =>                  "domain/#domain/",
180    DUPLICATES =>              "duplicates/{submission_id}/",
181    EDIT =>                    "api/editusertext/",
182    EMOJI_DELETE =>            "api/v1/#subreddit/emoji/{emoji_name}",
183    EMOJI_LEASE =>             "api/v1/#subreddit/emoji_asset_upload_s3.json/",
184    EMOJI_LIST =>              "api/v1/#subreddit/emojis/all/",
185    EMOJI_UPDATE =>            "api/v1/#subreddit/emoji_permissions/",
186    EMOJI_UPLOAD =>            "api/v1/#subreddit/emoji.json/",
187    FLAIR =>                   "r/#subreddit/api/flair/",
188    FLAIRCONFIG =>             "r/#subreddit/api/flairconfig/",
189    FLAIRCSV =>                "r/#subreddit/api/flaircsv/",
190    FLAIRLIST =>               "r/#subreddit/api/flairlist/",
191    FLAIRSELECTOR =>           "r/#subreddit/api/flairselector/",
192    FLAIRTEMPLATE_V2 =>        "r/#subreddit/api/flairtemplate_v2",
193    FLAIRTEMPLATECLEAR =>      "r/#subreddit/api/clearflairtemplates/",
194    FLAIRTEMPLATEDELETE =>     "r/#subreddit/api/deleteflairtemplate/",
195    FRIEND =>                  "r/#subreddit/api/friend/",
196    FRIEND_V1 =>               "api/v1/me/friends/#user/",
197    FRIENDS =>                 "api/v1/me/friends/",
198    GILD_THING =>              "api/v1/gold/gild/#fullname/",
199    GILD_USER =>               "api/v1/gold/give/#username/",
200    HIDE =>                    "api/hide/",
201    IGNORE_REPORTS =>          "api/ignore_reports/",
202    INBOX =>                   "message/inbox/",
203    INFO =>                    "api/info/",
204    KARMA =>                   "api/v1/me/karma/",
205    LEAVECONTRIBUTOR =>        "api/leavecontributor/",
206    LINK_FLAIR =>              "r/#subreddit/api/link_flair_v2",
207    LIST_BANNED =>             "r/#subreddit/about/banned/",
208    LIST_CONTRIBUTOR =>        "r/#subreddit/about/contributors/",
209    LIST_MODERATOR =>          "r/#subreddit/about/moderators/",
210    LIST_MUTED =>              "r/#subreddit/about/muted/",
211    LIST_WIKIBANNED =>         "r/#subreddit/about/wikibanned/",
212    LIST_WIKICONTRIBUTOR =>    "r/#subreddit/about/wikicontributors/",
213    LIVE_ACCEPT_INVITE =>      "api/live/#id/accept_contributor_invite/",
214    LIVE_ADD_UPDATE =>         "api/live/#id/update/",
215    LIVE_CLOSE =>              "api/live/#id/close_thread/",
216    LIVE_CONTRIBUTORS =>       "live/#id/contributors/",
217    LIVE_DISCUSSIONS =>        "live/#id/discussions/",
218    LIVE_FOCUS =>              "live/{thread_id}/updates/{update_id}",
219    LIVE_INFO =>               "api/live/by_id/#ids/",
220    LIVE_INVITE =>             "api/live/#id/invite_contributor/",
221    LIVE_LEAVE =>              "api/live/#id/leave_contributor/",
222    LIVE_NOW =>                "api/live/happening_now/",
223    LIVE_REMOVE_CONTRIB =>     "api/live/#id/rm_contributor/",
224    LIVE_REMOVE_INVITE =>      "api/live/#id/rm_contributor_invite/",
225    LIVE_REMOVE_UPDATE =>      "api/live/#id/delete_update/",
226    LIVE_REPORT =>             "api/live/#id/report/",
227    LIVE_STRIKE =>             "api/live/#id/strike_update/",
228    LIVE_UPDATE_PERMS =>       "api/live/#id/set_contributor_permissions/",
229    LIVE_UPDATE_THREAD =>      "api/live/#id/edit/",
230    LIVE_UPDATES =>            "live/#id/",
231    LIVEABOUT =>               "api/live/#id/about/",
232    LIVECREATE =>              "api/live/create/",
233    LOCK =>                    "api/lock/",
234    MARKNSFW =>                "api/marknsfw/",
235    ME =>                      "api/v1/me/",
236    MEDIA_ASSET =>             "api/media/asset.json/",
237    MENTIONS =>                "message/mentions/",
238    MESSAGE =>                 "message/messages/#id/",
239    MESSAGES =>                "message/messages/",
240    MODERATED =>               "user/#user/moderated_subreddits/",
241    MODERATOR_MESSAGES =>      "r/#subreddit/message/moderator/",
242    MODERATOR_UNREAD =>        "r/#subreddit/message/moderator/unread/",
243    MODMAIL_ARCHIVE =>         "api/mod/conversations/#id/archive/",
244    MODMAIL_BULK_READ =>       "api/mod/conversations/bulk/read/",
245    MODMAIL_CONVERSATION =>    "api/mod/conversations/#id/",
246    MODMAIL_CONVERSATIONS =>   "api/mod/conversations/",
247    MODMAIL_HIGHLIGHT =>       "api/mod/conversations/#id/highlight/",
248    MODMAIL_MUTE =>            "api/mod/conversations/#id/mute/",
249    MODMAIL_READ =>            "api/mod/conversations/read/",
250    MODMAIL_SUBREDDITS =>      "api/mod/conversations/subreddits/",
251    MODMAIL_UNARCHIVE =>       "api/mod/conversations/#id/unarchive/",
252    MODMAIL_UNMUTE =>          "api/mod/conversations/#id/unmute/",
253    MODMAIL_UNREAD =>          "api/mod/conversations/unread/",
254    MODMAIL_UNREAD_COUNT =>    "api/mod/conversations/unread/count/",
255    MORECHILDREN =>            "api/morechildren/",
256    MULTIREDDIT =>             "user/#user/m/#multi/",
257    MULTIREDDIT_API =>         "api/multi/user/#user/m/#multi/",
258    MULTIREDDIT_BASE =>        "api/multi/",
259    MULTIREDDIT_COPY =>        "api/multi/copy/",
260    MULTIREDDIT_RENAME =>      "api/multi/rename/",
261    MULTIREDDIT_UPDATE =>      "api/multi/user/#user/m/#multi/r/#subreddit/",
262    MULTIREDDIT_USER =>        "api/multi/user/#user/",
263    MUTE_SENDER =>             "api/mute_message_author/",
264    MY_CONTRIBUTOR =>          "subreddits/mine/contributor/",
265    MY_MODERATOR =>            "subreddits/mine/moderator/",
266    MY_MULTIREDDITS =>         "api/multi/mine/",
267    MY_SUBREDDITS =>           "subreddits/mine/subscriber/",
268    POST_REQUIREMENTS =>       "api/v1/#subreddit/post_requirements/",
269    PREFERENCES =>             "api/v1/me/prefs/",
270    QUARANTINE_OPT_IN =>       "api/quarantine_optin/",
271    QUARANTINE_OPT_OUT =>      "api/quarantine_optout/",
272    READ_MESSAGE =>            "api/read_message/",
273    REMOVAL_COMMENT_MESSAGE => "api/v1/modactions/removal_comment_message/",
274    REMOVAL_LINK_MESSAGE =>    "api/v1/modactions/removal_link_message/",
275    REMOVAL_REASONS =>         "api/v1/modactions/removal_reasons/",
276    REMOVAL_REASON =>          "api/v1/#subreddit/removal_reasons/#id/",
277    REMOVAL_REASONS_LIST =>    "api/v1/#subreddit/removal_reasons/",
278    REMOVE_SUBREDDIT_RULE =>   "api/remove_subreddit_rule/",
279    REMOVE =>                  "api/remove/",
280    REORDER_SUBREDDIT_RULES => "api/reorder_subreddit_rules/",
281    REPORT =>                  "api/report/",
282    RULES =>                   "r/#subreddit/about/rules/",
283    SAVE =>                    "api/save/",
284    SEARCH =>                  "search/",
285    SELECT_FLAIR =>            "r/#subreddit/api/selectflair/",
286    SENDREPLIES =>             "api/sendreplies/",
287    SENT =>                    "message/sent/",
288    SET_ORIGINAL_CONTENT =>    "api/set_original_content/",
289    SETPERMISSIONS =>          "r/#subreddit/api/setpermissions/",
290    SHOW_COMMENT =>            "api/show_comment/",
291    SITE_ADMIN =>              "api/site_admin/",
292    SPOILER =>                 "api/spoiler/",
293    STICKY_SUBMISSION =>       "api/set_subreddit_sticky/",
294    STORE_VISITS =>            "api/store_visits/",
295    STRUCTURED_STYLES =>       "api/v1/structured_styles/#subreddit/",
296    STYLE_ASSET_LEASE =>       "api/v1/style_asset_upload_s3/#subreddit/",
297    SUB_RECOMMENDED =>         "api/recommend/sr/#subreddits/",
298    SUBMISSION =>              "comments/#id/",
299    SUBMISSION_REPLIES =>      "message/selfreply/",
300    SUBMIT =>                  "api/submit/",
301    SUBMIT_POLL_POST =>        "api/submit_poll_post/",
302    SUBREDDIT =>               "r/#subreddit/",
303    SUBREDDIT_TOP =>           "r/#subreddit/top/",
304    SUBREDDIT_RISING =>        "r/#subreddit/rising/",
305    SUBREDDIT_NEW =>           "r/#subreddit/new/",
306    SUBREDDIT_HOT =>           "r/#subreddit/hot/",
307    SUBREDDIT_ABOUT =>         "r/#subreddit/about/",
308    SUBREDDIT_FILTER =>        "api/filter/user/#user/f/#special/r/#subreddit/",
309    SUBREDDIT_FILTER_LIST =>   "api/filter/user/#user/f/#special/",
310    SUBREDDIT_RANDOM =>        "r/#subreddit/random/",
311    SUBREDDIT_SETTINGS =>      "r/#subreddit/about/edit/",
312    SUBREDDIT_STYLESHEET =>    "r/#subreddit/api/subreddit_stylesheet/",
313    SUBREDDITS_BY_TOPIC =>     "api/subreddits_by_topic/",
314    SUBREDDITS_DEFAULT =>      "subreddits/default/",
315    SUBREDDITS_GOLD =>         "subreddits/gold/",
316    SUBREDDITS_NAME_SEARCH =>  "api/search_reddit_names/",
317    SUBREDDITS_NEW =>          "subreddits/new/",
318    SUBREDDITS_POPULAR =>      "subreddits/popular/",
319    SUBREDDIT_SEARCH =>         "r/#subreddit/search/",
320    SUBREDDITS_SEARCH =>       "subreddits/search/",
321    SUBSCRIBE =>               "api/subscribe/",
322    SUGGESTED_SORT =>          "api/set_suggested_sort/",
323    TROPHIES =>                "api/v1/user/#user/trophies/",
324    UNCOLLAPSE =>              "api/uncollapse_message/",
325    UNFRIEND =>                "r/#subreddit/api/unfriend/",
326    UNHIDE =>                  "api/unhide/",
327    UNIGNORE_REPORTS =>        "api/unignore_reports/",
328    UNLOCK =>                  "api/unlock/",
329    UNMARKNSFW =>              "api/unmarknsfw/",
330    UNMUTE_SENDER =>           "api/unmute_message_author/",
331    UNREAD =>                  "message/unread/",
332    UNREAD_MESSAGE =>          "api/unread_message/",
333    UNSAVE =>                  "api/unsave/",
334    UNSPOILER =>               "api/unspoiler/",
335    UPDATE_SETTINGS =>         "api/v1/subreddit/update_settings/",
336    UPDATE_SUBREDDIT_RULE =>   "api/update_subreddit_rule/",
337    UPLOAD_IMAGE =>            "r/#subreddit/api/upload_sr_img/",
338    USER =>                    "user/#user/",
339    USER_ABOUT =>              "user/#user/about/",
340    USER_SUBMITTED =>          "user/#user/submitted/",
341    USER_COMMENTS =>           "user/#user/comments/",
342    USER_BY_FULLNAME =>        "/api/user_data_by_account_ids/",
343    USER_FLAIR =>              "r/#subreddit/api/user_flair_v2",
344    USERS_NEW =>               "users/new/",
345    USERS_POPULAR =>           "users/popular/",
346    USERS_SEARCH =>            "users/search/",
347    VOTE =>                    "api/vote/",
348    WIDGET_CREATE =>           "r/#subreddit/api/widget/",
349    WIDGET_LEASE =>            "r/#subreddit/api/widget_image_upload_s3",
350    WIDGET_MODIFY =>           "r/#subreddit/api/widget/{widget_id}",
351    WIDGET_ORDER =>            "r/#subreddit/api/widget_order/#section/",
352    WIDGETS =>                 "r/#subreddit/api/widgets/",
353    WIKI_EDIT =>               "r/#subreddit/api/wiki/edit/",
354    WIKI_PAGE =>               "r/#subreddit/wiki/#page/",
355    WIKI_PAGE_EDITOR =>        "r/#subreddit/api/wiki/alloweditor/#method/",
356    WIKI_PAGE_REVISIONS =>     "r/#subreddit/wiki/revisions/#page/",
357    WIKI_PAGE_SETTINGS =>      "r/#subreddit/wiki/settings/#page/",
358    WIKI_PAGES =>              "r/#subreddit/wiki/pages/",
359    WIKI_REVISIONS =>          "r/#subreddit/wiki/revisions/"
360}