pub struct Ingredient { pub name: String, pub quantity: String, pub unit: Option, pub addendum: Option, } pub struct Recipe { pub title: Option, pub ingredients: Vec, pub process: Vec, } impl Recipe { pub fn parse(recipe: &str) -> Self { let mut ingredients = Vec::new(); let mut process = Vec::new(); let mut paragraph = String::new(); let mut title = None; for line in recipe.lines() { if line.trim().is_empty() { let paragraph = std::mem::take(&mut paragraph); if !paragraph.is_empty() { process.push(paragraph); } continue; } if line.starts_with("# ") && title.is_none() { title = Some(line[2..].to_string()); continue; } let chars: Vec = line.chars().collect(); let mut i = 0; while i < chars.len() { match capture(&chars[i..]) { Some((ingredient, length)) => { paragraph.push_str(&ingredient.name); ingredients.push(ingredient); i += length; } None => { paragraph.push(*chars.get(i).unwrap()); i += 1; } } } } let paragraph = std::mem::take(&mut paragraph); if !paragraph.is_empty() { process.push(paragraph); } Self { title, ingredients, process, } } } fn capture(chars: &[char]) -> Option<(Ingredient, usize)> { if chars.get(0) != Some(&'{') { return None; } let mut i = 1; let mut name = String::new(); let mut quantity = String::new(); let mut unit = None; let mut addendum = None; // Ingredient name loop { match chars.get(i) { Some(&',') => { i += 1; break; } Some(c) => { name.push(*c); i += 1; } None => return None, } } // Eat spaces loop { match chars.get(i) { Some(&' ') => i += 1, Some(_) => break, None => return None, } } // Quantity loop { match chars.get(i) { Some(&' ') => { i += 1; unit = Some(String::new()); break; } Some(&',') => { i += 1; addendum = Some(String::new()); break; } Some(&'}') => { i += 1; break; } Some(c) => { quantity.push(*c); i += 1; } None => return None, } } // Unit if let Some(ref mut unit) = unit { loop { match chars.get(i) { Some(&'}') => { i += 1; break; } Some(&',') => { i += 1; addendum = Some(String::new()); break; } Some(c) => { unit.push(*c); i += 1; } None => return None, } } } // Addendum if let Some(ref mut addendum) = addendum { loop { match chars.get(i) { Some(&'}') => { i += 1; break; } Some(c) => { addendum.push(*c); i += 1; } None => return None, } } } // Trim values let name = name.trim().to_string(); let quantity = quantity.trim().to_string(); let unit = unit.and_then(|s| Some(s.trim().to_string())); let addendum = addendum.and_then(|s| Some(s.trim().to_string())); Some(( Ingredient { name, quantity, unit, addendum, }, i, )) }