summaryrefslogtreecommitdiff
path: root/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs230
1 files changed, 77 insertions, 153 deletions
diff --git a/src/lib.rs b/src/lib.rs
index e961f1c..e671989 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,176 +2,100 @@ pub struct Ingredient {
pub name: String,
pub quantity: String,
pub unit: Option<String>,
- pub addendum: 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 title: Option<String>,
pub ingredients: Vec<Ingredient>,
pub process: Vec<String>,
}
+
impl Recipe {
- pub fn parse(recipe: &str) -> Self {
+ pub fn parse(raw_text: &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;
- }
+ 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 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;
+
+ 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);
}
- }
- }
-
- 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,
+ if !paragraph.is_empty() { process.push(paragraph) }
}
- }
- // Eat spaces
- loop {
- match chars.get(i) {
- Some(&' ') => i += 1,
- Some(_) => break,
- None => return None,
- }
+ Self { ingredients, process }
}
-
- // 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,
- ))
}
+