use chrono::{DateTime, NaiveDate, Utc};
use serde_derive::*;
use std::collections::HashMap;
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct ApiErrors {
pub errors: Vec<ApiError>,
}
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct ApiError {
pub detail: Option<String>,
}
impl std::fmt::Display for ApiError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
self.detail.as_deref().unwrap_or("Unknown API Error")
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Sort {
Alphabetical,
Relevance,
Downloads,
RecentDownloads,
RecentUpdates,
NewlyAdded,
}
impl Sort {
pub(crate) fn to_str(&self) -> &str {
match self {
Self::Alphabetical => "alpha",
Self::Relevance => "relevance",
Self::Downloads => "downloads",
Self::RecentDownloads => "recent-downloads",
Self::RecentUpdates => "recent-updates",
Self::NewlyAdded => "new",
}
}
}
#[derive(Clone, Debug)]
pub struct CratesQuery {
pub(crate) sort: Sort,
pub(crate) per_page: u64,
pub(crate) page: u64,
pub(crate) user_id: Option<u64>,
pub(crate) category: Option<String>,
pub(crate) search: Option<String>,
}
impl CratesQuery {
pub(crate) fn build(&self, mut q: url::form_urlencoded::Serializer<'_, url::UrlQuery<'_>>) {
q.append_pair("page", &self.page.to_string());
q.append_pair("per_page", &self.per_page.to_string());
q.append_pair("sort", self.sort.to_str());
if let Some(id) = self.user_id {
q.append_pair("user_id", &id.to_string());
}
if let Some(search) = &self.search {
q.append_pair("q", search);
}
if let Some(cat) = &self.category {
q.append_pair("category", cat);
}
}
}
impl CratesQuery {
pub fn builder() -> CratesQueryBuilder {
CratesQueryBuilder::new()
}
pub fn sort(&self) -> &Sort {
&self.sort
}
pub fn set_sort(&mut self, sort: Sort) {
self.sort = sort;
}
pub fn page_size(&self) -> u64 {
self.per_page
}
pub fn set_page_size(&mut self, per_page: u64) {
self.per_page = per_page;
}
pub fn page(&self) -> u64 {
self.page
}
pub fn set_page(&mut self, page: u64) {
self.page = page;
}
pub fn user_id(&self) -> Option<u64> {
self.user_id
}
pub fn set_user_id(&mut self, user_id: Option<u64>) {
self.user_id = user_id;
}
pub fn category(&self) -> Option<&String> {
self.category.as_ref()
}
pub fn set_category(&mut self, category: Option<String>) {
self.category = category;
}
pub fn search(&self) -> Option<&String> {
self.search.as_ref()
}
pub fn set_search(&mut self, search: Option<String>) {
self.search = search;
}
}
impl Default for CratesQuery {
fn default() -> Self {
Self {
sort: Sort::RecentUpdates,
per_page: 30,
page: 1,
user_id: None,
category: None,
search: None,
}
}
}
pub struct CratesQueryBuilder {
query: CratesQuery,
}
impl CratesQueryBuilder {
#[must_use]
pub fn new() -> Self {
Self {
query: CratesQuery::default(),
}
}
#[must_use]
pub fn sort(mut self, sort: Sort) -> Self {
self.query.sort = sort;
self
}
#[must_use]
pub fn page(mut self, page: u64) -> Self {
self.query.page = page;
self
}
#[must_use]
pub fn page_size(mut self, size: u64) -> Self {
self.query.per_page = size;
self
}
#[must_use]
pub fn user_id(mut self, user_id: u64) -> Self {
self.query.user_id = Some(user_id);
self
}
#[must_use]
pub fn category(mut self, category: impl Into<String>) -> Self {
self.query.category = Some(category.into());
self
}
#[must_use]
pub fn search(mut self, search: impl Into<String>) -> Self {
self.query.search = Some(search.into());
self
}
#[must_use]
pub fn build(self) -> CratesQuery {
self.query
}
}
impl Default for CratesQueryBuilder {
fn default() -> Self {
Self::new()
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Meta {
pub total: u64,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct CrateLinks {
pub owner_team: String,
pub owner_user: String,
pub owners: String,
pub reverse_dependencies: String,
pub version_downloads: String,
pub versions: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct Crate {
pub id: String,
pub name: String,
pub description: Option<String>,
#[deprecated(
since = "0.8.1",
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."
)]
pub license: Option<String>,
pub documentation: Option<String>,
pub homepage: Option<String>,
pub repository: Option<String>,
pub downloads: u64,
pub recent_downloads: Option<u64>,
pub categories: Option<Vec<String>>,
pub keywords: Option<Vec<String>>,
pub versions: Option<Vec<u64>>,
pub max_version: String,
pub max_stable_version: Option<String>,
pub links: CrateLinks,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub exact_match: Option<bool>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct CratesPage {
pub crates: Vec<Crate>,
#[serde(default)]
pub versions: Vec<Version>,
#[serde(default)]
pub keywords: Vec<Keyword>,
#[serde(default)]
pub categories: Vec<Category>,
pub meta: Meta,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct VersionLinks {
#[deprecated(
since = "0.7.1",
note = "This field was removed from the API and will always be empty. Will be removed in 0.8.0."
)]
#[serde(default)]
pub authors: String,
pub dependencies: String,
pub version_downloads: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct AuditAction {
action: String,
time: DateTime<Utc>,
user: User,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct Version {
#[serde(rename = "crate")]
pub crate_name: String,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub dl_path: String,
pub downloads: u64,
pub features: HashMap<String, Vec<String>>,
pub id: u64,
pub num: String,
pub yanked: bool,
pub license: Option<String>,
pub readme_path: Option<String>,
pub links: VersionLinks,
pub crate_size: Option<u64>,
pub published_by: Option<User>,
pub rust_version: Option<String>,
#[serde(default)]
pub audit_actions: Vec<AuditAction>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct Category {
pub category: String,
pub crates_cnt: u64,
pub created_at: DateTime<Utc>,
pub description: String,
pub id: String,
pub slug: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct Keyword {
pub id: String,
pub keyword: String,
pub crates_cnt: u64,
pub created_at: DateTime<Utc>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct CrateResponse {
pub categories: Vec<Category>,
#[serde(rename = "crate")]
pub crate_data: Crate,
pub keywords: Vec<Keyword>,
pub versions: Vec<Version>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct Summary {
pub just_updated: Vec<Crate>,
pub most_downloaded: Vec<Crate>,
pub new_crates: Vec<Crate>,
pub most_recently_downloaded: Vec<Crate>,
pub num_crates: u64,
pub num_downloads: u64,
pub popular_categories: Vec<Category>,
pub popular_keywords: Vec<Keyword>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct VersionDownloads {
pub date: NaiveDate,
pub downloads: u64,
pub version: u64,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct ExtraDownloads {
pub date: NaiveDate,
pub downloads: u64,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct CrateDownloadsMeta {
pub extra_downloads: Vec<ExtraDownloads>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct CrateDownloads {
pub version_downloads: Vec<VersionDownloads>,
pub meta: CrateDownloadsMeta,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct User {
pub avatar: Option<String>,
pub email: Option<String>,
pub id: u64,
pub kind: Option<String>,
pub login: String,
pub name: Option<String>,
pub url: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct AuthorsMeta {
pub names: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub(crate) struct AuthorsResponse {
pub meta: AuthorsMeta,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct Authors {
pub names: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct Owners {
pub users: Vec<User>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct Dependency {
pub crate_id: String,
pub default_features: bool,
pub downloads: u64,
pub features: Vec<String>,
pub id: u64,
pub kind: String,
pub optional: bool,
pub req: String,
pub target: Option<String>,
pub version_id: u64,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct Dependencies {
pub dependencies: Vec<Dependency>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct ReverseDependency {
pub crate_version: Version,
pub dependency: Dependency,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub(super) struct ReverseDependenciesAsReceived {
pub dependencies: Vec<Dependency>,
pub versions: Vec<Version>,
pub meta: Meta,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct ReverseDependencies {
pub dependencies: Vec<ReverseDependency>,
pub meta: Meta,
}
impl ReverseDependencies {
pub(crate) fn extend(&mut self, rdeps: ReverseDependenciesAsReceived) {
for d in rdeps.dependencies {
for v in &rdeps.versions {
if v.id == d.version_id {
self.dependencies.push(ReverseDependency {
crate_version: v.clone(),
dependency: d.clone(),
});
}
}
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct FullVersion {
#[serde(rename = "crate")]
pub crate_name: String,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub dl_path: String,
pub downloads: u64,
pub features: HashMap<String, Vec<String>>,
pub id: u64,
pub num: String,
pub yanked: bool,
pub license: Option<String>,
pub readme_path: Option<String>,
pub links: VersionLinks,
pub crate_size: Option<u64>,
pub published_by: Option<User>,
pub rust_version: Option<String>,
#[serde(default)]
pub audit_actions: Vec<AuditAction>,
pub author_names: Vec<String>,
pub dependencies: Vec<Dependency>,
}
impl FullVersion {
pub fn from_parts(version: Version, authors: Authors, dependencies: Vec<Dependency>) -> Self {
FullVersion {
crate_name: version.crate_name,
created_at: version.created_at,
updated_at: version.updated_at,
dl_path: version.dl_path,
downloads: version.downloads,
features: version.features,
id: version.id,
num: version.num,
yanked: version.yanked,
license: version.license,
links: version.links,
readme_path: version.readme_path,
crate_size: version.crate_size,
published_by: version.published_by,
rust_version: version.rust_version,
audit_actions: version.audit_actions,
author_names: authors.names,
dependencies,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(missing_docs)]
pub struct FullCrate {
pub id: String,
pub name: String,
pub description: Option<String>,
pub license: Option<String>,
pub documentation: Option<String>,
pub homepage: Option<String>,
pub repository: Option<String>,
pub total_downloads: u64,
pub recent_downloads: Option<u64>,
pub max_version: String,
pub max_stable_version: Option<String>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub categories: Vec<Category>,
pub keywords: Vec<Keyword>,
pub downloads: CrateDownloads,
pub owners: Vec<User>,
pub reverse_dependencies: ReverseDependencies,
pub versions: Vec<FullVersion>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub(crate) struct UserResponse {
pub user: User,
}