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

ambee/giterated

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

Begin new protocol refactor

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨26651b1

⁨giterated-models/src/model/repository.rs⁩ - ⁨7255⁩ bytes
Raw
1 use std::fmt::{Display, Formatter};
2 use std::str::FromStr;
3
4 use serde::{Deserialize, Serialize};
5
6 use crate::operation::GiteratedObject;
7
8 use super::{instance::Instance, user::User};
9
10 /// A repository, defined by the instance it exists on along with
11 /// its owner and name.
12 ///
13 /// # Textual Format
14 /// A repository's textual reference is defined as:
15 ///
16 /// `{owner: User}/{name: String}@{instance: Instance}`
17 ///
18 /// # Examples
19 /// For the repository named `foo` owned by `barson:giterated.dev` on the instance
20 /// `giterated.dev`, the following [`Repository`] initialization would
21 /// be valid:
22 ///
23 /// ```
24 /// let repository = Repository {
25 /// owner: User::from_str("barson:giterated.dev").unwrap(),
26 /// name: String::from("foo"),
27 /// instance: Instance::from_str("giterated.dev").unwrap()
28 /// };
29 ///
30 /// // This is correct
31 /// assert_eq!(Repository::from_str("barson:giterated.dev/[email protected]").unwrap(), repository);
32 /// ```
33 #[derive(Hash, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
34 pub struct Repository {
35 pub owner: User,
36 pub name: String,
37 /// Instance the repository is on
38 pub instance: Instance,
39 }
40
41 impl GiteratedObject for Repository {
42 fn object_name(&self) -> &str {
43 "repository"
44 }
45
46 fn from_object_str(object_str: &str) -> Result<Self, anyhow::Error> {
47 Ok(Repository::from_str(object_str).unwrap())
48 }
49 }
50
51 impl ToString for Repository {
52 fn to_string(&self) -> String {
53 format!("{}/{}@{}", self.owner, self.name, self.instance.to_string())
54 }
55 }
56
57 impl TryFrom<String> for Repository {
58 type Error = ();
59
60 fn try_from(value: String) -> Result<Self, Self::Error> {
61 Self::from_str(&value)
62 }
63 }
64
65 impl FromStr for Repository {
66 type Err = ();
67
68 fn from_str(s: &str) -> Result<Self, Self::Err> {
69 let mut by_ampersand = s.split('@');
70 let mut path_split = by_ampersand.next().unwrap().split('/');
71
72 let instance = Instance::from_str(by_ampersand.next().unwrap()).unwrap();
73 let owner = User::from_str(path_split.next().unwrap()).unwrap();
74 let name = path_split.next().unwrap().to_string();
75
76 Ok(Self {
77 instance,
78 owner,
79 name,
80 })
81 }
82 }
83
84 /// Visibility of the repository to the general eye
85 #[derive(PartialEq, Eq, Debug, Hash, Serialize, Deserialize, Clone, sqlx::Type)]
86 #[sqlx(type_name = "visibility", rename_all = "lowercase")]
87 pub enum RepositoryVisibility {
88 Public,
89 Unlisted,
90 Private,
91 }
92
93 /// Implements [`Display`] for [`RepositoryVisiblity`] using [`Debug`]
94 impl Display for RepositoryVisibility {
95 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
96 write!(f, "{:?}", self)
97 }
98 }
99
100 #[derive(Clone, Debug, Serialize, Deserialize)]
101 pub struct RepositoryView {
102 /// Name of the repository
103 ///
104 /// This is different than the [`Repository`] name,
105 /// which may be a path.
106 pub name: String,
107 /// Owner of the Repository
108 pub owner: User,
109 /// Repository description
110 pub description: Option<String>,
111 /// Repository visibility
112 pub visibility: RepositoryVisibility,
113 /// Default branch of the repository
114 pub default_branch: String,
115 /// Last commit made to the repository
116 pub latest_commit: Option<Commit>,
117 /// Revision of the displayed tree
118 pub tree_rev: Option<String>,
119 /// Repository tree
120 pub tree: Vec<RepositoryTreeEntry>,
121 }
122
123 #[derive(Debug, Clone, Serialize, Deserialize)]
124 pub enum RepositoryObjectType {
125 Tree,
126 Blob,
127 }
128
129 /// Stored info for our tree entries
130 #[derive(Debug, Clone, Serialize, Deserialize)]
131 pub struct RepositoryTreeEntry {
132 /// Name of the tree/blob
133 pub name: String,
134 /// Type of the tree entry
135 pub object_type: RepositoryObjectType,
136 /// Git supplies us with the mode at all times, and people like it displayed.
137 pub mode: i32,
138 /// File size
139 pub size: Option<usize>,
140 /// Last commit made to the tree/blob
141 pub last_commit: Option<Commit>,
142 }
143
144 impl RepositoryTreeEntry {
145 // I love you Emilia <3
146 pub fn new(name: &str, object_type: RepositoryObjectType, mode: i32) -> Self {
147 Self {
148 name: name.to_string(),
149 object_type,
150 mode,
151 size: None,
152 last_commit: None,
153 }
154 }
155 }
156
157 #[derive(Debug, Clone, Serialize, Deserialize)]
158 pub struct RepositoryTreeEntryWithCommit {
159 pub tree_entry: RepositoryTreeEntry,
160 pub commit: Commit,
161 }
162
163 /// Info about a git commit
164 #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
165 pub struct Commit {
166 /// Unique commit ID
167 pub oid: String,
168 /// Shortened abbreviated OID
169 /// This starts at the git config's "core.abbrev" length (default 7 characters) and
170 /// iteratively extends to a longer string if that length is ambiguous. The
171 /// result will be unambiguous (at least until new objects are added to the repository).
172 pub short_oid: String,
173 /// Full commit message
174 pub message: Option<String>,
175 /// Who created the commit
176 pub author: CommitSignature,
177 /// Who committed the commit
178 pub committer: CommitSignature,
179 /// Time when the commit happened
180 pub time: chrono::NaiveDateTime,
181 }
182
183 /// Gets all info from [`git2::Commit`] for easy use
184 impl From<git2::Commit<'_>> for Commit {
185 fn from(commit: git2::Commit<'_>) -> Self {
186 Self {
187 oid: commit.id().to_string(),
188 // This shouldn't ever fail, as we already know the object has an oid.
189 short_oid: commit
190 .as_object()
191 .short_id()
192 .unwrap()
193 .as_str()
194 .unwrap()
195 .to_string(),
196 message: commit.message().map(|message| message.to_string()),
197 author: commit.author().into(),
198 committer: commit.committer().into(),
199 time: chrono::NaiveDateTime::from_timestamp_opt(commit.time().seconds(), 0).unwrap(),
200 }
201 }
202 }
203
204 /// Git commit signature
205 #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
206 pub struct CommitSignature {
207 pub name: Option<String>,
208 pub email: Option<String>,
209 pub time: chrono::NaiveDateTime,
210 }
211
212 /// Converts the signature from git2 into something usable without explicit lifetimes.
213 impl From<git2::Signature<'_>> for CommitSignature {
214 fn from(signature: git2::Signature<'_>) -> Self {
215 Self {
216 name: signature.name().map(|name| name.to_string()),
217 email: signature.email().map(|email| email.to_string()),
218 time: chrono::NaiveDateTime::from_timestamp_opt(signature.when().seconds(), 0).unwrap(),
219 }
220 }
221 }
222
223 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
224 pub struct RepositorySummary {
225 pub repository: Repository,
226 pub owner: User,
227 pub visibility: RepositoryVisibility,
228 pub description: Option<String>,
229 pub last_commit: Option<Commit>,
230 }
231
232 #[derive(Clone, Debug, Serialize, Deserialize)]
233 pub struct IssueLabel {
234 pub name: String,
235 pub color: String,
236 }
237
238 #[derive(Clone, Debug, Serialize, Deserialize)]
239 pub struct RepositoryIssue {
240 pub author: User,
241 pub id: u64,
242 pub title: String,
243 pub contents: String,
244 pub labels: Vec<IssueLabel>,
245 }
246