use serde::{Deserialize, Serialize}; use std::{collections::HashMap, sync::Arc}; use warp::{ filters::{body::BodyDeserializeError, cors::CorsForbidden}, http::Method, http::StatusCode, reject::Reject, Filter, Rejection, Reply, }; use tokio::sync::RwLock; #[derive(Deserialize, Serialize, Debug, Clone)] struct Person { id: PersonId, remote_id: String, name: String, handle: Option, avatar: Option, cover: Option, bio: Option, is_active: bool, is_blocked: bool, created_at: usize, modified_at: usize, deleted_at: Option, modified_by: PersonId, deleted_by: Option, last_seen: usize, shipping_addresses: Option>, emails: Option>, credentials: Option>, } #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] struct PersonId(String); #[derive(Deserialize, Serialize, Debug, Clone)] struct PersonShippingAddress { id: PersonShippingAddressId, person_id: PersonId, shipping_address: String, is_primary: bool, created_at: usize, modified_at: usize, deleted_at: Option, modified_by: PersonId, deleted_by: Option, } #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] struct PersonShippingAddressId(String); #[derive(Deserialize, Serialize, Debug, Clone)] struct PersonEmail { person_id: PersonId, email: String, is_verified: bool, is_primary: bool, } #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] struct PersonEmailId(String); #[derive(Deserialize, Serialize, Debug, Clone)] struct PersonCredentialProvider { id: PersonCredentialProviderId, name: String, r#type: String, // TODO: create a type for this config: String, } #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] struct PersonCredentialProviderId(String); #[derive(Deserialize, Serialize, Debug, Clone)] struct PersonCredential { id: PersonCredentialId, person_id: PersonId, provider_user_id: String, is_enabled: bool, } #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] struct PersonCredentialId(String); #[derive(Deserialize, Serialize, Debug, Clone)] struct Tag { id: TagId, host: String, tag: String, } #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] struct TagId(String); #[derive(Deserialize, Serialize, Debug, Clone)] struct Label { id: LabelId, name: String, description: String, website: String, is_enabled: bool, created_at: usize, modified_at: usize, deleted_at: Option, created_by: PersonId, modified_by: PersonId, deleted_by: Option, contacts: Option>, tags: Option>, artists: Option>, } #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] struct LabelId(String); #[derive(Deserialize, Serialize, Debug, Clone)] struct LabelContact { id: LabelContactId, label_id: LabelId, method: String, // TODO: create a type for this address: String, created_at: usize, modified_at: usize, deleted_at: Option, created_by: PersonId, modified_by: PersonId, deleted_by: Option, sort_order: usize, } #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] struct LabelContactId(String); #[derive(Deserialize, Serialize, Debug, Clone)] struct Artist { id: ArtistId, label_id: Option, name: String, bio: String, website: String, created_at: usize, modified_at: usize, deleted_at: Option, created_by: PersonId, modified_by: PersonId, deleted_by: Option, is_enabled: bool, is_public: bool, contacts: Option>, tags: Option>, } #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] struct ArtistId(String); #[derive(Deserialize, Serialize, Debug, Clone)] struct ArtistContact { id: ArtistContactId, artist_id: ArtistId, method: String, // TODO: create a type for this address: String, created_at: usize, modified_at: usize, deleted_at: Option, created_by: PersonId, modified_by: PersonId, deleted_by: Option, sort_order: usize, } #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] struct ArtistContactId(String); #[derive(Deserialize, Serialize, Debug, Clone)] struct Track { id: TrackId, title: String, description: String, duration: f64, is_public: bool, is_available: bool, preview_source: Option, // if preview_source == None, use source as preview source: String, price: usize, created_at: usize, modified_at: usize, deleted_at: Option, created_by: PersonId, modified_by: PersonId, deleted_by: Option, lyrics: Option, primary_artist: Artist, other_artists: Option>, tags: Option>, } #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] struct TrackId(String); #[derive(Deserialize, Serialize, Debug, Clone)] struct Album { id: AlbumId, title: String, description: String, tracks: Vec, is_public: bool, is_available: bool, preview_source: Option, source: String, price: usize, created_at: usize, modified_at: usize, deleted_at: Option, created_by: PersonId, modified_by: PersonId, deleted_by: Option, primary_artist: Option, other_artists: Option>, tags: Option>, } #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] struct AlbumId(String); #[derive(Deserialize, Serialize, Debug, Clone)] struct Playlist { id: PlaylistId, title: String, description: String, created_by: PersonId, created_at: usize, modified_at: usize, deleted_at: Option, is_public: bool, tracks: Option>, tags: Option>, } #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] struct PlaylistId(String); #[derive(Debug)] struct Pagination { start: usize, end: usize, } #[derive(Clone)] struct Datastore { persons: Arc>>, labels: Arc>>, artists: Arc>>, albums: Arc>>, tracks: Arc>>, } impl Datastore { fn new() -> Self { Datastore { persons: Arc::new(RwLock::new(HashMap::new())), labels: Arc::new(RwLock::new(HashMap::new())), artists: Arc::new(RwLock::new(HashMap::new())), albums: Arc::new(RwLock::new(HashMap::new())), tracks: Arc::new(RwLock::new(HashMap::new())), } } } #[derive(Debug)] enum Error { ParseError(std::num::ParseIntError), MissingParameters, } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match *self { Error::ParseError(ref err) => write!(f, "Cannot parse parameter: {}", err), Error::MissingParameters => write!(f, "Missing parameter"), } } } impl Reject for Error {} async fn return_error(r: Rejection) -> Result { if let Some(error) = r.find::() { Ok(warp::reply::with_status( error.to_string(), StatusCode::RANGE_NOT_SATISFIABLE, )) } else if let Some(error) = r.find::() { Ok(warp::reply::with_status( error.to_string(), StatusCode::FORBIDDEN, )) } else if let Some(error) = r.find::() { Ok(warp::reply::with_status( error.to_string(), StatusCode::UNPROCESSABLE_ENTITY, )) } else { Ok(warp::reply::with_status( "Route not found".to_string(), StatusCode::NOT_FOUND, )) } } fn extract_pagination(params: HashMap) -> Result { if params.contains_key("start") && params.contains_key("end") { return Ok(Pagination { start: params .get("start") .unwrap() .parse::() .map_err(Error::ParseError)?, end: params .get("end") .unwrap() .parse::() .map_err(Error::ParseError)?, }); } Err(Error::MissingParameters) } async fn get_persons( params: HashMap, datastore: Datastore, ) -> Result { if !params.is_empty() { let pagination = extract_pagination(params)?; let res: Vec = datastore.persons.read().await.values().cloned().collect(); let res = &res[pagination.start..pagination.end]; Ok(warp::reply::json(&res)) } else { let res: Vec = datastore.persons.read().await.values().cloned().collect(); Ok(warp::reply::json(&res)) } } async fn get_labels( params: HashMap, datastore: Datastore, ) -> Result { if !params.is_empty() { let pagination = extract_pagination(params)?; let res: Vec