thunderstore/
models.rs

1use crate::{PackageIdent, VersionIdent};
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::{collections::HashSet, hash::Hash};
6use url::Url;
7use uuid::Uuid;
8
9#[derive(Serialize, Deserialize, Debug, Clone, Eq)]
10#[non_exhaustive]
11pub struct PackageV1 {
12    #[serde(rename = "uuid4")]
13    pub uuid: Uuid,
14    #[serde(rename = "owner")]
15    pub namespace: String,
16    pub name: String,
17    #[serde(rename = "full_name")]
18    pub ident: PackageIdent,
19    pub categories: HashSet<String>,
20    pub date_created: DateTime<Utc>,
21    pub date_updated: DateTime<Utc>,
22    pub donation_link: Option<Url>,
23    pub has_nsfw_content: bool,
24    pub is_deprecated: bool,
25    pub is_pinned: bool,
26    pub package_url: Url,
27    pub rating_score: u32,
28    pub versions: Vec<PackageVersionV1>,
29}
30
31impl PackageV1 {
32    pub fn latest(&self) -> &PackageVersionV1 {
33        &self.versions[0]
34    }
35
36    pub fn is_modpack(&self) -> bool {
37        self.categories.contains("Modpacks")
38    }
39
40    pub fn version_by_id(&self, uuid: &Uuid) -> Option<&PackageVersionV1> {
41        self.versions.iter().find(|v| v.uuid == *uuid)
42    }
43
44    pub fn version_by_name(&self, version: &semver::Version) -> Option<&PackageVersionV1> {
45        self.versions.iter().find(|v| v.number == *version)
46    }
47
48    pub fn total_downloads(&self) -> u32 {
49        self.versions.iter().map(|v| v.downloads).sum()
50    }
51}
52
53impl Hash for PackageV1 {
54    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
55        self.uuid.hash(state);
56    }
57}
58
59impl PartialEq for PackageV1 {
60    fn eq(&self, other: &Self) -> bool {
61        self.uuid == other.uuid
62    }
63}
64
65#[derive(Serialize, Deserialize, Debug, Clone, Eq)]
66#[non_exhaustive]
67pub struct PackageVersionV1 {
68    #[serde(rename = "uuid4")]
69    pub uuid: Uuid,
70    pub name: String,
71    #[serde(rename = "version_number")]
72    pub number: semver::Version,
73    #[serde(rename = "full_name")]
74    pub ident: VersionIdent,
75    pub date_created: DateTime<Utc>,
76    pub dependencies: Vec<VersionIdent>,
77    pub description: String,
78    pub download_url: Url,
79    pub downloads: u32,
80    pub file_size: u64,
81    pub icon: Url,
82    pub is_active: bool,
83    pub website_url: String,
84}
85
86impl PartialEq for PackageVersionV1 {
87    fn eq(&self, other: &Self) -> bool {
88        self.uuid == other.uuid
89    }
90}
91
92impl Hash for PackageVersionV1 {
93    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
94        self.uuid.hash(state);
95    }
96}
97
98#[derive(Serialize, Deserialize, Debug, Clone)]
99#[non_exhaustive]
100pub struct LegacyProfileCreateResponse {
101    pub key: Uuid,
102}
103
104#[derive(Serialize, Deserialize, Debug, Clone)]
105#[non_exhaustive]
106pub struct UserMediaInitiateUploadParams {
107    #[serde(rename = "filename")]
108    pub name: String,
109    #[serde(rename = "file_size_bytes")]
110    pub size: u64,
111}
112
113#[derive(Debug, Serialize, Deserialize, Clone)]
114#[non_exhaustive]
115pub struct UserMediaInitiateUploadResponse {
116    pub user_media: UserMedia,
117    pub upload_urls: Vec<UploadPartUrl>,
118}
119
120#[derive(Debug, Serialize, Deserialize, Clone)]
121#[non_exhaustive]
122pub struct UserMedia {
123    pub uuid: Uuid,
124    #[serde(rename = "filename")]
125    pub name: String,
126    pub size: u64,
127    #[serde(rename = "datetime_created")]
128    pub date_created: DateTime<Utc>,
129    pub expiry: DateTime<Utc>,
130    pub status: UserMediaStatus,
131}
132
133#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
134#[serde(rename_all = "snake_case")]
135#[non_exhaustive]
136pub enum UserMediaStatus {
137    Initial,
138    UploadInitiated,
139    UploadCreated,
140    UploadError,
141    UploadComplete,
142    UploadAborted,
143}
144
145#[derive(Debug, Serialize, Deserialize, Clone)]
146#[non_exhaustive]
147pub struct UploadPartUrl {
148    #[serde(rename = "part_number")]
149    pub number: u32,
150    pub url: String,
151    pub offset: u64,
152    pub length: u64,
153}
154
155#[derive(Debug, Serialize, Deserialize, Clone)]
156#[non_exhaustive]
157pub struct UserMediaFinishUploadParams {
158    pub parts: Vec<CompletedPart>,
159}
160
161#[derive(Debug, Serialize, Deserialize, Clone)]
162pub struct CompletedPart {
163    #[serde(rename = "ETag")]
164    pub tag: String,
165    #[serde(rename = "PartNumber")]
166    pub number: u32,
167}
168
169#[derive(Debug, Serialize, Deserialize, Clone)]
170#[non_exhaustive]
171pub struct PackageSubmissionResult {
172    pub package_version: PackageVersion,
173    pub available_communities: Vec<AvailableCommunity>,
174}
175
176#[derive(Debug, Serialize, Deserialize, Clone)]
177#[non_exhaustive]
178pub struct AvailableCommunity {
179    pub community: Community,
180    pub categories: CommunityCategory,
181    pub url: Url,
182}
183
184#[derive(Debug, Serialize, Deserialize, Clone)]
185#[non_exhaustive]
186pub struct CommunityCategory {
187    pub name: String,
188    pub slug: String,
189}
190
191#[derive(Debug, Serialize, Deserialize, Clone)]
192#[non_exhaustive]
193pub struct PackageVersionMetrics {
194    pub downloads: u64,
195}
196
197#[derive(Debug, Serialize, Deserialize, Clone)]
198#[non_exhaustive]
199pub struct PackageMetrics {
200    pub downloads: u64,
201}
202
203#[derive(Debug, Serialize, Deserialize, Clone)]
204#[non_exhaustive]
205pub struct RenderMarkdownParams {
206    pub markdown: String,
207}
208
209#[derive(Debug, Serialize, Deserialize, Clone)]
210#[non_exhaustive]
211pub struct RenderMarkdownResponse {
212    pub html: String,
213}
214
215#[derive(Debug, Serialize, Deserialize, Clone)]
216#[non_exhaustive]
217pub struct MarkdownResponse {
218    pub markdown: String,
219}
220
221#[derive(Debug, Serialize, Deserialize, Clone)]
222#[non_exhaustive]
223pub struct PackageIndexEntry {
224    pub namespace: String,
225    pub name: String,
226    pub version_number: semver::Version,
227    pub file_format: Option<String>,
228    pub file_size: u64,
229    pub dependencies: Vec<VersionIdent>,
230}
231
232#[derive(Serialize, Deserialize, Debug, Clone, Eq)]
233#[non_exhaustive]
234pub struct PackageVersion {
235    #[serde(rename = "full_name")]
236    pub ident: VersionIdent,
237    pub description: String,
238    pub icon: Url,
239    pub dependencies: Vec<VersionIdent>,
240    pub download_url: Url,
241    pub downloads: u32,
242    pub date_created: DateTime<Utc>,
243    pub website_url: String,
244    pub is_active: bool,
245}
246
247impl PartialEq for PackageVersion {
248    fn eq(&self, other: &Self) -> bool {
249        self.ident == other.ident
250    }
251}
252
253impl Hash for PackageVersion {
254    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
255        self.ident.hash(state);
256    }
257}
258
259#[derive(Serialize, Deserialize, Debug, Clone, Eq)]
260#[non_exhaustive]
261pub struct Package {
262    #[serde(rename = "full_name")]
263    pub ident: PackageIdent,
264    pub package_url: Url,
265    pub date_created: DateTime<Utc>,
266    pub date_updated: DateTime<Utc>,
267    pub rating_score: i32,
268    pub is_pinned: bool,
269    pub is_deprecated: bool,
270    pub total_downloads: i32,
271    pub latest: PackageVersion,
272    pub community_listings: Vec<PackageListingExperimental>,
273}
274
275impl PartialEq for Package {
276    fn eq(&self, other: &Self) -> bool {
277        self.ident == other.ident
278    }
279}
280
281impl Hash for Package {
282    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
283        self.ident.hash(state);
284    }
285}
286
287#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
288#[non_exhaustive]
289pub struct PackageListingExperimental {
290    pub has_nsfw_content: bool,
291    pub categories: HashSet<String>,
292    pub community: String,
293    pub review_status: ReviewStatus,
294}
295
296#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
297#[serde(rename_all = "snake_case")]
298#[non_exhaustive]
299pub enum ReviewStatus {
300    Unreviewed,
301    Approved,
302    Rejected,
303}
304
305#[derive(Serialize, Deserialize, Debug, Clone)]
306#[non_exhaustive]
307pub struct PaginatedResponse<T> {
308    pub pagination: Pagination,
309    pub results: Vec<T>,
310}
311
312#[derive(Serialize, Deserialize, Debug, Clone)]
313#[non_exhaustive]
314pub struct Pagination {
315    pub next_link: Option<Url>,
316    pub previous_link: Option<Url>,
317}
318
319#[derive(Serialize, Deserialize, Debug, Clone)]
320#[non_exhaustive]
321pub struct Community {
322    #[serde(rename = "identifier")]
323    pub ident: String,
324    pub name: String,
325    pub discord_url: Option<Url>,
326    pub wiki_url: Option<Url>,
327    pub require_package_listing_approval: bool,
328}
329
330#[derive(Serialize, Deserialize, Debug, Clone)]
331#[non_exhaustive]
332pub struct IconValidatorParams {
333    pub icon_data: String,
334}
335
336#[derive(Serialize, Deserialize, Debug, Clone)]
337#[non_exhaustive]
338pub struct ManifestV1ValidatorParams {
339    pub namespace: String,
340    pub manifest_data: String,
341}
342
343#[derive(Serialize, Deserialize, Debug, Clone)]
344#[non_exhaustive]
345pub struct ReadmeValidatorParams {
346    pub readme_data: String,
347}
348
349#[derive(Serialize, Deserialize, Debug, Clone)]
350#[non_exhaustive]
351pub struct ValidatorResponse {
352    pub success: bool,
353}
354
355#[derive(Serialize, Deserialize, Debug, Clone)]
356#[non_exhaustive]
357pub struct WikisResponse {
358    pub results: Vec<ListedWiki>,
359    pub cursor: DateTime<Utc>,
360    pub has_more: bool,
361}
362
363#[derive(Serialize, Deserialize, Debug, Clone)]
364#[non_exhaustive]
365pub struct ListedWiki {
366    pub namespace: String,
367    pub name: String,
368    pub wiki: Wiki,
369}
370
371#[derive(Serialize, Deserialize, Debug, Clone)]
372#[non_exhaustive]
373pub struct Wiki {
374    pub id: String,
375    pub title: String,
376    pub slug: String,
377    #[serde(rename = "datetime_created")]
378    pub created_at: DateTime<Utc>,
379    #[serde(rename = "datetime_updated")]
380    pub updated_at: DateTime<Utc>,
381    pub pages: Vec<WikiPage>,
382}
383
384#[derive(Serialize, Deserialize, Debug, Clone)]
385#[non_exhaustive]
386pub struct WikiPage {
387    pub id: String,
388    pub title: String,
389    pub slug: String,
390    #[serde(rename = "datetime_created")]
391    pub created_at: DateTime<Utc>,
392    #[serde(rename = "datetime_updated")]
393    pub updated_at: DateTime<Utc>,
394    #[serde(default, rename = "markdown_content")]
395    pub content: Option<String>,
396}
397
398#[derive(Serialize, Deserialize, Debug, Clone)]
399#[non_exhaustive]
400pub struct WikiPageUpsert {
401    pub id: Option<String>,
402    pub title: String,
403    #[serde(rename = "markdown_content")]
404    pub content: String,
405}
406
407#[derive(Serialize, Deserialize, Debug, Clone)]
408#[non_exhaustive]
409pub struct WikiPageDelete {
410    pub id: String,
411}