1use chrono::{DateTime, Utc};
2use compact_str::CompactString;
3
4#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
6pub struct Release {
7 pub name: String,
8 pub tag_name: String,
9 pub description: Option<String>,
10 pub created_at: DateTime<Utc>,
11 pub released_at: DateTime<Utc>,
12 pub upcoming_release: bool,
13 pub author: Author,
14 pub commit: Commit,
15 pub milestones: Option<Vec<Milestone>>,
16 pub commit_path: String,
17 pub tag_path: String,
18 pub assets: ReleaseAssets,
19 pub evidences: Vec<ReleaseEvidence>,
20 pub _links: ReleaseLinks,
21}
22
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub struct AuthorId(u64);
26
27impl AuthorId {
28 pub const fn new(id: u64) -> Self {
29 Self(id)
30 }
31
32 pub const fn into_u64(self) -> u64 {
33 self.0
34 }
35}
36
37#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
38#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
39pub struct Author {
40 pub id: AuthorId,
41 pub name: String,
42 pub username: String,
43 pub state: String,
44 pub avatar_url: String,
45 pub web_url: String,
46}
47
48#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
49#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
50pub struct Commit {
51 pub id: String,
52 pub short_id: String,
53 pub title: String,
54 pub created_at: DateTime<Utc>,
55 pub parent_ids: Vec<String>,
56 pub message: String,
57 pub author_name: String,
58 pub author_email: String,
59 pub authored_date: DateTime<Utc>,
60 pub committer_name: String,
61 pub committer_email: String,
62 pub committed_date: DateTime<Utc>,
63}
64
65#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
66#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
67pub struct Milestone {
68 }
70
71#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
72#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
73pub struct ReleaseAssets {
74 pub count: u64,
75 pub sources: Vec<ReleaseSource>,
76 pub links: Vec<ReleaseLink>,
77}
78
79#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
80#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
81pub struct ReleaseSource {
82 pub format: String,
83 pub url: String,
84}
85
86#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
87#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
88pub struct ReleaseEvidence {
89 }
91
92#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
93#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
94pub struct ReleaseLinks {
95 closed_issues_url: String,
96 closed_merge_requests_url: String,
97 edit_url: Option<String>,
98 merged_merge_requests_url: String,
99 opened_issues_url: String,
100 opened_merge_requests_url: String,
101 #[cfg_attr(feature = "serde", serde(rename = "self"))]
102 this: String,
103}
104
105#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
107#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
108pub enum ReleaseOrder {
109 ReleasedAt,
110 CreatedAt,
111}
112
113impl ReleaseOrder {
114 pub fn as_str(self) -> &'static str {
115 match self {
116 Self::ReleasedAt => "released_at",
117 Self::CreatedAt => "created_at",
118 }
119 }
120}
121
122#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
123#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
124pub struct ReleaseLinkId(u64);
125
126impl ReleaseLinkId {
127 pub const fn new(id: u64) -> Self {
128 Self(id)
129 }
130
131 pub const fn into_u64(self) -> u64 {
132 self.0
133 }
134}
135
136#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
137#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
138pub struct ReleaseLink {
139 pub id: ReleaseLinkId,
140 pub name: String,
141 pub url: String,
142 pub direct_asset_url: String,
143 pub link_type: ReleaseLinkType,
144}
145
146#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
147#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
148pub enum ReleaseLinkType {
149 #[cfg_attr(feature = "serde", serde(rename = "other"))]
150 Other,
151 #[cfg_attr(feature = "serde", serde(rename = "runbook"))]
152 Runbook,
153 #[cfg_attr(feature = "serde", serde(rename = "image"))]
154 Image,
155 #[cfg_attr(feature = "serde", serde(rename = "package"))]
156 Package,
157}
158
159impl ReleaseLinkType {
160 pub fn as_str(self) -> &'static str {
161 match self {
162 Self::Other => "other",
163 Self::Runbook => "runbook",
164 Self::Image => "image",
165 Self::Package => "package",
166 }
167 }
168}
169
170#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
171#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
172pub struct InputReleaseAssets<Links = Vec<InputReleaseLink<CompactString>>> {
173 pub links: Links,
174}
175
176pub type InputReleaseAssetsView<'req, Str = CompactString> = InputReleaseAssets<&'req [InputReleaseLink<Str>]>;
177
178impl<Links> InputReleaseAssets<Links> {
179 pub fn as_view<Str>(&self) -> InputReleaseAssetsView<'_, Str>
180 where
181 Links: AsRef<[InputReleaseLink<Str>]>,
182 {
183 InputReleaseAssetsView {
184 links: self.links.as_ref(),
185 }
186 }
187}
188
189#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
190#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
191pub struct InputReleaseLink<Str = CompactString> {
192 pub name: Str,
193 pub url: Str,
194 pub direct_asset_path: Option<Str>,
195 pub link_type: ReleaseLinkType,
196}
197
198pub type InputReleaseLinkView<'req> = InputReleaseLink<&'req str>;
199
200impl<Str: AsRef<str>> InputReleaseLink<Str> {
201 pub fn as_view(&self) -> InputReleaseLinkView<'_> {
202 InputReleaseLinkView {
203 name: self.name.as_ref(),
204 url: self.url.as_ref(),
205 direct_asset_path: self.direct_asset_path.as_ref().map(|s| s.as_ref()),
206 link_type: self.link_type,
207 }
208 }
209}
210
211#[cfg(test)]
212mod test {
213 use super::*;
214 use chrono::{TimeZone, Utc};
215
216 #[cfg_attr(feature = "serde", test)]
217 #[allow(deprecated)]
218 fn read_release() {
219 let raw = r#"{"name": "v0.12.5","tag_name": "v0.12.5","description": null,"created_at": "2023-03-25T23:03:17.165Z","released_at": "2023-03-25T23:03:17.165Z","upcoming_release": false,"author": {"id": 743516,"username": "demurgos","name": "Charles Samborski","state": "active","avatar_url": "https://secure.gravatar.com/avatar/4ccbb457b6d50bb79ec32b66c2f4e301?s=80&d=identicon","web_url": "https://gitlab.com/demurgos"},"commit": {"id": "e8cfd320ee2b8dcee22b0bc0ceaf52d7dd667c2d","short_id": "e8cfd320","created_at": "2023-03-17T00:39:35.000+01:00","parent_ids": ["c8528d12e05c9ee803547eb556cb5394ac3d1abc"],"title": "[bin] Add support for precompiled binary","message": "[bin] Add support for precompiled binary\n\nThis commit adds support for precompiled binaries built in GitLab CI.\n","author_name": "Charles Samborski","author_email": "demurgos@demurgos.net","authored_date": "2023-03-16T23:56:54.000+01:00","committer_name": "Charles Samborski","committer_email": "demurgos@demurgos.net","committed_date": "2023-03-17T00:39:35.000+01:00","trailers": {},"web_url": "https://gitlab.com/eternaltwin/eternaltwin/-/commit/e8cfd320ee2b8dcee22b0bc0ceaf52d7dd667c2d"},"commit_path": "/eternaltwin/eternaltwin/-/commit/e8cfd320ee2b8dcee22b0bc0ceaf52d7dd667c2d","tag_path": "/eternaltwin/eternaltwin/-/tags/v0.12.5","assets": {"count": 5,"sources": [{"format": "zip","url": "https://gitlab.com/eternaltwin/eternaltwin/-/archive/v0.12.5/eternaltwin-v0.12.5.zip"},{"format": "tar.gz","url": "https://gitlab.com/eternaltwin/eternaltwin/-/archive/v0.12.5/eternaltwin-v0.12.5.tar.gz"},{"format": "tar.bz2","url": "https://gitlab.com/eternaltwin/eternaltwin/-/archive/v0.12.5/eternaltwin-v0.12.5.tar.bz2"},{"format": "tar","url": "https://gitlab.com/eternaltwin/eternaltwin/-/archive/v0.12.5/eternaltwin-v0.12.5.tar"}],"links": [{"id": 1492034,"name": "eternaltwin-x86_64-apple-darwin","url": "https://gitlab.com/eternaltwin/eternaltwin/-/packages/13511182","direct_asset_url": "https://gitlab.com/eternaltwin/eternaltwin/-/releases/v0.12.5/downloads/eternaltwin-x86_64-apple-darwin","link_type": "package"}]},"evidences": [{"sha": "f9021712d926f78fc9272ac24733492facd5fb4353ed","filepath": "https://gitlab.com/eternaltwin/eternaltwin/-/releases/v0.12.5/evidences/4460894.json","collected_at": "2023-03-25T23:03:17.328Z"}],"_links": {"closed_issues_url": "https://gitlab.com/eternaltwin/eternaltwin/-/issues?release_tag=v0.12.5&scope=all&state=closed","closed_merge_requests_url": "https://gitlab.com/eternaltwin/eternaltwin/-/merge_requests?release_tag=v0.12.5&scope=all&state=closed","edit_url": "https://gitlab.com/eternaltwin/eternaltwin/-/releases/v0.12.5/edit","merged_merge_requests_url": "https://gitlab.com/eternaltwin/eternaltwin/-/merge_requests?release_tag=v0.12.5&scope=all&state=merged","opened_issues_url": "https://gitlab.com/eternaltwin/eternaltwin/-/issues?release_tag=v0.12.5&scope=all&state=opened","opened_merge_requests_url": "https://gitlab.com/eternaltwin/eternaltwin/-/merge_requests?release_tag=v0.12.5&scope=all&state=opened","self": "https://gitlab.com/eternaltwin/eternaltwin/-/releases/v0.12.5"}}"#;
220 let actual: Release = serde_json::from_str(raw).unwrap();
221 let expected = Release {
222 name: "v0.12.5".to_string(),
223 tag_name: "v0.12.5".to_string(),
224 description: None,
225 created_at: Utc.ymd(2023, 3, 25).and_hms_milli(23, 3, 17, 165),
226 released_at: Utc.ymd(2023, 3, 25).and_hms_milli(23, 3, 17, 165),
227 upcoming_release: false,
228 author: Author {
229 id: AuthorId::new(743516),
230 name: "Charles Samborski".to_string(),
231 username: "demurgos".to_string(),
232 state: "active".to_string(),
233 avatar_url: "https://secure.gravatar.com/avatar/4ccbb457b6d50bb79ec32b66c2f4e301?s=80&d=identicon".to_string(),
234 web_url: "https://gitlab.com/demurgos".to_string(),
235 },
236 commit: Commit {
237 id: "e8cfd320ee2b8dcee22b0bc0ceaf52d7dd667c2d".to_string(),
238 short_id: "e8cfd320".to_string(),
239 title: "[bin] Add support for precompiled binary".to_string(),
240 created_at: Utc.ymd(2023, 3, 16).and_hms(23, 39, 35),
241 parent_ids: vec![
242 "c8528d12e05c9ee803547eb556cb5394ac3d1abc".to_string(),
243 ],
244 message: "[bin] Add support for precompiled binary\n\nThis commit adds support for precompiled binaries built in GitLab CI.\n".to_string(),
245 author_name: "Charles Samborski".to_string(),
246 author_email: "demurgos@demurgos.net".to_string(),
247 authored_date: Utc.ymd(2023, 3, 16).and_hms(22, 56, 54),
248 committer_name: "Charles Samborski".to_string(),
249 committer_email: "demurgos@demurgos.net".to_string(),
250 committed_date: Utc.ymd(2023, 3, 16).and_hms(23, 39, 35),
251 },
252 milestones: None,
253 commit_path: "/eternaltwin/eternaltwin/-/commit/e8cfd320ee2b8dcee22b0bc0ceaf52d7dd667c2d".to_string(),
254 tag_path: "/eternaltwin/eternaltwin/-/tags/v0.12.5".to_string(),
255 assets: ReleaseAssets {
256 count: 5,
257 sources: vec![
258 ReleaseSource {
259 format: "zip".to_string(),
260 url: "https://gitlab.com/eternaltwin/eternaltwin/-/archive/v0.12.5/eternaltwin-v0.12.5.zip".to_string(),
261 },
262 ReleaseSource {
263 format: "tar.gz".to_string(),
264 url: "https://gitlab.com/eternaltwin/eternaltwin/-/archive/v0.12.5/eternaltwin-v0.12.5.tar.gz".to_string(),
265 },
266 ReleaseSource {
267 format: "tar.bz2".to_string(),
268 url: "https://gitlab.com/eternaltwin/eternaltwin/-/archive/v0.12.5/eternaltwin-v0.12.5.tar.bz2".to_string(),
269 },
270 ReleaseSource {
271 format: "tar".to_string(),
272 url: "https://gitlab.com/eternaltwin/eternaltwin/-/archive/v0.12.5/eternaltwin-v0.12.5.tar".to_string(),
273 },
274 ],
275 links: vec![
276 ReleaseLink {
277 id: ReleaseLinkId::new(1492034),
278 name: "eternaltwin-x86_64-apple-darwin".to_string(),
279 url: "https://gitlab.com/eternaltwin/eternaltwin/-/packages/13511182".to_string(),
280 direct_asset_url: "https://gitlab.com/eternaltwin/eternaltwin/-/releases/v0.12.5/downloads/eternaltwin-x86_64-apple-darwin".to_string(),
281 link_type: ReleaseLinkType::Package,
282 }
283 ],
284 },
285 evidences: vec![
286 ReleaseEvidence {},
287 ],
288 _links: ReleaseLinks {
289 closed_issues_url: "https://gitlab.com/eternaltwin/eternaltwin/-/issues?release_tag=v0.12.5&scope=all&state=closed".to_string(),
290 closed_merge_requests_url: "https://gitlab.com/eternaltwin/eternaltwin/-/merge_requests?release_tag=v0.12.5&scope=all&state=closed".to_string(),
291 edit_url: Some("https://gitlab.com/eternaltwin/eternaltwin/-/releases/v0.12.5/edit".to_string()),
292 merged_merge_requests_url: "https://gitlab.com/eternaltwin/eternaltwin/-/merge_requests?release_tag=v0.12.5&scope=all&state=merged".to_string(),
293 opened_issues_url: "https://gitlab.com/eternaltwin/eternaltwin/-/issues?release_tag=v0.12.5&scope=all&state=opened".to_string(),
294 opened_merge_requests_url: "https://gitlab.com/eternaltwin/eternaltwin/-/merge_requests?release_tag=v0.12.5&scope=all&state=opened".to_string(),
295 this: "https://gitlab.com/eternaltwin/eternaltwin/-/releases/v0.12.5".to_string(),
296 },
297 };
298 assert_eq!(actual, expected);
299 }
300
301 #[cfg_attr(feature = "serde", test)]
302 #[allow(deprecated)]
303 fn read_release_link() {
304 let raw = r#"{"id":1492019,"name":"eternaltwin-x86_64-unknown-linux-gnu","url":"https://gitlab.com/eternaltwin/eternaltwin/-/packages/13511234","direct_asset_url":"https://gitlab.com/eternaltwin/eternaltwin/-/releases/v0.12.5/downloads/eternaltwin-x86_64-unknown-linux-gnu","link_type":"package"}"#;
305 let actual: ReleaseLink = serde_json::from_str(raw).unwrap();
306 let expected = ReleaseLink {
307 id: ReleaseLinkId::new(1492019),
308 name: "eternaltwin-x86_64-unknown-linux-gnu".to_string(),
309 url: "https://gitlab.com/eternaltwin/eternaltwin/-/packages/13511234".to_string(),
310 direct_asset_url:
311 "https://gitlab.com/eternaltwin/eternaltwin/-/releases/v0.12.5/downloads/eternaltwin-x86_64-unknown-linux-gnu"
312 .to_string(),
313 link_type: ReleaseLinkType::Package,
314 };
315 assert_eq!(actual, expected);
316 }
317
318 #[cfg_attr(feature = "serde", test)]
319 #[allow(deprecated)]
320 fn read_xml2_release_list() {
321 let raw = include_str!("../../test-resources/get-project-release-list/libxml2/output.json");
322 let actual: Vec<Release> = serde_json::from_str(raw).unwrap();
323 assert_eq!(actual.len(), 20);
324 }
325}