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

ambee/giterated

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

Change forwarding

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨2556801

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