1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
pub struct Ingredient {
pub name: String,
pub quantity: String,
pub unit: Option<String>,
pub note: Option<String>,
}
impl std::fmt::Display for Ingredient {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
let n = &self.name;
let q = if let Some((whole,frac)) = self.quantity.split_once('-') {
match frac_to_char(frac) {
Some(frac) => format!("{whole}{frac}"),
None => format!("{whole}-{frac}"),
}
} else if self.quantity.contains('/') {
let frac = &self.quantity;
match frac_to_char(frac) {
Some(frac) => format!("{frac}"),
None => format!("{frac}"),
}
} else {
self.quantity.to_string()
};
let string = match (&self.unit, &self.note) {
(Some(ref u), Some(ref a)) => format!("{q} {u} of {n} ({a})"),
(Some(ref u), None) => format!("{q} {u} of {n}"),
(None, Some(ref a)) => format!("{q} {n} ({a})"),
(None, None) => format!("{q} {n}"),
};
f.write_str(&string)
}
}
fn frac_to_char(frac: &str) -> Option<&str> {
match frac {
"1/2" => Some("½"),
"1/3" => Some("⅓"),
"1/4" => Some("¼"),
"1/8" => Some("⅛"),
"2/3" => Some("⅔"),
"3/4" => Some("¾"),
_ => None,
}
}
pub struct Recipe {
pub ingredients: Vec<Ingredient>,
pub process: Vec<String>,
}
impl Recipe {
pub fn parse(raw_text: &str) -> Self {
let mut ingredients = Vec::new();
let mut process = Vec::new();
for line in raw_text.lines() {
let line = line.trim();
if line.is_empty() { continue }
let mut paragraph = String::new();
let chars: Vec<char> = line.chars().collect();
let mut i = 0;
while let Some(c) = chars.get(i) {
if *c == '{' {
let mut j = i + 1;
while let Some(d) = chars.get(j) {
if *d == '}' {
// {rice, 2 cups} quan unit
// {rice, a handful} quan unit
// {egg, one} quan
let section: String = chars[i+1..j].iter().collect();
let mut clauses = section.split(',');
let name = clauses.next().unwrap_or("").trim().to_string();
let (quantity, unit) = {
let quantity_unit = clauses.next().unwrap_or("").trim();
match quantity_unit.split_once(' ') {
Some((quantity, unit)) => (quantity.to_string(), Some(unit.to_string())),
None => (quantity_unit.to_string(), None)
}
};
let note = clauses.next().and_then(|s| Some(s.trim().to_string()));
paragraph.push_str(&name);
ingredients.push(Ingredient { name, quantity, unit, note });
i = j; break
}
j += 1;
}
} else {
paragraph.push(*c);
}
i += 1;
}
if !paragraph.is_empty() { process.push(paragraph) }
}
Self { ingredients, process }
}
}
|