From 6eee1102918857b11ecc193e684e2438520ef90f Mon Sep 17 00:00:00 2001 From: Ben Bridle Date: Thu, 25 Aug 2022 20:57:37 +1200 Subject: Initial commit --- src/lib.rs | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 src/lib.rs (limited to 'src/lib.rs') diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..b7db9df --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,171 @@ +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; + 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, + )) +} -- cgit v1.2.3-70-g09d2