use chrono::{DateTime, Utc};
use macros::make_path_parts;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub mod curation;
pub mod report;
pub use report::{ReportId, ResourceReport};
use crate::api::endpoints::PathPart;
use crate::domain::UpdateNonNullable;
use super::{
additional_resource::AdditionalResource,
asset::{DraftOrLive, OrderBy, PrivacyLevel, UserOrMe},
category::CategoryId,
meta::{AffiliationId, AgeRangeId, ResourceTypeId},
module::LiteModule,
user::UserId,
};
wrap_uuid! {
pub struct ResourceId
}
make_path_parts!(ResourceCreatePath => "/v1/resource");
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ResourceResponse {
pub id: ResourceId,
pub published_at: Option<DateTime<Utc>>,
pub creator_id: Option<UserId>,
pub author_id: Option<UserId>,
pub author_name: Option<String>,
pub likes: i64,
pub views: i64,
pub live_up_to_date: bool,
pub is_liked: bool,
pub resource_data: ResourceData,
pub admin_data: ResourceAdminData,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ResourceData {
pub draft_or_live: DraftOrLive,
pub display_name: String,
pub cover: Option<LiteModule>,
pub age_ranges: Vec<AgeRangeId>,
pub affiliations: Vec<AffiliationId>,
pub language: String,
pub categories: Vec<CategoryId>,
pub description: String,
pub created_at: DateTime<Utc>,
pub last_edited: Option<DateTime<Utc>>,
pub privacy_level: PrivacyLevel,
pub locked: bool,
pub other_keywords: String,
pub translated_keywords: String,
#[serde(default)]
pub translated_description: HashMap<String, String>,
pub additional_resources: Vec<AdditionalResource>,
}
#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct ResourceCreateRequest {
#[serde(default)]
pub display_name: String,
#[serde(default)]
pub description: String,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub age_ranges: Vec<AgeRangeId>,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub affiliations: Vec<AffiliationId>,
#[serde(default)]
pub language: String,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub categories: Vec<CategoryId>,
}
make_path_parts!(ResourceGetLivePath => "/v1/resource/{}/live" => ResourceId);
make_path_parts!(ResourceGetDraftPath => "/v1/resource/{}/draft" => ResourceId);
make_path_parts!(ResourceUpdateDraftDataPath => "/v1/resource/{}" => ResourceId);
#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct ResourceUpdateDraftDataRequest {
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub display_name: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub language: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub categories: Option<Vec<CategoryId>>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub age_ranges: Option<Vec<AgeRangeId>>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub affiliations: Option<Vec<AffiliationId>>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub author_id: Option<UserId>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub privacy_level: Option<PrivacyLevel>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub other_keywords: Option<String>,
}
make_path_parts!(ResourcePublishPath => "/v1/resource/{}/draft/publish" => ResourceId);
make_path_parts!(ResourceBrowsePath => "/v1/resource/browse");
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct ResourceBrowseQuery {
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub is_published: Option<bool>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub author_id: Option<UserOrMe>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub page: Option<u32>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub draft_or_live: Option<DraftOrLive>,
#[serde(default)]
#[serde(deserialize_with = "super::from_csv")]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub privacy_level: Vec<PrivacyLevel>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub blocked: Option<bool>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub page_limit: Option<u32>,
#[serde(default)]
#[serde(serialize_with = "super::csv_encode_uuids")]
#[serde(deserialize_with = "super::from_csv")]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub resource_types: Vec<ResourceTypeId>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub order_by: Option<OrderBy>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct ResourceBrowseResponse {
pub resources: Vec<ResourceResponse>,
pub pages: u32,
pub total_resource_count: u64,
}
make_path_parts!(ResourceSearchPath => "/v1/resource");
pub struct DeleteUserResources {
pub resource_id: ResourceId,
}
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct ResourceSearchQuery {
#[serde(default)]
#[serde(skip_serializing_if = "String::is_empty")]
pub q: String,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub page: Option<u32>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub language: Option<String>,
#[serde(default)]
#[serde(serialize_with = "super::csv_encode_uuids")]
#[serde(deserialize_with = "super::from_csv")]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub age_ranges: Vec<AgeRangeId>,
#[serde(default)]
#[serde(serialize_with = "super::csv_encode_uuids")]
#[serde(deserialize_with = "super::from_csv")]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub affiliations: Vec<AffiliationId>,
#[serde(default)]
#[serde(serialize_with = "super::csv_encode_uuids")]
#[serde(deserialize_with = "super::from_csv")]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub resource_types: Vec<ResourceTypeId>,
#[serde(default)]
#[serde(serialize_with = "super::csv_encode_uuids")]
#[serde(deserialize_with = "super::from_csv")]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub categories: Vec<CategoryId>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub is_published: Option<bool>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub author_id: Option<UserOrMe>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub author_name: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub other_keywords: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub translated_keywords: Option<String>,
#[serde(default)]
#[serde(deserialize_with = "super::from_csv")]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub privacy_level: Vec<PrivacyLevel>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub blocked: Option<bool>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub page_limit: Option<u32>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub is_rated: Option<bool>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct ResourceSearchResponse {
pub resources: Vec<ResourceResponse>,
pub pages: u32,
pub total_resource_count: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct ResourceIdResponse {
pub id: ResourceId,
}
make_path_parts!(ResourceClonePath => "/v1/resource/{}/clone" => ResourceId);
make_path_parts!(ResourceDeletePath => "/v1/resource/{}" => ResourceId);
make_path_parts!(ResourceDeleteAllPath => "/v1/resource");
make_path_parts!(ResourceCoverPath => "/v1/resource/{}/cover" => ResourceId);
make_path_parts!(ResourceCountPath => "/v1/resource/count");
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct ResourceCountResponse {
pub total_count: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ResourceLikedResponse {
pub is_liked: bool,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct ResourceAdminData {
#[serde(default)]
pub rating: Option<ResourceRating>,
pub blocked: bool,
pub curated: bool,
pub premium: bool,
}
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct ResourceUpdateAdminDataRequest {
#[serde(default, skip_serializing_if = "UpdateNonNullable::is_keep")]
pub rating: UpdateNonNullable<ResourceRating>,
#[serde(default, skip_serializing_if = "UpdateNonNullable::is_keep")]
pub blocked: UpdateNonNullable<bool>,
#[serde(default, skip_serializing_if = "UpdateNonNullable::is_keep")]
pub curated: UpdateNonNullable<bool>,
#[serde(default, skip_serializing_if = "UpdateNonNullable::is_keep")]
pub premium: UpdateNonNullable<bool>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "backend", derive(sqlx::Type))]
#[serde(rename_all = "camelCase")]
#[repr(i16)]
pub enum ResourceRating {
#[allow(missing_docs)]
One = 1,
#[allow(missing_docs)]
Two = 2,
#[allow(missing_docs)]
Three = 3,
}
impl TryFrom<u8> for ResourceRating {
type Error = ();
fn try_from(num: u8) -> Result<Self, Self::Error> {
match num {
1 => Ok(Self::One),
2 => Ok(Self::Two),
3 => Ok(Self::Three),
_ => Err(()),
}
}
}
make_path_parts!(ResourceLikePath => "/v1/resource/{}/like" => ResourceId);
make_path_parts!(ResourceUnlikePath => "/v1/resource/{}/unlike" => ResourceId);
make_path_parts!(ResourceLikedPath => "/v1/resource/{}/like" => ResourceId);
make_path_parts!(ListLikedPath => "/v1/resource/likes");
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct ListLikedRequest {
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub page: Option<u32>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub page_limit: Option<u32>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct ListLikedResponse {
pub resources: Vec<ResourceResponse>,
pub total_resource_count: u64,
}
make_path_parts!(ResourceViewPath => "/v1/resource/{}/view" => ResourceId);
make_path_parts!(ResourceAdminDataUpdatePath => "/v1/resource/{}/admin" => ResourceId);