1#[cfg(feature = "serde")]
5use serde::{Deserialize, Serialize};
6
7#[derive(Clone, Debug)]
11#[cfg_attr(any(test, feature = "tests"), derive(PartialEq))]
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13pub struct Recipe {
14 pub title: String,
15 pub description: Option<String>,
16 pub tags: Vec<String>,
17 pub yields: Vec<Amount>,
18 pub ingredients: Vec<Ingredient>,
19 pub ingredient_groups: Vec<IngredientGroup>,
20 pub instructions: Option<String>,
21}
22
23#[derive(Clone, Debug)]
25#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
26#[cfg_attr(any(test, feature = "tests"), derive(PartialEq))]
27pub struct IngredientGroup {
28 pub title: String,
29 pub ingredients: Vec<Ingredient>,
30 pub ingredient_groups: Vec<IngredientGroup>,
31}
32
33#[derive(Clone, Debug)]
35#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
36#[cfg_attr(any(test, feature = "tests"), derive(PartialEq))]
37pub struct Ingredient {
38 pub amount: Option<Amount>,
39 pub name: String,
40 pub link: Option<String>,
41}
42
43#[derive(Clone, Debug)]
45#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
46#[cfg_attr(any(test, feature = "tests"), derive(PartialEq))]
47pub struct Amount {
48 pub factor: Factor,
49 pub unit: Option<String>,
50}
51
52#[derive(Copy, Clone, Debug)]
56pub enum Factor {
57 Integer(u32),
58 Fraction(u16, u16),
59 Float(f32),
60}
61
62impl From<Factor> for f32 {
63 fn from(value: Factor) -> Self {
64 match value {
65 Factor::Integer(v) => v as f32,
66 Factor::Fraction(num, denom) => num as f32 / denom as f32,
67 Factor::Float(v) => v,
68 }
69 }
70}
71
72#[cfg(feature = "serde")]
73impl Serialize for Factor {
74 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
75 where
76 S: serde::Serializer,
77 {
78 match self {
79 Factor::Integer(v) => v.to_string().serialize(serializer),
80 Factor::Fraction(num, denom) => (*num as f32 / *denom as f32)
81 .to_string()
82 .serialize(serializer),
83 Factor::Float(v) => v.to_string().serialize(serializer),
84 }
85 }
86}
87
88#[cfg(feature = "serde")]
89impl<'de> Deserialize<'de> for Factor {
90 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
91 where
92 D: serde::Deserializer<'de>,
93 {
94 use std::str::FromStr;
95
96 let s = <&str>::deserialize(deserializer)?;
97 u32::from_str(s)
98 .map(Factor::Integer)
99 .or_else(|_| f32::from_str(s).map(Factor::Float))
100 .map_err(serde::de::Error::custom)
101 }
102}
103
104#[cfg(any(test, feature = "tests"))]
105impl PartialEq for Factor {
106 fn eq(&self, other: &Self) -> bool {
107 f32::from(*self).eq(&f32::from(*other))
108 }
109}