diff --git a/src/lib.rs b/src/lib.rs index 81d7bca..e472a4f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,12 +9,14 @@ pub struct Forgejo { } mod misc; +mod notification; mod organization; mod issue; mod repository; mod user; pub use misc::*; +pub use notification::*; pub use organization::*; pub use issue::*; pub use repository::*; @@ -150,6 +152,12 @@ impl Forgejo { self.execute(request).await } + async fn put(&self, path: &str) -> Result { + let url = self.url.join("api/v1/").unwrap().join(path).unwrap(); + let request = self.client.put(url).build()?; + self.execute(request).await + } + async fn execute(&self, request: Request) -> Result { let response = self.client.execute(request).await?; match response.status() { diff --git a/src/notification.rs b/src/notification.rs new file mode 100644 index 0000000..9dbff21 --- /dev/null +++ b/src/notification.rs @@ -0,0 +1,221 @@ +use super::*; + +impl Forgejo { + pub async fn notifications(&self, query: NotificationQuery) -> Result, ForgejoError> { + self.get(&format!("notifications?{}", query.query_string())).await + } + + pub async fn set_notifications_state(&self, query: NotificationPutQuery) -> Result, ForgejoError> { + self.put(&format!("notifications?{}", query.query_string())).await + } + + pub async fn notification_count(&self) -> Result, ForgejoError> { + self.get("notifications/new").await + } + + pub async fn get_notification(&self, id: u64) -> Result, ForgejoError> { + self.get_opt(&format!("notifications/threads/{id}")).await + } + + pub async fn set_notification_state(&self, id: u64, to_status: ToStatus) -> Result, ForgejoError> { + self.patch(&format!("notifications/threads/{id}?to-status={}", to_status.as_str()), &()).await + } + + pub async fn get_repo_notifications(&self, owner: &str, name: &str, query: NotificationQuery) -> Result, ForgejoError> { + self.get(&format!("repos/{owner}/{name}/notifications?{}", query.query_string())).await + } + + pub async fn set_repo_notifications_state(&self, owner: &str, name: &str, query: NotificationPutQuery) -> Result, ForgejoError> { + self.put(&format!("repos/{owner}/{name}/notifications?{}", query.query_string())).await + } +} + +#[derive(Debug)] +pub struct NotificationQuery { + pub all: bool, + pub include_unread: bool, + pub include_read: bool, + pub include_pinned: bool, + pub subject_type: Option, + pub since: Option, + pub before: Option, + pub page: Option, + pub limit: Option, +} + +impl Default for NotificationQuery { + fn default() -> Self { + NotificationQuery { + all: false, + include_unread: true, + include_read: false, + include_pinned: true, + subject_type: None, + since: None, + before: None, + page: None, + limit: None, + } + } +} + +impl NotificationQuery { + fn query_string(&self) -> String { + use std::fmt::Write; + let mut s = String::new(); + if self.all { + s.push_str("all=true&"); + } + if self.include_unread { + s.push_str("status-types=unread&"); + } + if self.include_read { + s.push_str("status-types=read&"); + } + if self.include_pinned { + s.push_str("status-types=pinned&"); + } + if let Some(subject_type) = self.subject_type { + s.push_str("subject-type="); + s.push_str(subject_type.as_str()); + s.push('&'); + } + if let Some(since) = &self.since { + s.push_str("since="); + s.push_str(&since.format(&time::format_description::well_known::Rfc3339).unwrap()); + s.push('&'); + } + if let Some(before) = &self.before { + s.push_str("before="); + s.push_str(&before.format(&time::format_description::well_known::Rfc3339).unwrap()); + s.push('&'); + } + if let Some(page) = self.page { + s.push_str("page="); + s.write_fmt(format_args!("{page}")) + .expect("writing to a string never fails"); + s.push('&'); + } + if let Some(limit) = self.limit { + s.push_str("limit="); + s.write_fmt(format_args!("{limit}")) + .expect("writing to a string never fails"); + } + s + } +} + +#[derive(Debug, Clone, Copy)] +pub enum NotificationSubjectType { + Issue, + Pull, + Commit, + Repository, +} + +impl NotificationSubjectType { + fn as_str(&self) -> &'static str { + match self { + NotificationSubjectType::Issue => "issue", + NotificationSubjectType::Pull => "pull", + NotificationSubjectType::Commit => "commit", + NotificationSubjectType::Repository => "repository", + } + } +} + +#[derive(serde::Deserialize, Debug, PartialEq)] +pub struct NotificationThread { + pub id: u64, + pub pinned: bool, + pub repository: Repository, + pub subject: NotificationSubject, + pub unread: bool, + #[serde(with = "time::serde::rfc3339")] + pub updated_at: time::OffsetDateTime, + pub url: Url, +} + +#[derive(serde::Deserialize, Debug, PartialEq)] +pub struct NotificationSubject { + pub html_url: Url, + pub latest_comment_html_url: Url, + pub latest_comment_url: Url, + pub state: String, + pub title: String, + #[serde(rename = "type")] + pub _type: String, + pub url: Url, +} + +#[derive(Debug)] +pub struct NotificationPutQuery { + pub last_read_at: Option, + pub all: bool, + pub include_unread: bool, + pub include_read: bool, + pub include_pinned: bool, + pub to_status: ToStatus, +} + +impl Default for NotificationPutQuery { + fn default() -> Self { + NotificationPutQuery { + last_read_at: None, + all: false, + include_unread: true, + include_read: false, + include_pinned: false, + to_status: ToStatus::default(), + } + } +} + +impl NotificationPutQuery { + fn query_string(&self) -> String { + let mut s = String::new(); + if let Some(last_read_at) = &self.last_read_at { + s.push_str("since="); + s.push_str(&last_read_at.format(&time::format_description::well_known::Rfc3339).unwrap()); + s.push('&'); + } + if self.all { + s.push_str("all=true&"); + } + if self.include_unread { + s.push_str("status-types=unread&"); + } + if self.include_read { + s.push_str("status-types=read&"); + } + if self.include_pinned { + s.push_str("status-types=pinned&"); + } + s.push_str("subject-type="); + s.push_str(self.to_status.as_str()); + s + } +} + +#[derive(Default, Debug)] +pub enum ToStatus { + #[default] + Read, + Unread, + Pinned, +} + +impl ToStatus { + fn as_str(&self) -> &'static str { + match self { + ToStatus::Read => "read", + ToStatus::Unread => "unread", + ToStatus::Pinned => "pinned", + } + } +} + +#[derive(serde::Deserialize, Debug, PartialEq)] +pub struct NotificationCount { + pub new: u64, +}