1use chrono::{DateTime, NaiveDate, Utc};
4use serde_derive::*;
5use std::{collections::HashMap, fmt};
6
7#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
9pub struct ApiErrors {
10 pub errors: Vec<ApiError>,
12}
13
14#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
16pub struct ApiError {
17 pub detail: Option<String>,
19}
20
21impl fmt::Display for ApiError {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 write!(
24 f,
25 "{}",
26 self.detail.as_deref().unwrap_or("Unknown API Error")
27 )
28 }
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
33pub enum Sort {
34 Alphabetical,
36 Relevance,
38 Downloads,
40 RecentDownloads,
42 RecentUpdates,
44 NewlyAdded,
46}
47
48impl Sort {
49 pub(crate) fn to_str(&self) -> &str {
50 match self {
51 Self::Alphabetical => "alpha",
52 Self::Relevance => "relevance",
53 Self::Downloads => "downloads",
54 Self::RecentDownloads => "recent-downloads",
55 Self::RecentUpdates => "recent-updates",
56 Self::NewlyAdded => "new",
57 }
58 }
59}
60
61#[derive(Clone, Debug)]
65pub struct CratesQuery {
66 pub(crate) sort: Sort,
68 pub(crate) per_page: u64,
70 pub(crate) page: u64,
72 pub(crate) user_id: Option<u64>,
73 pub(crate) team_id: Option<u64>,
74 pub(crate) category: Option<String>,
79 pub(crate) search: Option<String>,
81 pub(crate) ids: Option<Vec<String>>,
83}
84
85impl CratesQuery {
86 pub(crate) fn build(&self, mut q: url::form_urlencoded::Serializer<'_, url::UrlQuery<'_>>) {
87 q.append_pair("page", &self.page.to_string());
88 q.append_pair("per_page", &self.per_page.to_string());
89 q.append_pair("sort", self.sort.to_str());
90 if let Some(id) = self.user_id {
91 q.append_pair("user_id", &id.to_string());
92 }
93 if let Some(id) = self.team_id {
94 q.append_pair("team_id", &id.to_string());
95 }
96 if let Some(search) = &self.search {
97 q.append_pair("q", search);
98 }
99 if let Some(cat) = &self.category {
100 q.append_pair("category", cat);
101 }
102 if let Some(ids) = &self.ids {
103 for id in ids {
104 q.append_pair("ids[]", id);
105 }
106 }
107 }
108}
109
110impl CratesQuery {
111 pub fn builder() -> CratesQueryBuilder {
113 CratesQueryBuilder::new()
114 }
115
116 pub fn sort(&self) -> &Sort {
118 &self.sort
119 }
120
121 pub fn set_sort(&mut self, sort: Sort) {
123 self.sort = sort;
124 }
125
126 pub fn page_size(&self) -> u64 {
128 self.per_page
129 }
130
131 pub fn set_page_size(&mut self, per_page: u64) {
133 self.per_page = per_page;
134 }
135
136 pub fn page(&self) -> u64 {
138 self.page
139 }
140
141 pub fn set_page(&mut self, page: u64) {
143 self.page = page;
144 }
145
146 pub fn user_id(&self) -> Option<u64> {
148 self.user_id
149 }
150
151 pub fn set_user_id(&mut self, user_id: Option<u64>) {
153 self.user_id = user_id;
154 }
155
156 pub fn team_id(&self) -> Option<u64> {
158 self.team_id
159 }
160
161 pub fn set_team_id(&mut self, team_id: Option<u64>) {
163 self.team_id = team_id;
164 }
165
166 pub fn category(&self) -> Option<&String> {
168 self.category.as_ref()
169 }
170
171 pub fn set_category(&mut self, category: Option<String>) {
173 self.category = category;
174 }
175
176 pub fn search(&self) -> Option<&String> {
178 self.search.as_ref()
179 }
180
181 pub fn set_search(&mut self, search: Option<String>) {
183 self.search = search;
184 }
185
186 pub fn ids(&self) -> Option<&Vec<String>> {
188 self.ids.as_ref()
189 }
190
191 pub fn set_ids(&mut self, ids: Option<Vec<String>>) {
193 self.ids = ids;
194 }
195}
196
197impl Default for CratesQuery {
198 fn default() -> Self {
199 Self {
200 sort: Sort::RecentUpdates,
201 per_page: 30,
202 page: 1,
203 user_id: None,
204 team_id: None,
205 category: None,
206 search: None,
207 ids: None,
208 }
209 }
210}
211
212pub struct CratesQueryBuilder {
214 query: CratesQuery,
215}
216
217impl CratesQueryBuilder {
218 #[must_use]
220 pub fn new() -> Self {
221 Self {
222 query: CratesQuery::default(),
223 }
224 }
225
226 #[must_use]
228 pub fn sort(mut self, sort: Sort) -> Self {
229 self.query.sort = sort;
230 self
231 }
232
233 #[must_use]
235 pub fn page(mut self, page: u64) -> Self {
236 self.query.page = page;
237 self
238 }
239
240 #[must_use]
242 pub fn page_size(mut self, size: u64) -> Self {
243 self.query.per_page = size;
244 self
245 }
246
247 #[must_use]
249 pub fn user_id(mut self, user_id: u64) -> Self {
250 self.query.user_id = Some(user_id);
251 self
252 }
253
254 #[must_use]
256 pub fn team_id(mut self, team_id: u64) -> Self {
257 self.query.team_id = Some(team_id);
258 self
259 }
260
261 #[must_use]
266 pub fn category(mut self, category: impl Into<String>) -> Self {
267 self.query.category = Some(category.into());
268 self
269 }
270
271 #[must_use]
273 pub fn search(mut self, search: impl Into<String>) -> Self {
274 self.query.search = Some(search.into());
275 self
276 }
277
278 #[must_use]
280 pub fn ids(mut self, ids: Vec<String>) -> Self {
281 self.query.ids = Some(ids);
282 self
283 }
284
285 #[must_use]
287 pub fn build(self) -> CratesQuery {
288 self.query
289 }
290}
291
292impl Default for CratesQueryBuilder {
293 fn default() -> Self {
294 Self::new()
295 }
296}
297
298#[derive(Serialize, Deserialize, Debug, Clone)]
300pub struct Meta {
301 pub total: u64,
303}
304
305#[derive(Serialize, Deserialize, Debug, Clone)]
307#[allow(missing_docs)]
308pub struct CrateLinks {
309 pub owner_team: String,
310 pub owner_user: String,
311 pub owners: String,
312 pub reverse_dependencies: String,
313 pub version_downloads: String,
314 pub versions: Option<String>,
315}
316
317#[derive(Serialize, Deserialize, Debug, Clone)]
319#[allow(missing_docs)]
320pub struct Crate {
321 pub id: String,
322 pub name: String,
323 pub description: Option<String>,
324 #[deprecated(
326 since = "0.8.1",
327 note = "This field is always empty. The license is only available on a specific `Version` of a crate or on `FullCrate`. This field will be removed in the next minor version bump."
328 )]
329 pub license: Option<String>,
330 pub documentation: Option<String>,
331 pub homepage: Option<String>,
332 pub repository: Option<String>,
333 pub downloads: u64,
336 pub recent_downloads: Option<u64>,
337 pub categories: Option<Vec<String>>,
339 pub keywords: Option<Vec<String>>,
341 pub versions: Option<Vec<u64>>,
342 pub max_version: String,
343 pub max_stable_version: Option<String>,
344 pub links: CrateLinks,
345 pub created_at: DateTime<Utc>,
346 pub updated_at: DateTime<Utc>,
347 pub exact_match: Option<bool>,
348}
349
350#[derive(Serialize, Deserialize, Debug, Clone)]
352#[allow(missing_docs)]
353pub struct CratesPage {
354 pub crates: Vec<Crate>,
355 #[serde(default)]
356 pub versions: Vec<Version>,
357 #[serde(default)]
358 pub keywords: Vec<Keyword>,
359 #[serde(default)]
360 pub categories: Vec<Category>,
361 pub meta: Meta,
362}
363
364#[derive(Serialize, Deserialize, Debug, Clone)]
366#[allow(missing_docs)]
367pub struct VersionLinks {
368 #[deprecated(
369 since = "0.7.1",
370 note = "This field was removed from the API and will always be empty. Will be removed in 0.8.0."
371 )]
372 #[serde(default)]
373 pub authors: String,
374 pub dependencies: String,
375 pub version_downloads: String,
376}
377
378#[derive(Serialize, Deserialize, Debug, Clone)]
380#[allow(missing_docs)]
381pub struct AuditAction {
382 action: String,
384 time: DateTime<Utc>,
385 user: User,
386}
387
388#[derive(Serialize, Deserialize, Debug, Clone)]
390#[allow(missing_docs)]
391pub struct Version {
392 #[serde(rename = "crate")]
393 pub crate_name: String,
394 pub created_at: DateTime<Utc>,
395 pub updated_at: DateTime<Utc>,
396 pub dl_path: String,
397 pub downloads: u64,
398 pub features: HashMap<String, Vec<String>>,
399 pub id: u64,
400 pub num: String,
401 pub yanked: bool,
402 pub license: Option<String>,
403 pub readme_path: Option<String>,
404 pub links: VersionLinks,
405 pub crate_size: Option<u64>,
406 pub published_by: Option<User>,
407 pub rust_version: Option<String>,
408 #[serde(default)]
409 pub audit_actions: Vec<AuditAction>,
410 pub checksum: String,
411}
412
413#[derive(Serialize, Deserialize, Debug, Clone)]
415#[allow(missing_docs)]
416pub struct Category {
417 pub category: String,
418 pub crates_cnt: u64,
419 pub created_at: DateTime<Utc>,
420 pub description: String,
421 pub id: String,
422 pub slug: String,
423}
424
425#[derive(Serialize, Deserialize, Debug, Clone)]
427#[allow(missing_docs)]
428pub struct Keyword {
429 pub id: String,
430 pub keyword: String,
431 pub crates_cnt: u64,
432 pub created_at: DateTime<Utc>,
433}
434
435#[derive(Serialize, Deserialize, Debug, Clone)]
437#[allow(missing_docs)]
438pub struct CrateResponse {
439 pub categories: Vec<Category>,
440 #[serde(rename = "crate")]
441 pub crate_data: Crate,
442 pub keywords: Vec<Keyword>,
443 pub versions: Vec<Version>,
444}
445
446#[derive(Serialize, Deserialize, Debug, Clone)]
448#[allow(missing_docs)]
449pub struct Summary {
450 pub just_updated: Vec<Crate>,
451 pub most_downloaded: Vec<Crate>,
452 pub new_crates: Vec<Crate>,
453 pub most_recently_downloaded: Vec<Crate>,
454 pub num_crates: u64,
455 pub num_downloads: u64,
456 pub popular_categories: Vec<Category>,
457 pub popular_keywords: Vec<Keyword>,
458}
459
460#[derive(Serialize, Deserialize, Debug, Clone)]
462#[allow(missing_docs)]
463pub struct VersionDownloads {
464 pub date: NaiveDate,
465 pub downloads: u64,
466 pub version: u64,
467}
468
469#[derive(Serialize, Deserialize, Debug, Clone)]
472#[allow(missing_docs)]
473pub struct ExtraDownloads {
474 pub date: NaiveDate,
475 pub downloads: u64,
476}
477
478#[derive(Serialize, Deserialize, Debug, Clone)]
480#[allow(missing_docs)]
481pub struct CrateDownloadsMeta {
482 pub extra_downloads: Vec<ExtraDownloads>,
483}
484
485#[derive(Serialize, Deserialize, Debug, Clone)]
487#[allow(missing_docs)]
488pub struct CrateDownloads {
489 pub version_downloads: Vec<VersionDownloads>,
490 pub meta: CrateDownloadsMeta,
491}
492
493#[derive(Serialize, Deserialize, Debug, Clone)]
495#[allow(missing_docs)]
496pub struct User {
497 pub avatar: Option<String>,
498 pub email: Option<String>,
499 pub id: u64,
500 pub kind: Option<String>,
501 pub login: String,
502 pub name: Option<String>,
503 pub url: String,
504}
505
506#[derive(Serialize, Deserialize, Debug, Clone)]
508#[allow(missing_docs)]
509pub struct AuthorsMeta {
510 pub names: Vec<String>,
511}
512
513#[derive(Serialize, Deserialize, Debug, Clone)]
515#[allow(missing_docs)]
516pub(crate) struct AuthorsResponse {
517 pub meta: AuthorsMeta,
518}
519
520#[derive(Serialize, Deserialize, Debug, Clone)]
522#[allow(missing_docs)]
523pub struct Authors {
524 pub names: Vec<String>,
525}
526
527#[derive(Serialize, Deserialize, Debug, Clone)]
529#[allow(missing_docs)]
530pub struct Owners {
531 pub users: Vec<User>,
532}
533
534#[derive(Serialize, Deserialize, Debug, Clone)]
537#[allow(missing_docs)]
538pub struct Dependency {
539 pub crate_id: String,
540 pub default_features: bool,
541 pub downloads: u64,
542 pub features: Vec<String>,
543 pub id: u64,
544 pub kind: String,
545 pub optional: bool,
546 pub req: String,
547 pub target: Option<String>,
548 pub version_id: u64,
549}
550
551#[derive(Serialize, Deserialize, Debug, Clone)]
553#[allow(missing_docs)]
554pub struct Dependencies {
555 pub dependencies: Vec<Dependency>,
556}
557
558#[derive(Serialize, Deserialize, Debug, Clone)]
560#[allow(missing_docs)]
561pub struct ReverseDependency {
562 pub crate_version: Version,
563 pub dependency: Dependency,
564}
565
566#[derive(Serialize, Deserialize, Debug, Clone)]
568pub(super) struct ReverseDependenciesAsReceived {
569 pub dependencies: Vec<Dependency>,
570 pub versions: Vec<Version>,
571 pub meta: Meta,
572}
573
574#[derive(Serialize, Deserialize, Debug, Clone)]
576#[allow(missing_docs)]
577pub struct ReverseDependencies {
578 pub dependencies: Vec<ReverseDependency>,
579 pub meta: Meta,
580}
581
582impl ReverseDependencies {
583 pub(crate) fn extend(&mut self, rdeps: ReverseDependenciesAsReceived) {
585 for d in rdeps.dependencies {
586 for v in &rdeps.versions {
587 if v.id == d.version_id {
588 self.dependencies.push(ReverseDependency {
593 crate_version: v.clone(),
594 dependency: d.clone(),
595 });
596 }
597 }
598 }
599 }
600}
601
602#[derive(Serialize, Deserialize, Debug, Clone)]
604#[allow(missing_docs)]
605pub struct FullVersion {
606 #[serde(rename = "crate")]
607 pub crate_name: String,
608 pub created_at: DateTime<Utc>,
609 pub updated_at: DateTime<Utc>,
610 pub dl_path: String,
611 pub downloads: u64,
612 pub features: HashMap<String, Vec<String>>,
613 pub id: u64,
614 pub num: String,
615 pub yanked: bool,
616 pub license: Option<String>,
617 pub readme_path: Option<String>,
618 pub links: VersionLinks,
619 pub crate_size: Option<u64>,
620 pub published_by: Option<User>,
621 pub rust_version: Option<String>,
622 #[serde(default)]
623 pub audit_actions: Vec<AuditAction>,
624
625 pub author_names: Vec<String>,
626 pub dependencies: Vec<Dependency>,
627 pub checksum: String,
628}
629
630impl FullVersion {
631 pub fn from_parts(version: Version, authors: Authors, dependencies: Vec<Dependency>) -> Self {
633 FullVersion {
634 crate_name: version.crate_name,
635 created_at: version.created_at,
636 updated_at: version.updated_at,
637 dl_path: version.dl_path,
638 downloads: version.downloads,
639 features: version.features,
640 id: version.id,
641 num: version.num,
642 yanked: version.yanked,
643 license: version.license,
644 links: version.links,
645 readme_path: version.readme_path,
646 crate_size: version.crate_size,
647 published_by: version.published_by,
648 rust_version: version.rust_version,
649 audit_actions: version.audit_actions,
650
651 author_names: authors.names,
652 dependencies,
653 checksum: version.checksum,
654 }
655 }
656}
657
658#[derive(Serialize, Deserialize, Debug, Clone)]
660#[allow(missing_docs)]
661pub struct FullCrate {
662 pub id: String,
663 pub name: String,
664 pub description: Option<String>,
665 pub license: Option<String>,
666 pub documentation: Option<String>,
667 pub homepage: Option<String>,
668 pub repository: Option<String>,
669 pub total_downloads: u64,
670 pub recent_downloads: Option<u64>,
671 pub max_version: String,
672 pub max_stable_version: Option<String>,
673 pub created_at: DateTime<Utc>,
674 pub updated_at: DateTime<Utc>,
675
676 pub categories: Vec<Category>,
677 pub keywords: Vec<Keyword>,
678 pub downloads: CrateDownloads,
679 pub owners: Vec<User>,
680 pub reverse_dependencies: ReverseDependencies,
681
682 pub versions: Vec<FullVersion>,
683}
684
685#[derive(Serialize, Deserialize, Debug, Clone)]
686pub(crate) struct UserResponse {
687 pub user: User,
688}