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

ambee/giterated

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

Fixed imports!

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨ef0e853

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