diff --git a/giterated-models/src/issue/events.rs b/giterated-models/src/issue/events.rs new file mode 100644 index 0000000..c20411a --- /dev/null +++ b/giterated-models/src/issue/events.rs @@ -0,0 +1,38 @@ +use serde::{Deserialize, Serialize}; + +use crate::user::User; + +use super::{IssueComment, IssueCommentRevision, IssueStatus, IssueTag}; + +#[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct IssueTimeline { + pub events: Vec, +} + +#[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct IssueEvent { + /// The id of this event + pub id: u64, + /// Time at which the event was created + pub created: chrono::DateTime, + /// User who triggered the event + pub triggeror: User, + /// Content of the event + pub content: IssueEventContent, +} + +// TODO: serialize with flattened internal struct +#[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub enum IssueEventContent { + CommentCreated(IssueComment), + CommentRevised(IssueCommentRevision), + CommentDeleted(IssueComment), + + Tagged(IssueTag), + Untagged(IssueTag), + + StatusChanged(IssueStatus), + + UserAssigned(User), + UserUnassigned(User), +} diff --git a/giterated-models/src/issue/mod.rs b/giterated-models/src/issue/mod.rs new file mode 100644 index 0000000..3197f5c --- /dev/null +++ b/giterated-models/src/issue/mod.rs @@ -0,0 +1,139 @@ +pub mod events; +pub mod operations; +pub mod values; + +use std::{fmt::Display, str::FromStr}; + +use events::IssueTimeline; +use serde::{Deserialize, Serialize}; + +use crate::{object::GiteratedObject, repository::Repository, user::User}; + +/// An issue, defined by the [`Repository`] it is related to along with +/// its id. +/// +/// # Textual Format +/// An issue's textual reference is defined as: +/// +/// `#{id: u64}:{repository: Repository}` +#[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct Issue { + pub id: u64, + /// The repository the issue is related to + pub repository: Repository, +} + +impl GiteratedObject for Issue { + fn object_name() -> &'static str { + "issue" + } + + fn home_uri(&self) -> String { + self.repository.home_uri() + } + + fn from_object_str(object_str: &str) -> Result { + Ok(Issue::from_str(object_str)?) + } +} + +impl Display for Issue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&format!("#{}:{}", self.id, self.repository)) + } +} + +#[derive(Debug, thiserror::Error)] +#[error("couldn't parse issue")] +pub struct IssueParseError; + +impl FromStr for Issue { + type Err = IssueParseError; + + fn from_str(s: &str) -> Result { + // Remove the pound from the start + let s = s + .starts_with('#') + .then(|| s.replace('#', "")) + .ok_or(IssueParseError)?; + // Split the id from the repository + let (id, repository) = s.split_once(':').ok_or(IssueParseError)?; + + // Parse the id and the repository + let id: u64 = id.parse().map_err(|_| IssueParseError)?; + let repository = Repository::from_str(repository).map_err(|_| IssueParseError)?; + + Ok(Self { id, repository }) + } +} + +#[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct IssueInfo { + #[serde(flatten)] + pub basic: IssueInfoBasic, + pub body: String, + pub timeline: IssueTimeline, +} + +#[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct IssueInfoBasic { + pub id: u64, + pub status: IssueStatus, + pub title: String, + pub tags: Vec, + pub last_activity: chrono::DateTime, + pub created: chrono::DateTime, + pub creator: User, + pub statistics: IssueStatistics, + pub assignees: Vec, +} + +#[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct IssueStatistics { + pub comments: u64, +} + +/// Tag (sometimes referred to as label) to categorize issues. +#[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct IssueTag { + pub category: String, + pub sub_category: Option, + pub description: Option, +} + +#[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub enum IssueStatus { + Open, + Closed, +} + +impl Display for IssueStatus { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + IssueStatus::Open => f.write_str("Open"), + IssueStatus::Closed => f.write_str("Closed"), + } + } +} + +#[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct IssueComment { + /// Event id + pub id: u64, + /// Creation date + pub created: chrono::DateTime, + /// The user who created the comment + pub creator: User, + /// Body of the comment + pub body: String, +} + +#[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct IssueCommentRevision { + /// Event id + pub id: u64, + /// Creation date + pub created: chrono::DateTime, + /// Body of the comment + pub body: String, +} diff --git a/giterated-models/src/issue/operations.rs b/giterated-models/src/issue/operations.rs new file mode 100644 index 0000000..fcf49c0 --- /dev/null +++ b/giterated-models/src/issue/operations.rs @@ -0,0 +1,40 @@ +use serde::{Deserialize, Serialize}; + +use crate::{ + error::{OperationError, RepositoryError}, + object::Object, + object_backend::ObjectBackend, + operation::GiteratedOperation, +}; + +use super::{Issue, IssueInfo}; + +/// A request to get info about an issue. (graphql please) +/// +/// # Authentication +/// - Instance Authentication +/// - Validate request against the `issued_for` public key +/// - Validate User token against the user's instance's public key +/// # Authorization +/// - User Authorization +/// - Potential User permissions checks +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct IssueInfoRequest { + pub id: u64, +} + +impl GiteratedOperation for IssueInfoRequest { + type Success = IssueInfo; + type Failure = RepositoryError; +} + +impl + std::fmt::Debug> Object<'_, S, Issue, B> { + pub async fn info( + &mut self, + id: u64, + operation_state: &S, + ) -> Result> { + self.request::(IssueInfoRequest { id }, operation_state) + .await + } +} diff --git a/giterated-models/src/issue/values.rs b/giterated-models/src/issue/values.rs new file mode 100644 index 0000000..2ce32f8 --- /dev/null +++ b/giterated-models/src/issue/values.rs @@ -0,0 +1,81 @@ +use serde::{Deserialize, Serialize}; + +use crate::{settings::Setting, user::User, value::GiteratedObjectValue}; + +use super::{Issue, IssueStatus, IssueTag}; + +#[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[repr(transparent)] +#[serde(transparent)] +pub struct Tags(pub Vec); + +impl GiteratedObjectValue for Tags { + type Object = Issue; + + fn value_name() -> &'static str { + "tags" + } +} + +impl Setting for Tags { + fn name() -> &'static str { + "tags" + } +} + +#[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[repr(transparent)] +#[serde(transparent)] +pub struct Status(pub IssueStatus); + +impl GiteratedObjectValue for Status { + type Object = Issue; + + fn value_name() -> &'static str { + "status" + } +} + +impl Setting for Status { + fn name() -> &'static str { + "status" + } +} + +#[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[repr(transparent)] +#[serde(transparent)] +pub struct Title(pub String); + +impl GiteratedObjectValue for Title { + type Object = Issue; + + fn value_name() -> &'static str { + "title" + } +} + +impl Setting for Title { + fn name() -> &'static str { + "title" + } +} + +#[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[repr(transparent)] +#[serde(transparent)] +pub struct Assignees(pub Vec); + +impl GiteratedObjectValue for Assignees { + type Object = Issue; + + fn value_name() -> &'static str { + "assignees" + } +} + +impl Setting for Assignees { + fn name() -> &'static str { + "assignees" + } +} diff --git a/giterated-models/src/lib.rs b/giterated-models/src/lib.rs index 5b557af..7a476d9 100644 --- a/giterated-models/src/lib.rs +++ b/giterated-models/src/lib.rs @@ -3,6 +3,7 @@ pub mod discovery; pub mod error; pub mod handshake; pub mod instance; +pub mod issue; pub mod message; pub mod object; pub mod object_backend; diff --git a/giterated-models/src/repository/mod.rs b/giterated-models/src/repository/mod.rs index e304fc3..8e2a2c8 100644 --- a/giterated-models/src/repository/mod.rs +++ b/giterated-models/src/repository/mod.rs @@ -472,18 +472,3 @@ pub struct RepositorySummary { pub description: Option, pub last_commit: Option, } - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct IssueLabel { - pub name: String, - pub color: String, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct RepositoryIssue { - pub author: User, - pub id: u64, - pub title: String, - pub contents: String, - pub labels: Vec, -}