1use std::collections::HashMap;
3
4use serde::Deserialize;
5use url::form_urlencoded;
6
7use crate::users::User;
8use crate::Future;
9use crate::Github;
10
11pub struct Notifications {
15 github: Github,
16}
17
18impl Notifications {
19 #[doc(hidden)]
20 pub fn new(github: Github) -> Self {
21 Self { github }
22 }
23
24 pub fn list(&self, options: &ThreadListOptions) -> Future<Vec<Thread>> {
29 let mut uri = vec!["/notifications".into()];
30 if let Some(query) = options.serialize() {
31 uri.push(query);
32 }
33 self.github.get(&uri.join("?"))
34 }
35
36 pub fn list_for_repo<O, R>(
41 &self,
42 owner: O,
43 repo: R,
44 options: &ThreadListOptions,
45 ) -> Future<Vec<Thread>>
46 where
47 O: Into<String>,
48 R: Into<String>,
49 {
50 let mut uri = vec![format!(
51 "/repos/{}/{}/notifications",
52 owner.into(),
53 repo.into()
54 )];
55 if let Some(query) = options.serialize() {
56 uri.push(query);
57 }
58 self.github.get(&uri.join("?"))
59 }
60
61 pub fn mark_as_read<S>(&self, last_read_at: S) -> Future<()>
66 where
67 S: Into<Option<String>>,
68 {
69 let url = match last_read_at.into() {
70 Some(last_read_at) => format!(
71 "/notifications?{}",
72 form_urlencoded::Serializer::new(String::new())
73 .append_pair("last_read_at", &last_read_at)
74 .finish()
75 ),
76 None => String::from("/notifications"),
77 };
78 self.github.put_no_response(&url, Vec::new())
79 }
80
81 pub fn mark_as_read_for_repo<O, R, S>(&self, owner: O, repo: R, last_read_at: S) -> Future<()>
86 where
87 O: Into<String>,
88 R: Into<String>,
89 S: Into<Option<String>>,
90 {
91 let path = match last_read_at.into() {
92 Some(last_read_at) => format!(
93 "/notifications?{}",
94 form_urlencoded::Serializer::new(String::new())
95 .append_pair("last_read_at", &last_read_at)
96 .finish()
97 ),
98 None => String::from("/notifications"),
99 };
100 self.github.put_no_response(
101 &format!("/repos/{}/{}{}", owner.into(), repo.into(), path),
102 Vec::new(),
103 )
104 }
105
106 pub fn get_thread<S>(&self, id: S) -> Future<Thread>
111 where
112 S: Into<String>,
113 {
114 self.github
115 .get(&format!("/notifications/threads/{}", id.into()))
116 }
117
118 pub fn mark_thread_as_read<S>(&self, id: S) -> Future<()>
123 where
124 S: Into<String>,
125 {
126 self.github
127 .patch_no_response(&format!("/notifications/threads/{}", id.into()), Vec::new())
128 }
129
130 pub fn get_subscription<S>(&self, id: S) -> Future<Subscription>
135 where
136 S: Into<String>,
137 {
138 self.github.get(&format!(
139 "/notifications/threads/{}/subscription",
140 id.into(),
141 ))
142 }
143
144 pub fn subscribe<S>(&self, id: S) -> Future<Subscription>
149 where
150 S: Into<String>,
151 {
152 self.github.put(
153 &format!("/notifications/threads/{}/subscription", id.into()),
154 json_lit!({ "subscribed": true }),
155 )
156 }
157
158 pub fn unsubscribe<S>(&self, id: S) -> Future<Subscription>
163 where
164 S: Into<String>,
165 {
166 self.github.put(
167 &format!("/notifications/threads/{}/subscription", id.into()),
168 json_lit!({ "ignored": true }),
169 )
170 }
171
172 pub fn delete_subscription<S>(&self, id: S) -> Future<()>
177 where
178 S: Into<String>,
179 {
180 self.github.delete(&format!(
181 "/notifications/threads/{}/subscription",
182 id.into()
183 ))
184 }
185}
186
187#[derive(Debug, Deserialize)]
190pub struct Thread {
191 pub id: String,
192 pub unread: bool,
193 pub updated_at: String,
194 pub last_read_at: Option<String>,
195 pub reason: String,
196 pub subject: Subject,
197 pub repository: Repository,
198 pub url: String,
199 pub subscription_url: String,
200}
201
202#[derive(Default)]
203pub struct ThreadListOptions {
204 params: HashMap<&'static str, String>,
205}
206
207impl ThreadListOptions {
208 pub fn builder() -> ThreadListOptionsBuilder {
209 ThreadListOptionsBuilder::default()
210 }
211
212 pub fn serialize(&self) -> Option<String> {
214 if self.params.is_empty() {
215 None
216 } else {
217 let encoded: String = form_urlencoded::Serializer::new(String::new())
218 .extend_pairs(&self.params)
219 .finish();
220 Some(encoded)
221 }
222 }
223}
224
225#[derive(Default)]
226pub struct ThreadListOptionsBuilder(ThreadListOptions);
227
228impl ThreadListOptionsBuilder {
229 pub fn all(&mut self, all: bool) -> &mut Self {
231 self.0.params.insert("all", all.to_string());
232 self
233 }
234
235 pub fn participating(&mut self, val: bool) -> &mut Self {
238 self.0.params.insert("participating", val.to_string());
239 self
240 }
241
242 pub fn since<T>(&mut self, since: T) -> &mut Self
244 where
245 T: Into<String>,
246 {
247 self.0.params.insert("since", since.into());
248 self
249 }
250
251 pub fn before<T>(&mut self, before: T) -> &mut Self
253 where
254 T: Into<String>,
255 {
256 self.0.params.insert("before", before.into());
257 self
258 }
259
260 pub fn build(&self) -> ThreadListOptions {
261 ThreadListOptions {
262 params: self.0.params.clone(),
263 }
264 }
265}
266
267#[derive(Debug, Deserialize)]
268pub struct Subject {
269 title: String,
270 url: String,
271 latest_comment_url: String,
272 #[serde(rename = "type")]
273 kind: String,
274}
275
276#[derive(Debug, Deserialize)]
277pub struct Repository {
278 pub id: u32,
279 pub node_id: String,
280 pub name: String,
281 pub full_name: String,
282 pub owner: User,
283 pub html_url: String,
284}
285
286#[derive(Debug, Deserialize)]
287pub struct Subscription {
288 pub subscribed: bool,
289 pub ignored: bool,
290 pub reason: String,
291 pub created_at: String,
292 pub url: String,
293 pub thread_url: String,
294}