Initial commit
This commit is contained in:
commit
6eee110291
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "recipe_parser"
|
||||||
|
version = "1.0.0"
|
||||||
|
authors = ["Ben Bridle <bridle.benjamin@gmail.com>"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
|
@ -0,0 +1,171 @@
|
||||||
|
pub struct Ingredient {
|
||||||
|
pub name: String,
|
||||||
|
pub quantity: String,
|
||||||
|
pub unit: Option<String>,
|
||||||
|
pub addendum: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Recipe {
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub ingredients: Vec<Ingredient>,
|
||||||
|
pub process: Vec<String>,
|
||||||
|
}
|
||||||
|
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<char> = 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,
|
||||||
|
))
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
use recipe_parser::Recipe;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let recipe = Recipe::parse(
|
||||||
|
"Combine {bulghur wheat, 1 cup} and {boiling water, 1 cup, for wheat}. Set aside for 20 minutes. Put {whole-meal flour, 2 cups}, {salt, 2 tsp}, and {yeast, 2 tbsp} in a bowl and stir together. Add {cold water, 1 cup} and {golden syrup, 1 tbsp}, immediately followed by {boiling water, 1 cup}. Stir to a smooth paste and stand for 2 to 3 minutes. Mix in the {egg, 1} and {high-grade flour, 3 cups}, adding the last cup of flour slowly (more or less than the cup may be needed to give a very thick batter, which is not quite as stiff as dough). Mix for 3 to 4 minutes. Cover and put in a warm place for 15 minutes. Stir well and pour into two 22cm greased loaf tins. Put in a warm place until the dough doubles in volume. Bake at 200°C for 35 minutes or until the loaf sounds hollow when tapped on the bottom. If loaf is browning too quickly, cover with foil.",
|
||||||
|
);
|
||||||
|
for i in recipe.ingredients {
|
||||||
|
let unit = match i.unit {
|
||||||
|
Some(unit) => format!("{} of ", unit),
|
||||||
|
None => String::new(),
|
||||||
|
};
|
||||||
|
let addendum = match i.addendum {
|
||||||
|
Some(addendum) => format!(" ({})", addendum),
|
||||||
|
None => String::new(),
|
||||||
|
};
|
||||||
|
println!("- {} {}{}{}", i.quantity, unit, i.name, addendum);
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
for paragraph in recipe.process {
|
||||||
|
println!("{}\n", paragraph);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue