├── Cargo.toml └── src ├── db.rs ├── db ├── database.rs └── pizza_data_trait.rs ├── error.rs ├── error └── pizza_error.rs ├── main.rs ├── models.rs └── models └── pizza.rs /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust_backend" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | actix-web = "4.4.0" 10 | async-trait = "0.1.73" 11 | derive_more = "0.99.17" 12 | json = "0.12.4" 13 | serde = { version = "1.0.188", features = ["derive"] } 14 | surrealdb = "1.0.0" 15 | uuid = "1.4.1" 16 | validator = { version = "0.16.1", features = ["derive"] } 17 | -------------------------------------------------------------------------------- /src/db.rs: -------------------------------------------------------------------------------- 1 | // db.rs 2 | pub mod database; 3 | pub use database::Database; 4 | pub mod pizza_data_trait; 5 | -------------------------------------------------------------------------------- /src/db/database.rs: -------------------------------------------------------------------------------- 1 | use surrealdb::engine::remote::ws::{Client, Ws}; 2 | use surrealdb::opt::auth::Root; 3 | use surrealdb::{Error, Surreal}; 4 | 5 | use crate::models::pizza::Pizza; 6 | 7 | #[derive(Clone)] 8 | pub struct Database { 9 | pub client: Surreal, 10 | pub name_space: String, 11 | pub db_name: String, 12 | } 13 | 14 | impl Database { 15 | pub async fn init() -> Result { 16 | let client = Surreal::new::("127.0.0.1:8000").await?; 17 | client 18 | .signin(Root { 19 | username: "root", 20 | password: "root", 21 | }) 22 | .await?; 23 | client.use_ns("surreal").use_db("pizzas").await.unwrap(); 24 | Ok(Database { 25 | client, 26 | name_space: String::from("surreal"), 27 | db_name: String::from("pizzas"), 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/db/pizza_data_trait.rs: -------------------------------------------------------------------------------- 1 | // data trait for pizza 2 | 3 | use crate::db::Database; 4 | use crate::models::Pizza; 5 | use actix_web::web::Data; 6 | use async_trait::async_trait; 7 | use surrealdb::Error; 8 | 9 | #[async_trait] 10 | pub trait PizzaDataTrait { 11 | async fn get_all_pizzas(db: &Data) -> Option>; 12 | async fn add_pizza(db: &Data, new_pizza: Pizza) -> Option; 13 | async fn update_pizza(db: &Data, uuid: String) -> Option; 14 | } 15 | 16 | #[async_trait] 17 | impl PizzaDataTrait for Database { 18 | async fn get_all_pizzas(db: &Data) -> Option> { 19 | let result = db.client.select("pizza").await; 20 | match result { 21 | Ok(all_pizzas) => Some(all_pizzas), 22 | Err(_) => None, 23 | } 24 | } 25 | 26 | async fn add_pizza(db: &Data, new_pizza: Pizza) -> Option { 27 | let created_pizza = db 28 | .client 29 | .create(("pizza", new_pizza.uuid.clone())) 30 | .content(new_pizza) 31 | .await; 32 | 33 | match created_pizza { 34 | Ok(created) => created, 35 | Err(_) => None, 36 | } 37 | } 38 | 39 | async fn update_pizza(db: &Data, uuid: String) -> Option { 40 | let find_pizza: Result, Error> = db.client.select(("pizza", &uuid)).await; 41 | 42 | match find_pizza { 43 | Ok(found) => { 44 | match found { 45 | Some(_found_pizza) => { 46 | // and if found the pizza 47 | let updated_pizza: Result, Error> = db 48 | .client 49 | .update(("pizza", &uuid)) 50 | .merge(Pizza { 51 | uuid, 52 | pizza_name: String::from("sold"), 53 | }) 54 | .await; 55 | match updated_pizza { 56 | Ok(updated) => updated, 57 | Err(_) => None, 58 | } 59 | } 60 | None => None, 61 | } 62 | } 63 | Err(_) => None, 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | pub mod pizza_error; 2 | pub use pizza_error::PizzaError; 3 | -------------------------------------------------------------------------------- /src/error/pizza_error.rs: -------------------------------------------------------------------------------- 1 | // will contain our new enum pizza error here 2 | use actix_web::{ 3 | http::{header::ContentType, StatusCode}, 4 | HttpResponse, ResponseError, 5 | }; 6 | 7 | use derive_more::Display; 8 | #[derive(Debug, Display)] 9 | pub enum PizzaError { 10 | NoPizzasFound, 11 | PizzaCreationFailure, 12 | NoSuchPizzaFound, 13 | } 14 | 15 | impl ResponseError for PizzaError { 16 | fn error_response(&self) -> HttpResponse { 17 | HttpResponse::build(self.status_code()) 18 | .insert_header(ContentType::json()) 19 | .body(self.to_string()) 20 | } 21 | 22 | fn status_code(&self) -> StatusCode { 23 | match self { 24 | PizzaError::NoPizzasFound => StatusCode::NOT_FOUND, 25 | PizzaError::PizzaCreationFailure => StatusCode::INTERNAL_SERVER_ERROR, 26 | PizzaError::NoSuchPizzaFound => StatusCode::NOT_FOUND, 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use actix_web::web::Data; 2 | use actix_web::{get, patch, post, web::Json, web::Path, App, HttpResponse, HttpServer, Responder}; 3 | use error::PizzaError; 4 | mod models; 5 | use crate::db::{pizza_data_trait::PizzaDataTrait, Database}; 6 | mod db; 7 | use crate::models::pizza::{BuyPizzaRequest, Pizza, UpdatePizzaURL}; 8 | use uuid; 9 | use validator::Validate; 10 | mod error; 11 | 12 | #[get("/pizzas")] 13 | async fn get_pizzas(db: Data) -> Result>, PizzaError> { 14 | let pizzas = Database::get_all_pizzas(&db).await; 15 | match pizzas { 16 | Some(found_pizzas) => Ok(Json(found_pizzas)), 17 | None => Err(PizzaError::NoPizzasFound), 18 | } 19 | } 20 | 21 | #[post("/buypizza")] 22 | async fn buy_pizza( 23 | body: Json, 24 | db: Data, 25 | ) -> Result, PizzaError> { 26 | let is_valid = body.validate(); 27 | match is_valid { 28 | Ok(_) => { 29 | let pizza_name = body.pizza_name.clone(); 30 | 31 | let mut buffer = uuid::Uuid::encode_buffer(); 32 | let new_uuid = uuid::Uuid::new_v4().simple().encode_lower(&mut buffer); 33 | 34 | let new_pizza = 35 | Database::add_pizza(&db, Pizza::new(String::from(new_uuid), pizza_name)).await; 36 | 37 | match new_pizza { 38 | Some(created) => Ok(Json(created)), 39 | None => Err(PizzaError::PizzaCreationFailure), 40 | } 41 | } 42 | Err(_) => Err(PizzaError::PizzaCreationFailure), 43 | } 44 | } 45 | 46 | #[patch("/updatepizza/{uuid}")] 47 | async fn update_pizza( 48 | update_pizza_url: Path, 49 | db: Data, 50 | ) -> Result, PizzaError> { 51 | let uuid = update_pizza_url.into_inner().uuid; 52 | let update_result = Database::update_pizza(&db, uuid).await; 53 | match update_result { 54 | Some(updated_pizza) => Ok(Json(updated_pizza)), 55 | None => Err(PizzaError::NoSuchPizzaFound), 56 | } 57 | } 58 | 59 | #[actix_web::main] 60 | async fn main() -> std::io::Result<()> { 61 | let db = Database::init() 62 | .await 63 | .expect("error connecting to database"); 64 | let db_data = Data::new(db); 65 | 66 | HttpServer::new(move || { 67 | App::new() 68 | .app_data(db_data.clone()) 69 | .service(get_pizzas) 70 | .service(buy_pizza) 71 | .service(update_pizza) 72 | }) 73 | .bind("127.0.0.1:8080")? 74 | .run() 75 | .await 76 | } 77 | -------------------------------------------------------------------------------- /src/models.rs: -------------------------------------------------------------------------------- 1 | pub mod pizza; 2 | pub use pizza::BuyPizzaRequest; 3 | pub use pizza::Pizza; 4 | pub use pizza::UpdatePizzaURL; 5 | -------------------------------------------------------------------------------- /src/models/pizza.rs: -------------------------------------------------------------------------------- 1 | // pizza.rs 2 | use serde::{Deserialize, Serialize}; 3 | use validator::Validate; 4 | 5 | #[derive(Validate, Deserialize, Serialize)] 6 | pub struct BuyPizzaRequest { 7 | #[validate(length(min = 1, message = "pizza name required"))] 8 | pub pizza_name: String, 9 | } 10 | 11 | #[derive(Validate, Deserialize, Serialize)] 12 | pub struct UpdatePizzaURL { 13 | pub uuid: String, 14 | } 15 | 16 | #[derive(Validate, Deserialize, Serialize, Debug)] 17 | pub struct Pizza { 18 | pub uuid: String, 19 | pub pizza_name: String, 20 | } 21 | 22 | impl Pizza { 23 | pub fn new(uuid: String, pizza_name: String) -> Pizza { 24 | Pizza { uuid, pizza_name } 25 | } 26 | } 27 | --------------------------------------------------------------------------------