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

ambee/giterated

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

Base protocol refactor complete

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨079d544

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