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
56pub 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}