JavaScript is disabled, refresh for a better experience. ambee/giterated

ambee/giterated

Git repository hosting, collaboration, and discovery for the Fediverse.

Basic Issue data structures

Emilia - ⁨1⁩ year ago

parent: tbd commit: ⁨509aa4e

Showing ⁨⁨6⁩ changed files⁩ with ⁨⁨299⁩ insertions⁩ and ⁨⁨15⁩ deletions⁩

giterated-models/src/issue/events.rs

View file
@@ -0,0 +1,38 @@
1 use serde::{Deserialize, Serialize};
2
3 use crate::user::User;
4
5 use super::{IssueComment, IssueCommentRevision, IssueStatus, IssueTag};
6
7 #[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
8 pub struct IssueTimeline {
9 pub events: Vec<IssueEvent>,
10 }
11
12 #[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
13 pub struct IssueEvent {
14 /// The id of this event
15 pub id: u64,
16 /// Time at which the event was created
17 pub created: chrono::DateTime<chrono::Utc>,
18 /// User who triggered the event
19 pub triggeror: User,
20 /// Content of the event
21 pub content: IssueEventContent,
22 }
23
24 // TODO: serialize with flattened internal struct
25 #[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
26 pub enum IssueEventContent {
27 CommentCreated(IssueComment),
28 CommentRevised(IssueCommentRevision),
29 CommentDeleted(IssueComment),
30
31 Tagged(IssueTag),
32 Untagged(IssueTag),
33
34 StatusChanged(IssueStatus),
35
36 UserAssigned(User),
37 UserUnassigned(User),
38 }

giterated-models/src/issue/mod.rs

View file
@@ -0,0 +1,139 @@
1 pub mod events;
2 pub mod operations;
3 pub mod values;
4
5 use std::{fmt::Display, str::FromStr};
6
7 use events::IssueTimeline;
8 use serde::{Deserialize, Serialize};
9
10 use crate::{object::GiteratedObject, repository::Repository, user::User};
11
12 /// An issue, defined by the [`Repository`] it is related to along with
13 /// its id.
14 ///
15 /// # Textual Format
16 /// An issue's textual reference is defined as:
17 ///
18 /// `#{id: u64}:{repository: Repository}`
19 #[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
20 pub struct Issue {
21 pub id: u64,
22 /// The repository the issue is related to
23 pub repository: Repository,
24 }
25
26 impl GiteratedObject for Issue {
27 fn object_name() -> &'static str {
28 "issue"
29 }
30
31 fn home_uri(&self) -> String {
32 self.repository.home_uri()
33 }
34
35 fn from_object_str(object_str: &str) -> Result<Self, anyhow::Error> {
36 Ok(Issue::from_str(object_str)?)
37 }
38 }
39
40 impl Display for Issue {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 f.write_str(&format!("#{}:{}", self.id, self.repository))
43 }
44 }
45
46 #[derive(Debug, thiserror::Error)]
47 #[error("couldn't parse issue")]
48 pub struct IssueParseError;
49
50 impl FromStr for Issue {
51 type Err = IssueParseError;
52
53 fn from_str(s: &str) -> Result<Self, Self::Err> {
54 // Remove the pound from the start
55 let s = s
56 .starts_with('#')
57 .then(|| s.replace('#', ""))
58 .ok_or(IssueParseError)?;
59 // Split the id from the repository
60 let (id, repository) = s.split_once(':').ok_or(IssueParseError)?;
61
62 // Parse the id and the repository
63 let id: u64 = id.parse().map_err(|_| IssueParseError)?;
64 let repository = Repository::from_str(repository).map_err(|_| IssueParseError)?;
65
66 Ok(Self { id, repository })
67 }
68 }
69
70 #[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
71 pub struct IssueInfo {
72 #[serde(flatten)]
73 pub basic: IssueInfoBasic,
74 pub body: String,
75 pub timeline: IssueTimeline,
76 }
77
78 #[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
79 pub struct IssueInfoBasic {
80 pub id: u64,
81 pub status: IssueStatus,
82 pub title: String,
83 pub tags: Vec<IssueTag>,
84 pub last_activity: chrono::DateTime<chrono::Utc>,
85 pub created: chrono::DateTime<chrono::Utc>,
86 pub creator: User,
87 pub statistics: IssueStatistics,
88 pub assignees: Vec<User>,
89 }
90
91 #[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
92 pub struct IssueStatistics {
93 pub comments: u64,
94 }
95
96 /// Tag (sometimes referred to as label) to categorize issues.
97 #[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
98 pub struct IssueTag {
99 pub category: String,
100 pub sub_category: Option<String>,
101 pub description: Option<String>,
102 }
103
104 #[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
105 pub enum IssueStatus {
106 Open,
107 Closed,
108 }
109
110 impl Display for IssueStatus {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 match self {
113 IssueStatus::Open => f.write_str("Open"),
114 IssueStatus::Closed => f.write_str("Closed"),
115 }
116 }
117 }
118
119 #[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
120 pub struct IssueComment {
121 /// Event id
122 pub id: u64,
123 /// Creation date
124 pub created: chrono::DateTime<chrono::Utc>,
125 /// The user who created the comment
126 pub creator: User,
127 /// Body of the comment
128 pub body: String,
129 }
130
131 #[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
132 pub struct IssueCommentRevision {
133 /// Event id
134 pub id: u64,
135 /// Creation date
136 pub created: chrono::DateTime<chrono::Utc>,
137 /// Body of the comment
138 pub body: String,
139 }

