Zheoni / cooklang-chef

A CLI to manage cooklang recipes
MIT License
68 stars 8 forks source link

Use cooklang as a library #4

Closed bbstilson closed 1 year ago

bbstilson commented 1 year ago

Hi there. I love the project, but I would like to interact with a few components as a library so that I can create a more automated experience.

Specifically, what I'm trying to do is get access to the combined ingredients given a list of recipes so that I can programmatically update my grocery list.

It doesn't seem like this is directly possible, but I am somewhat new to Rust, so I might be missing something. If it is not possible, I'm happy to tackle abstracting this as a PR if that's inline with what you have in mind for the future of the project.

Zheoni commented 1 year ago

I think you are looking for cooklang/cooklang-rs (crates.io), it was part of this repository before but now it has been moved to the cooklang org.

That is the library that does all the parsing and combining ingredients.

bbstilson commented 1 year ago

Thanks! I'll dig in.

I'm struggling to find the merging logic (found here in this repo) in the cooklang-rs crate. Is it best to just copy that code into my project and use the parse function over my list of recipes?

Zheoni commented 1 year ago

Half of that function is just resolving the recipe names into file names, reading the recipes and scaling them. The key part implemented by cooklang-rs is in the recipe.ingredient_list() and the GroupedQuantity::merge . I really need to improve the documentation in the library 😅 .

But as you can see here, once the recipe has been parsed and scaled, I iterate over the ingredient list of each recipe and merge their quantities by ingredient name.

https://github.com/Zheoni/cooklang-chef/blob/b306c563024de7e6a15dae71dda8d1d7686abf98/src/shopping_list/create.rs#L126-L142

bbstilson commented 1 year ago

Thanks for your help! I was able to get my weekend hackathon working. At a high-level, I have a function that chooses 3 random recipes, then adds those ingredients to a todoist project where each category from cooklang gets its own section in the todoist project.

There's a couple things I noticed that could be improved to make things a bit easier on users.

First, a small thing, there's a typo: cooklang::aisle::AileConf should be cooklang::aisle::AisleConf (missing an s)

Second, I had to pull in a bunch of code from the guts of cooklang internal functions to get the categorized ingredients list. A much more convenient api would have two functions, I think.

The first would be a simple function that returns the recipes in a directory or returns a single recipe if it's a file path instead of a directory.

The second would be a function that, given a recipe, returns a categorized (or uncategorized) ingredient list. This response model should also be significantly simplified. Something like (somewhat pseudocode):

pub type CategorizedIngredients: HashMap<String, Vec<Ingredient>>;

pub struct Ingredient {
  name: String,
  amount: f64,
  unit: Unit
}

impl Display for Ingredient {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    write!(f, "{} {} {}", self.amount, self.unit, self.name)
  }
}

Finally, a resolver for the Many(quanities) would be really helpful. I just picked the first one from the vec and used that one, but I'm not sure if that's correct.

The first two should be pretty straightforward to add, and I would be happy to take the first pass in a PR if that sounds appealing.

Thanks again for your help 🙂

Zheoni commented 1 year ago

Thanks for the feedback 😃 . In order:

  1. Oops, I will fix the typo.

  2. The function that returns recipes in a directory or a single recipe may be a good fit in cooklang-fs, a supporting library to interact with the file system, as I intend to keep the main cooklang lib independent from the way recipes are stored.

  3. [Recipe::ingredient_list_grouped] could be added to return a categorized list. I think that's a good idea. Also, a function to merge 2 ingredient lists, grouped or not.

    The main problem with the current ingredient list model I think is the index. I didn't want to copy the whole ingredient but wanted to still have access to all the data of the ingredient definition. I know it's not perfect, but I can't think of a better one. While development I tried putting the ingredients behind an Arc but I wasn't convinced.

  4. The Many(quantities) confusion is just lacking documentation. It is needed that way. For example, if you are trying to merge 2 recipes where the same ingredient is used with units that can't be added, the total quantity will be Many. For example, one recipe may use 1 bottle of wine and another 0.5 litres of wine. In that case Many will have 2 items because I cannot add 1 bottle and 0.5 litres into the same quantity.

If you want to try a PR of 2 (in this repo in the cooklang-fs crate) go for it. As for point 3, they are relatively simple functions and I am in the process of version 0.4 of the library, so I have no problem adding them myself. However if you think you have a good idea for the model, submit a PR in https://github.com/cooklang/cooklang-rs