use crate::v2::errors::SmugMugError;
use crate::v2::macros::{
obj_from_url, obj_update_from_uri, obj_update_from_url, objs_from_id_slice,
stream_children_from_url,
};
use crate::v2::parsers::{from_privacy, is_none_or_empty_str};
use crate::v2::{API_ORIGIN, Client, Image, Pages, PrivacyLevel};
use async_stream::try_stream;
use chrono::{DateTime, Utc};
use futures::Stream;
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
pub struct Album {
#[serde(skip)]
pub(crate) client: Option<Client>,
#[serde(rename = "Uri")]
pub uri: String,
#[serde(rename = "AlbumKey")]
pub album_key: String,
#[serde(rename = "AllowDownloads")]
pub do_allow_downloads: bool,
#[serde(rename = "Name")]
pub name: String,
#[serde(rename = "Description", skip_serializing_if = "is_none_or_empty_str")]
pub description: Option<String>,
#[serde(rename = "PasswordHint", skip_serializing_if = "is_none_or_empty_str")]
pub password_hint: Option<String>,
#[serde(rename = "UrlName")]
pub url_name: String,
#[serde(rename = "WebUri")]
pub web_uri: String,
#[serde(rename = "UploadKey", skip_serializing_if = "is_none_or_empty_str")]
pub upload_key: Option<String>,
#[serde(rename = "ImageCount")]
pub image_count: u64,
#[serde(rename = "TotalSizes")]
pub total_sizes: Option<u64>,
#[serde(rename = "OriginalSizes")]
pub original_sizes: Option<u64>,
#[serde(
default,
rename = "Privacy",
deserialize_with = "from_privacy",
skip_serializing_if = "Option::is_none"
)]
pub privacy: Option<PrivacyLevel>,
#[serde(rename = "Date", skip_serializing_if = "Option::is_none")]
pub date_created: Option<DateTime<Utc>>,
#[serde(rename = "ImagesLastUpdated")]
pub images_last_updated: DateTime<Utc>,
#[serde(rename = "LastUpdated")]
pub last_updated: DateTime<Utc>,
#[serde(rename = "Uris")]
uris: AlbumUris,
}
impl Album {
const BASE_URI: &'static str = "/api/v2/album/";
pub async fn from_url(client: Client, url: &str) -> Result<Self, SmugMugError> {
obj_from_url!(client, url, AlbumResponse, album)
}
pub async fn from_id(client: Client, id: &str) -> Result<Self, SmugMugError> {
let req_url = url::Url::parse(API_ORIGIN)?
.join(Self::BASE_URI)?
.join(id)?;
Self::from_url(client, req_url.as_str()).await
}
pub async fn from_id_slice(
client: Client,
id_list: &[&str],
) -> Result<Vec<Self>, SmugMugError> {
objs_from_id_slice!(client, id_list, Self::BASE_URI, AlbumsResponse, albums)
}
pub fn images(&self) -> Result<impl Stream<Item = Result<Image, SmugMugError>>, SmugMugError> {
self.images_with_client(
self.client
.as_ref()
.ok_or(SmugMugError::ClientNotFound())
.unwrap()
.clone(),
)
}
pub fn images_with_client(
&self,
client: Client,
) -> Result<impl Stream<Item = Result<Image, SmugMugError>>, SmugMugError> {
let params: Vec<(&str, &str)> = Vec::new();
Ok(stream_children_from_url!(
client,
self.uris.album_images.as_ref(),
¶ms,
AlbumImagesResponse,
images
))
}
pub async fn update_album_data_with_client(
&self,
client: Client,
data: Vec<u8>,
) -> Result<Album, SmugMugError> {
obj_update_from_uri!(client, self.uri.as_str(), data, AlbumResponse, album)
}
pub async fn update_album_data_fields(&self, data: Vec<u8>) -> Result<Album, SmugMugError> {
let client = self
.client
.as_ref()
.ok_or(SmugMugError::ClientNotFound())?
.clone();
self.update_album_data_with_client(client, data).await
}
pub async fn update_album_data_with_client_from_id(
client: Client,
data: Vec<u8>,
id: &str,
) -> Result<Album, SmugMugError> {
let req_url = url::Url::parse(API_ORIGIN)?
.join(Self::BASE_URI)?
.join(id)?;
obj_update_from_url!(client, req_url.as_str(), data, AlbumResponse, album)
}
pub async fn clear_upload_key_with_client(
&self,
client: Client,
) -> Result<Album, SmugMugError> {
let data = serde_json::to_vec(&json!({"UploadKey": ""}))?;
self.update_album_data_with_client(client, data).await
}
pub async fn clear_upload_key(&self) -> Result<Album, SmugMugError> {
let client = self
.client
.as_ref()
.ok_or(SmugMugError::ClientNotFound())?
.clone();
self.clear_upload_key_with_client(client).await
}
pub async fn set_upload_key_with_client(
&self,
client: Client,
upload_key: &str,
) -> Result<Album, SmugMugError> {
let data = serde_json::to_vec(&json!({"UploadKey": upload_key}))?;
self.update_album_data_with_client(client, data).await
}
pub async fn set_upload_key(&self, upload_key: &str) -> Result<Album, SmugMugError> {
let client = self
.client
.as_ref()
.ok_or(SmugMugError::ClientNotFound())?
.clone();
self.set_upload_key_with_client(client, upload_key).await
}
}
impl PartialEq for Album {
fn eq(&self, other: &Self) -> bool {
self.album_key == other.album_key
}
}
impl Eq for Album {}
impl Hash for Album {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
state.write(self.album_key.as_bytes());
let _ = state.finish();
}
}
impl PartialOrd for Album {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.album_key.cmp(&other.album_key))
}
}
impl Ord for Album {
fn cmp(&self, other: &Self) -> Ordering {
self.album_key.cmp(&other.album_key)
}
}
impl std::fmt::Display for Album {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "name: {}, id: {}", self.name, self.album_key)
}
}
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
struct AlbumUris {
#[serde(rename = "AlbumImages")]
album_images: Option<String>,
}
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
pub struct CreateAlbumProps {
#[serde(rename = "Name")]
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "Description")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "PasswordHint")]
pub password_hint: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "UrlName")]
pub url_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "WebUri")]
pub web_uri: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default, rename = "UploadKey")]
pub upload_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "Privacy")]
pub privacy: Option<PrivacyLevel>,
}
#[derive(Deserialize, Debug)]
pub(crate) struct AlbumResponse {
#[serde(rename = "Album")]
pub(crate) album: Album,
}
#[derive(Deserialize, Debug)]
struct AlbumsResponse {
#[serde(rename = "Album")]
albums: Vec<Album>,
}
#[derive(Deserialize, Debug)]
struct AlbumImagesResponse {
#[serde(rename = "AlbumImage")]
images: Vec<Image>,
#[serde(rename = "Pages")]
pages: Option<Pages>,
}