giterated-models/src/issue/operations.rs

View file
@@ -0,0 +1,40 @@
1 use serde::{Deserialize, Serialize};
2
3 use crate::{
4 error::{OperationError, RepositoryError},
5 object::Object,
6 object_backend::ObjectBackend,
7 operation::GiteratedOperation,
8 };
9
10 use super::{Issue, IssueInfo};
11
12 /// A request to get info about an issue. (graphql please)
13 ///
14 /// # Authentication
15 /// - Instance Authentication
16 /// - Validate request against the `issued_for` public key
17 /// - Validate User token against the user's instance's public key
18 /// # Authorization
19 /// - User Authorization
20 /// - Potential User permissions checks
21 #[derive(Clone, Debug, Serialize, Deserialize)]
22 pub struct IssueInfoRequest {
23 pub id: u64,
24 }
25
26 impl GiteratedOperation<Issue> for IssueInfoRequest {
27 type Success = IssueInfo;
28 type Failure = RepositoryError;
29 }
30
31 impl<S: Clone + Send + Sync, B: ObjectBackend<S> + std::fmt::Debug> Object<'_, S, Issue, B> {
32 pub async fn info(
33 &mut self,
34 id: u64,
35 operation_state: &S,
36 ) -> Result<IssueInfo, OperationError<RepositoryError>> {
37 self.request::<IssueInfoRequest>(IssueInfoRequest { id }, operation_state)
38 .await
39 }
40 }

giterated-models/src/issue/values.rs

View file
@@ -0,0 +1,81 @@
1 use serde::{Deserialize, Serialize};
2
3 use crate::{settings::Setting, user::User, value::GiteratedObjectValue};
4
5 use super::{Issue, IssueStatus, IssueTag};
6
7 #[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)]
8 #[repr(transparent)]
9 #[serde(transparent)]
10 pub struct Tags(pub Vec<IssueTag>);
11
12 impl GiteratedObjectValue for Tags {
13 type Object = Issue;
14
15 fn value_name() -> &'static str {
16 "tags"
17 }
18 }
19
20 impl Setting for Tags {
21 fn name() -> &'static str {
22 "tags"
23 }
24 }
25
26 #[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)]
27 #[repr(transparent)]
28 #[serde(transparent)]
29 pub struct Status(pub IssueStatus);
30
31 impl GiteratedObjectValue for Status {
32 type Object = Issue;
33
34 fn value_name() -> &'static str {
35 "status"
36 }
37 }
38
39 impl Setting for Status {
40 fn name() -> &'static str {
41 "status"
42 }
43 }
44
45 #[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)]
46 #[repr(transparent)]
47 #[serde(transparent)]
48 pub struct Title(pub String);
49
50 impl GiteratedObjectValue for Title {
51 type Object = Issue;
52
53 fn value_name() -> &'static str {
54 "title"
55 }
56 }
57
58 impl Setting for Title {
59 fn name() -> &'static str {
60 "title"
61 }
62 }
63
64 #[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)]
65 #[repr(transparent)]
66 #[serde(transparent)]
67 pub struct Assignees(pub Vec<User>);
68
69 impl GiteratedObjectValue for Assignees {
70 type Object = Issue;
71
72 fn value_name() -> &'static str {
73 "assignees"
74 }
75 }
76
77 impl Setting for Assignees {
78 fn name() -> &'static str {
79 "assignees"
80 }
81 }

giterated-models/src/lib.rs

View file
@@ -3,6 +3,7 @@ pub mod discovery;
3 3 pub mod error;
4 4 pub mod handshake;
5 5 pub mod instance;
6 pub mod issue;
6 7 pub mod message;
7 8 pub mod object;
8 9 pub mod object_backend;

giterated-models/src/repository/mod.rs

View file
@@ -472,18 +472,3 @@ pub struct RepositorySummary {
472 472 pub description: Option<String>,
473 473 pub last_commit: Option<Commit>,
474 474 }
475
476 #[derive(Clone, Debug, Serialize, Deserialize)]
477 pub struct IssueLabel {
478 pub name: String,
479 pub color: String,
480 }
481
482 #[derive(Clone, Debug, Serialize, Deserialize)]
483 pub struct RepositoryIssue {
484 pub author: User,
485 pub id: u64,
486 pub title: String,
487 pub contents: String,
488 pub labels: Vec<IssueLabel>,
489 }