forked from MountainTownTechnology/aural_isle
		
	adds initial structs and get routes for persons, labels, artists, albums, and tracks
This commit is contained in:
		
							
								
								
									
										935
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										935
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -9,4 +9,6 @@ tokio = { version = "1.2", features = ["full"] } | ||||
| warp = "0.3" | ||||
| rusty-money = "0.4.1" | ||||
| dotenv = "0.15.0" | ||||
| serde = { version = "1.0", features = ["derive"] } | ||||
| serde_json = "1.0" | ||||
|  | ||||
|  | ||||
| @ -8,4 +8,4 @@ You will need to initialize a sqlite database store: `touch aural_isle.sqlite.db | ||||
|  | ||||
| You will need to copy the example env file to .env: `cp env.example .env` | ||||
|  | ||||
| Then you should be able to run the initial database migrations: `sqlx migrate run`. (During initial dev, if you have already ran the initial migration, you will need to delete the sqlite database store and re-create it as above) | ||||
| Then you should be able to run the initial database migrations: `sqlx migrate run`. (During initial dev, if you have already run the initial migration, you will need to delete the sqlite database store and re-create it as above) | ||||
							
								
								
									
										24
									
								
								SCHEMA.md
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								SCHEMA.md
									
									
									
									
									
								
							| @ -24,7 +24,6 @@ CREATE TABLE Persons ( | ||||
| 	deleted_at INTEGER,               /* timestamp */ | ||||
| 	deleted_by TEXT(36),              /* UUIDv4 */ | ||||
| 	last_seen INTEGER,                /* timestamp */ | ||||
| 	shipping_address TEXT,            /* optional, should use \n between lines to keep localized format as needed */ | ||||
| 	CONSTRAINT Persons_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT Persons_FK FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Persons_FK_1 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| @ -37,6 +36,28 @@ CREATE INDEX Persons_is_blocked_IDX ON Persons (is_blocked); | ||||
| CREATE INDEX Persons_deleted_at_IDX ON Persons (deleted_at); | ||||
| ``` | ||||
|  | ||||
| # PersonShippingAddresses | ||||
| The PersonShippingAddresses table will contain zero or more shipping addresses as used by a Person | ||||
|  | ||||
| ``` sql | ||||
| CREATE TABLE IF NOT EXISTS PersonShippingAddresses ( | ||||
|     id TEXT(36),                        /* UUIDv4 */ | ||||
|     person_id TEXT(36),                 /* UUIDv4 */  | ||||
|     shipping_address TEXT,              /* mandatory, should use \n between lines to keep localized format as needed */  | ||||
|     is_primary INTEGER DEFAULT (0),     /* bool, default false */  | ||||
|     created_at INTEGER,                 /* timestamp */  | ||||
|     modified_at INTEGER,                /* timestamp */  | ||||
|     modified_by TEXT(36),               /* UUIDv4 */  | ||||
|     deleted_at INTEGER,                 /* timestamp */  | ||||
|     deleted_by TEXT(36),                /* UUIDv4 */  | ||||
|     CONSTRAINT PersonShippingAddresses_PK PRIMARY KEY (id), | ||||
|     CONSTRAINT PersonShippingAddresses_FK FOREIGN KEY (person_id) REFERENCES Persons(id), | ||||
|     CONSTRAINT PersonShippingAddresses_FK_1 FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
|     CONSTRAINT PersonShippingAddresses_FK_2 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE UNIQUE INDEX IF NOT EXISTS PersonShippingAddresses_is_primary_IDX ON PersonShippingAddresses (is_primary); | ||||
| ``` | ||||
|  | ||||
| # PersonEmails Table | ||||
| The PersonEmails table is a one-to-many lookup table relating a Person to zero or more email addresses | ||||
|  | ||||
| @ -191,6 +212,7 @@ The Artists table will contain Artists! | ||||
| ``` sql | ||||
| CREATE TABLE Artists ( | ||||
| 	id TEXT(36), | ||||
| 	label_id TEXT(36), | ||||
| 	name TEXT, | ||||
| 	bio TEXT, | ||||
| 	website TEXT, | ||||
|  | ||||
| @ -168,6 +168,7 @@ CREATE INDEX IF NOT EXISTS LabelTags_label_id_IDX ON LabelTags (label_id); | ||||
| -- The Artists table will contain Artists! | ||||
| CREATE TABLE IF NOT EXISTS Artists ( | ||||
| 	id TEXT(36), | ||||
| 	label_id TEXT(36), | ||||
| 	name TEXT, | ||||
| 	bio TEXT, | ||||
| 	website TEXT, | ||||
|  | ||||
							
								
								
									
										564
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										564
									
								
								src/main.rs
									
									
									
									
									
								
							| @ -1,9 +1,565 @@ | ||||
| use warp::Filter; | ||||
| 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<String>, | ||||
|     avatar: Option<String>, | ||||
|     cover: Option<String>, | ||||
|     bio: Option<String>, | ||||
|     is_active: bool, | ||||
|     is_blocked: bool, | ||||
|     created_at: usize, | ||||
|     modified_at: usize, | ||||
|     deleted_at: Option<usize>, | ||||
|     modified_by: PersonId, | ||||
|     deleted_by: Option<PersonId>, | ||||
|     last_seen: usize, | ||||
|     shipping_addresses: Option<Vec<PersonShippingAddress>>, | ||||
|     emails: Option<Vec<PersonEmail>>, | ||||
|     credentials: Option<Vec<PersonCredential>>, | ||||
| } | ||||
|  | ||||
| #[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<usize>, | ||||
|     modified_by: PersonId, | ||||
|     deleted_by: Option<PersonId>, | ||||
| } | ||||
|  | ||||
| #[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<usize>, | ||||
|     created_by: PersonId, | ||||
|     modified_by: PersonId, | ||||
|     deleted_by: Option<PersonId>, | ||||
|     contacts: Option<Vec<LabelContact>>, | ||||
|     tags: Option<Vec<Tag>>, | ||||
|     artists: Option<Vec<Artist>>, | ||||
| } | ||||
|  | ||||
| #[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<usize>, | ||||
|     created_by: PersonId, | ||||
|     modified_by: PersonId, | ||||
|     deleted_by: Option<PersonId>, | ||||
|     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<LabelId>, | ||||
|     name: String, | ||||
|     bio: String, | ||||
|     website: String, | ||||
|     created_at: usize, | ||||
|     modified_at: usize, | ||||
|     deleted_at: Option<usize>, | ||||
|     created_by: PersonId, | ||||
|     modified_by: PersonId, | ||||
|     deleted_by: Option<PersonId>, | ||||
|     is_enabled: bool, | ||||
|     is_public: bool, | ||||
|     contacts: Option<Vec<ArtistContact>>, | ||||
|     tags: Option<Vec<Tag>>, | ||||
| } | ||||
|  | ||||
| #[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<usize>, | ||||
|     created_by: PersonId, | ||||
|     modified_by: PersonId, | ||||
|     deleted_by: Option<PersonId>, | ||||
|     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<String>, // if preview_source == None, use source as preview | ||||
|     source: String, | ||||
|     price: usize, | ||||
|     created_at: usize, | ||||
|     modified_at: usize, | ||||
|     deleted_at: Option<usize>, | ||||
|     created_by: PersonId, | ||||
|     modified_by: PersonId, | ||||
|     deleted_by: Option<PersonId>, | ||||
|     lyrics: Option<String>, | ||||
|     primary_artist: Artist, | ||||
|     other_artists: Option<Vec<Artist>>, | ||||
|     tags: Option<Vec<Tag>>, | ||||
| } | ||||
|  | ||||
| #[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<Track>, | ||||
|     is_public: bool, | ||||
|     is_available: bool, | ||||
|     preview_source: Option<String>, | ||||
|     source: String, | ||||
|     price: usize, | ||||
|     created_at: usize, | ||||
|     modified_at: usize, | ||||
|     deleted_at: Option<usize>, | ||||
|     created_by: PersonId, | ||||
|     modified_by: PersonId, | ||||
|     deleted_by: Option<PersonId>, | ||||
|     primary_artist: Option<Artist>, | ||||
|     other_artists: Option<Vec<Artist>>, | ||||
|     tags: Option<Vec<Tag>>, | ||||
| } | ||||
|  | ||||
| #[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<usize>, | ||||
|     is_public: bool, | ||||
|     tracks: Option<Vec<Track>>, | ||||
|     tags: Option<Vec<Tag>>, | ||||
| } | ||||
|  | ||||
| #[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<RwLock<HashMap<PersonId, Person>>>, | ||||
|     labels: Arc<RwLock<HashMap<LabelId, Label>>>, | ||||
|     artists: Arc<RwLock<HashMap<ArtistId, Artist>>>, | ||||
|     albums: Arc<RwLock<HashMap<AlbumId, Album>>>, | ||||
|     tracks: Arc<RwLock<HashMap<TrackId, Track>>>, | ||||
| } | ||||
|  | ||||
| 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<impl Reply, Rejection> { | ||||
|     if let Some(error) = r.find::<Error>() { | ||||
|         Ok(warp::reply::with_status( | ||||
|             error.to_string(), | ||||
|             StatusCode::RANGE_NOT_SATISFIABLE, | ||||
|         )) | ||||
|     } else if let Some(error) = r.find::<CorsForbidden>() { | ||||
|         Ok(warp::reply::with_status( | ||||
|             error.to_string(), | ||||
|             StatusCode::FORBIDDEN, | ||||
|         )) | ||||
|     } else if let Some(error) = r.find::<BodyDeserializeError>() { | ||||
|         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<String, String>) -> Result<Pagination, Error> { | ||||
|     if params.contains_key("start") && params.contains_key("end") { | ||||
|         return Ok(Pagination { | ||||
|             start: params | ||||
|                 .get("start") | ||||
|                 .unwrap() | ||||
|                 .parse::<usize>() | ||||
|                 .map_err(Error::ParseError)?, | ||||
|             end: params | ||||
|                 .get("end") | ||||
|                 .unwrap() | ||||
|                 .parse::<usize>() | ||||
|                 .map_err(Error::ParseError)?, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     Err(Error::MissingParameters) | ||||
| } | ||||
|  | ||||
| async fn get_persons( | ||||
|     params: HashMap<String, String>, | ||||
|     datastore: Datastore, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     if !params.is_empty() { | ||||
|         let pagination = extract_pagination(params)?; | ||||
|         let res: Vec<Person> = datastore.persons.read().await.values().cloned().collect(); | ||||
|         let res = &res[pagination.start..pagination.end]; | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } else { | ||||
|         let res: Vec<Person> = datastore.persons.read().await.values().cloned().collect(); | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| async fn get_labels( | ||||
|     params: HashMap<String, String>, | ||||
|     datastore: Datastore, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     if !params.is_empty() { | ||||
|         let pagination = extract_pagination(params)?; | ||||
|         let res: Vec<Label> = datastore.labels.read().await.values().cloned().collect(); | ||||
|         let res = &res[pagination.start..pagination.end]; | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } else { | ||||
|         let res: Vec<Label> = datastore.labels.read().await.values().cloned().collect(); | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| async fn get_artists( | ||||
|     params: HashMap<String, String>, | ||||
|     datastore: Datastore, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     if !params.is_empty() { | ||||
|         let pagination = extract_pagination(params)?; | ||||
|         let res: Vec<Artist> = datastore.artists.read().await.values().cloned().collect(); | ||||
|         let res = &res[pagination.start..pagination.end]; | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } else { | ||||
|         let res: Vec<Artist> = datastore.artists.read().await.values().cloned().collect(); | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| async fn get_albums( | ||||
|     params: HashMap<String, String>, | ||||
|     datastore: Datastore, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     if !params.is_empty() { | ||||
|         let pagination = extract_pagination(params)?; | ||||
|         let res: Vec<Album> = datastore.albums.read().await.values().cloned().collect(); | ||||
|         let res = &res[pagination.start..pagination.end]; | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } else { | ||||
|         let res: Vec<Album> = datastore.albums.read().await.values().cloned().collect(); | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| async fn get_tracks( | ||||
|     params: HashMap<String, String>, | ||||
|     datastore: Datastore, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     if !params.is_empty() { | ||||
|         let pagination = extract_pagination(params)?; | ||||
|         let res: Vec<Track> = datastore.tracks.read().await.values().cloned().collect(); | ||||
|         let res = &res[pagination.start..pagination.end]; | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } else { | ||||
|         let res: Vec<Track> = datastore.tracks.read().await.values().cloned().collect(); | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| async fn add_person( | ||||
|     datastore: Datastore, | ||||
|     person: Person, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     datastore | ||||
|         .persons | ||||
|         .write() | ||||
|         .await | ||||
|         .insert(person.id.clone(), person); | ||||
|  | ||||
|     Ok(warp::reply::with_status("Person added", StatusCode::OK)) | ||||
| } | ||||
|  | ||||
| async fn add_label( | ||||
|     datastore: Datastore, | ||||
|     label: Label, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     datastore | ||||
|         .labels | ||||
|         .write() | ||||
|         .await | ||||
|         .insert(label.id.clone(), label); | ||||
|  | ||||
|     Ok(warp::reply::with_status("Label added", StatusCode::OK)) | ||||
| } | ||||
|  | ||||
| async fn add_artist( | ||||
|     datastore: Datastore, | ||||
|     artist: Artist, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     datastore | ||||
|         .artists | ||||
|         .write() | ||||
|         .await | ||||
|         .insert(artist.id.clone(), artist); | ||||
|  | ||||
|     Ok(warp::reply::with_status("Artist added", StatusCode::OK)) | ||||
| } | ||||
|  | ||||
| async fn add_album( | ||||
|     datastore: Datastore, | ||||
|     album: Album, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     datastore | ||||
|         .albums | ||||
|         .write() | ||||
|         .await | ||||
|         .insert(album.id.clone(), album); | ||||
|  | ||||
|     Ok(warp::reply::with_status("Album added", StatusCode::OK)) | ||||
| } | ||||
|  | ||||
| async fn add_track( | ||||
|     datastore: Datastore, | ||||
|     track: Track, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     datastore | ||||
|         .tracks | ||||
|         .write() | ||||
|         .await | ||||
|         .insert(track.id.clone(), track); | ||||
|  | ||||
|     Ok(warp::reply::with_status("Track added", StatusCode::OK)) | ||||
| } | ||||
|  | ||||
| #[tokio::main] | ||||
| async fn main() { | ||||
|     let hello = warp::get() | ||||
|         .map(|| format!("Hello world!")); | ||||
|     let datastore = Datastore::new(); | ||||
|     let datastore_filter = warp::any().map(move || datastore.clone()); | ||||
|  | ||||
|     warp::serve(hello).run(([127, 0, 0, 1], 5309)).await; | ||||
|     let cors = warp::cors() | ||||
|         .allow_any_origin() | ||||
|         .allow_header("content-type") | ||||
|         .allow_methods(&[Method::PUT, Method::DELETE, Method::GET, Method::POST]); | ||||
|  | ||||
|     let get_persons = warp::get() | ||||
|         .and(warp::path("persons")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(warp::query()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and_then(get_persons); | ||||
|  | ||||
|     let get_labels = warp::get() | ||||
|         .and(warp::path("labels")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(warp::query()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and_then(get_labels); | ||||
|  | ||||
|     let get_artists = warp::get() | ||||
|         .and(warp::path("artists")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(warp::query()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and_then(get_artists); | ||||
|      | ||||
|     let get_albums = warp::get() | ||||
|         .and(warp::path("albums")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(warp::query()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and_then(get_albums); | ||||
|  | ||||
|     let get_tracks = warp::get() | ||||
|         .and(warp::path("tracks")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(warp::query()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and_then(get_tracks); | ||||
|  | ||||
|     let add_person = warp::post() | ||||
|         .and(warp::path("persons")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and(warp::body::json()) | ||||
|         .and_then(add_person); | ||||
|  | ||||
|     let add_label = warp::post() | ||||
|         .and(warp::path("labels")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and(warp::body::json()) | ||||
|         .and_then(add_label); | ||||
|  | ||||
|     let add_artist = warp::post() | ||||
|         .and(warp::path("artists")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and(warp::body::json()) | ||||
|         .and_then(add_artist); | ||||
|  | ||||
|     let add_album = warp::post() | ||||
|         .and(warp::path("albums")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and(warp::body::json()) | ||||
|         .and_then(add_album); | ||||
|  | ||||
|     let add_track = warp::post() | ||||
|         .and(warp::path("tracks")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and(warp::body::json()) | ||||
|         .and_then(add_track); | ||||
|  | ||||
|     let routes = get_persons | ||||
|         .or(get_labels) | ||||
|         .or(get_artists) | ||||
|         .or(get_albums) | ||||
|         .or(get_tracks) | ||||
|         .or(add_person) | ||||
|         .or(add_label) | ||||
|         .or(add_artist) | ||||
|         .or(add_album) | ||||
|         .or(add_track) | ||||
|         .with(cors) | ||||
|         .recover(return_error); | ||||
|  | ||||
|     warp::serve(routes).run(([127, 0, 0, 1], 5309)).await; | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user