summaryrefslogtreecommitdiff
path: root/src/lib.rs
diff options
context:
space:
mode:
authorBen Bridle <bridle.benjamin@gmail.com>2022-08-25 20:57:37 +1200
committerBen Bridle <bridle.benjamin@gmail.com>2022-08-25 20:57:37 +1200
commit6eee1102918857b11ecc193e684e2438520ef90f (patch)
tree19533d851c646331c38965a2ab3fd91a15f131a3 /src/lib.rs
downloadrecipe-43df640b5abd25fc14ada9c03dd0134e3c10ce49.zip
Initial commitv1.0
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs171
1 files changed, 171 insertions, 0 deletions
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<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,
+ ))
+}