Skip to content

Commit

Permalink
Further Implement editing of Recipes
Browse files Browse the repository at this point in the history
The only Missing things left are updating the ingredients and
Subrecipes.

Also did the Following:
* Removed unused Imports
* Fixed miscellaneous Warnings
  • Loading branch information
isiko committed Aug 27, 2024
1 parent 94786a1 commit e663b0a
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 10 deletions.
3 changes: 0 additions & 3 deletions api/src/ingredients.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::{any::Any, borrow::BorrowMut};

use axum::{
extract::{Path, State},
http::StatusCode,
Expand All @@ -10,7 +8,6 @@ use axum::{
use foodlib::Ingredient;
use serde::{Deserialize, Serialize};
use sqlx::types::BigDecimal;
use tracing::Instrument;

use crate::ApiState;

Expand Down
2 changes: 1 addition & 1 deletion api/src/places.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use axum::{
extract::{Path, State},
http::StatusCode,
response::IntoResponse,
routing::{delete, get, post, put},
routing::{get, post, put},
Json, Router,
};
use foodlib::Place;
Expand Down
186 changes: 180 additions & 6 deletions api/src/reciepes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ use axum::{
routing::{delete, get, post, put},
Json, Router,
};
use foodlib::Recipe;
use foodlib::{Recipe, RecipeIngredient, RecipeMetaIngredient, RecipeStep, Unit};
use serde::{Deserialize, Serialize};
use sqlx::{postgres::types::PgInterval, types::BigDecimal};

use crate::ApiState;

Expand All @@ -17,10 +18,12 @@ pub fn router() -> Router<crate::ApiState> {
.route("/:recipe_id/", get(get_recipe))
.route("/:recipe_id/", post(update_reciepe))
.route("/:recipe_id/", delete(delete_reciepe))
.route("/:recipe_id/steps/", get(not_implemented))
.route("/:recipe_id/steps/", post(not_implemented))
.route("/:recipe_id/ingredients/", get(update_reciepe))
.route("/:recipe_id/ingredients/", post(update_reciepe))
.route("/:recipe_id/steps/", get(get_steps))
.route("/:recipe_id/steps/", post(update_steps))
.route("/:recipe_id/subrecipes/", get(get_subrecipes))
.route("/:recipe_id/subrecipes/", post(update_subrecipes))
.route("/:recipe_id/ingredients/", get(get_ingredients))
.route("/:recipe_id/ingredients/", post(update_ingredients))
}

#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
Expand All @@ -38,6 +41,85 @@ fn recipe_to_body(recipe: Recipe) -> RecipeBody {
}
}

pub(crate) fn serialize_interval<S>(interval: &PgInterval, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let duration = interval.microseconds / 1000_0000;
serializer.serialize_str(&duration.to_string())
}

pub(crate) fn deserialize_interval<'de, D>(deserializer: D) -> Result<PgInterval, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let microseconds: i64 = s
.parse()
.map_err(|e| serde::de::Error::custom(format!("Failed to parse interval: {}", e)))?;
let interval = PgInterval {
microseconds: microseconds * 1000_1000,
days: 0,
months: 0,
};
Ok(interval)
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
struct RecipeStepBody {
name: String,
description: String,
#[serde(
serialize_with = "serialize_interval",
deserialize_with = "deserialize_interval"
)]
pub duration_fixed: PgInterval,
#[serde(
serialize_with = "serialize_interval",
deserialize_with = "deserialize_interval"
)]
pub duration_per_kg: PgInterval,
}
fn step_to_body(step: RecipeStep) -> RecipeStepBody {
RecipeStepBody {
name: step.step_name,
description: step.step_description,
duration_fixed: step.fixed_duration,
duration_per_kg: step.duration_per_kg,
}
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
struct RecipeIngredientBody {
id: i32,
name: String,
amount: BigDecimal,
unit: Unit,
}
fn ingredient_to_body(ingredient: RecipeIngredient) -> Option<RecipeIngredientBody> {
match ingredient.ingredient {
RecipeMetaIngredient::Ingredient(ingredient_data) => Some(RecipeIngredientBody {
id: ingredient_data.ingredient_id,
name: ingredient_data.name,
amount: ingredient.amount,
unit: ingredient.unit,
}),
_ => None,
}
}

fn subrecipe_to_body(ingredient: RecipeIngredient) -> Option<RecipeIngredientBody> {
match ingredient.ingredient {
RecipeMetaIngredient::MetaRecipe(ingredient_data) => Some(RecipeIngredientBody {
id: ingredient_data.recipe_id,
name: ingredient_data.name,
amount: ingredient.amount,
unit: ingredient.unit,
}),
_ => None,
}
}

async fn list_reciepes(State(state): State<ApiState>) -> impl IntoResponse {
match state.food_base.get_recipes().await {
Ok(recipes) => (
Expand Down Expand Up @@ -98,6 +180,98 @@ async fn delete_reciepe(
}
}

async fn not_implemented() -> impl IntoResponse {
async fn update_steps(
State(state): State<ApiState>,
Path(recipe_id): Path<i32>,
Json(body): Json<Vec<RecipeStepBody>>,
) -> impl IntoResponse {
match state
.food_base
.update_recipe_steps(
recipe_id,
body.into_iter().enumerate().map(|(i, step)| {
return RecipeStep {
step_id: 0,
step_order: i as f64,
step_name: step.name,
step_description: step.description,
fixed_duration: step.duration_fixed,
duration_per_kg: step.duration_per_kg,
recipe_id: recipe_id,
};
}),
)
.await
{
Ok(_) => StatusCode::OK,
Err(_) => StatusCode::INTERNAL_SERVER_ERROR,
}
}

async fn get_steps(State(state): State<ApiState>, Path(recipe_id): Path<i32>) -> impl IntoResponse {
match state.food_base.get_recipe_steps(recipe_id).await {
Ok(steps) => (
StatusCode::OK,
Json(
steps
.into_iter()
.map(step_to_body)
.collect::<Vec<RecipeStepBody>>(),
),
)
.into_response(),
Err(_) => StatusCode::NOT_FOUND.into_response(),
}
}

async fn get_ingredients(
State(state): State<ApiState>,
Path(recipe_id): Path<i32>,
) -> impl IntoResponse {
match state.food_base.get_recipe_ingredients(recipe_id).await {
Ok(ingredients) => (
StatusCode::OK,
Json(
ingredients
.into_iter()
.flat_map(ingredient_to_body)
.collect::<Vec<RecipeIngredientBody>>(),
),
)
.into_response(),
Err(_) => StatusCode::NOT_FOUND.into_response(),
}
}

async fn update_ingredients(
State(_state): State<ApiState>,
Path(_recipe_id): Path<i32>,
) -> impl IntoResponse {
StatusCode::NOT_IMPLEMENTED
}

async fn get_subrecipes(
State(state): State<ApiState>,
Path(recipe_id): Path<i32>,
) -> impl IntoResponse {
match state.food_base.get_recipe_meta_ingredients(recipe_id).await {
Ok(ingredients) => (
StatusCode::OK,
Json(
ingredients
.into_iter()
.flat_map(subrecipe_to_body)
.collect::<Vec<RecipeIngredientBody>>(),
),
)
.into_response(),
Err(_) => StatusCode::NOT_FOUND.into_response(),
}
}

async fn update_subrecipes(
State(_state): State<ApiState>,
Path(_recipe_id): Path<i32>,
) -> impl IntoResponse {
StatusCode::NOT_IMPLEMENTED
}

0 comments on commit e663b0a

Please sign in to comment.