rust_recipe/
nutrition_information.rs1use once_cell::sync::Lazy;
2use regex::Regex;
3
4#[derive(Default, Debug, PartialEq)]
7pub struct NutritionInformation {
8 pub calories: Option<f32>,
10
11 pub carbohydrate_grams: Option<f32>,
13
14 pub cholesterol_milligrams: Option<f32>,
16
17 pub fat_grams: Option<f32>,
19
20 pub fiber_grams: Option<f32>,
22
23 pub protein_grams: Option<f32>,
25
26 pub saturated_fat_grams: Option<f32>,
28
29 pub serving_size: Option<String>,
31
32 pub sodium_milligrams: Option<f32>,
34
35 pub sugar_grams: Option<f32>,
37
38 pub trans_fat_grams: Option<f32>,
40
41 pub unsaturated_fat_grams: Option<f32>,
43}
44
45impl NutritionInformation {
46 pub fn add_info(&mut self, field_name: &str, value: &str) {
47 let numeric_value = get_first_number_from_str(value);
48 match field_name {
49 "calories" => self.calories = numeric_value,
50 "carbohydrateContent" => self.carbohydrate_grams = numeric_value,
51 "cholesterolContent" => self.cholesterol_milligrams = numeric_value,
52 "fatContent" => self.fat_grams = numeric_value,
53 "fiberContent" => self.fiber_grams = numeric_value,
54 "proteinContent" => self.protein_grams = numeric_value,
55 "saturatedFatContent" => self.saturated_fat_grams = numeric_value,
56 "servingSize" => self.serving_size = Some(value.to_string()),
57 "sodiumContent" => self.sodium_milligrams = numeric_value,
58 "sugarContent" => self.sugar_grams = numeric_value,
59 "transFatContent" => self.trans_fat_grams = numeric_value,
60 "unsaturatedFatContent" => self.unsaturated_fat_grams = numeric_value,
61 _ => (),
62 }
63 }
64}
65
66fn get_first_number_from_str(s: &str) -> Option<f32> {
67 static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r#"(\d*[.])?\d+"#).unwrap());
68 let captures = RE.captures(s);
69 match captures {
70 Some(caps) => caps[0].parse().ok(),
71 None => None,
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn test_add_info_success() {
81 let mut ni = NutritionInformation::default();
82 ni.add_info("calories", "200 calories");
83 ni.add_info("carbohydrateContent", "1 carbs");
84 ni.add_info("cholesterolContent", "2 cholesterol");
85 ni.add_info("fatContent", "3 fat");
86 ni.add_info("fiberContent", "4 fiber");
87 ni.add_info("proteinContent", "5 protein");
88 ni.add_info("saturatedFatContent", "6 fat");
89 ni.add_info("servingSize", "7 servings");
90 ni.add_info("sodiumContent", "8 sodium");
91 ni.add_info("sugarContent", "9 sugar");
92 ni.add_info("transFatContent", "10 fat");
93 ni.add_info("unsaturatedFatContent", "11 fat");
94
95 assert_eq!(Some(200_f32), ni.calories);
96 assert_eq!(Some(1_f32), ni.carbohydrate_grams);
97 assert_eq!(Some(2_f32), ni.cholesterol_milligrams);
98 assert_eq!(Some(3_f32), ni.fat_grams);
99 assert_eq!(Some(4_f32), ni.fiber_grams);
100 assert_eq!(Some(5_f32), ni.protein_grams);
101 assert_eq!(Some(6_f32), ni.saturated_fat_grams);
102 assert_eq!(Some(String::from("7 servings")), ni.serving_size);
103 assert_eq!(Some(8_f32), ni.sodium_milligrams);
104 assert_eq!(Some(9_f32), ni.sugar_grams);
105 assert_eq!(Some(10_f32), ni.trans_fat_grams);
106 assert_eq!(Some(11_f32), ni.unsaturated_fat_grams);
107 }
108
109 #[test]
110 fn test_add_info_failure() {
111 let mut ni = NutritionInformation::default();
112 ni.add_info("not valid", "100 of something");
113 assert_eq!(NutritionInformation::default(), ni);
114 }
115
116 #[test]
117 fn test_get_first_number_from_str_success_int() {
118 let res = get_first_number_from_str("About 200 calories");
119 assert_eq!(Some(200_f32), res)
120 }
121
122 #[test]
123 fn test_get_first_number_from_str_success_float() {
124 let res = get_first_number_from_str("200.1 calories");
125 assert_eq!(Some(200.1), res)
126 }
127
128 #[test]
129 fn test_get_first_number_from_str_failure() {
130 let res = get_first_number_from_str("some calories");
131 assert_eq!(None, res)
132 }
133}