CooklangEx

Hex.pmDocumentation

Elixir bindings for the canonical Cooklang parser, powered by cooklang-rs via Rustler NIFs.

Features

Installation

Add cooklang_ex to your list of dependencies in mix.exs:

def deps do
  [
    {:cooklang_ex, "~> 0.1.0"}
  ]
end

Requirements

Quick Start

# Parse a simple recipe
recipe_text = """
>> servings: 2
>> time: 15 minutes

Crack @eggs{3} into a #bowl{large} and whisk until fluffy.

Heat @butter{2%tbsp} in a #pan{non-stick} over medium heat.

Pour egg mixture into pan and cook for ~{3%minutes}, stirring gently.

Season with @salt{} and @black pepper{} to taste.
"""

{:ok, recipe} = CooklangEx.parse(recipe_text)

# Access parsed data
recipe.metadata
# => %{"servings" => "2", "time" => "15 minutes"}

recipe.ingredients
# => [
#   %CooklangEx.Recipe.Ingredient{name: "eggs", quantity: %{value: 3.0, unit: nil}},
#   %CooklangEx.Recipe.Ingredient{name: "butter", quantity: %{value: 2.0, unit: "tbsp"}},
#   %CooklangEx.Recipe.Ingredient{name: "salt", quantity: nil},
#   %CooklangEx.Recipe.Ingredient{name: "black pepper", quantity: nil}
# ]

recipe.cookware
# => [
#   %CooklangEx.Recipe.Cookware{name: "bowl", quantity: %{value: "large"}},
#   %CooklangEx.Recipe.Cookware{name: "pan", quantity: %{value: "non-stick"}}
# ]

recipe.timers
# => [%CooklangEx.Recipe.Timer{quantity: %{value: 3.0, unit: "minutes"}}]

Scaling Recipes

Scale recipes to different serving sizes:

recipe_text = """
>> servings: 4

Mix @flour{400%g} with @water{250%ml}.
Add @yeast{1%packet} and @salt{1%tsp}.
"""

# Scale from 4 servings to 8
{:ok, scaled} = CooklangEx.parse_and_scale(recipe_text, 8)

# Quantities are automatically doubled
hd(scaled.ingredients).quantity.value
# => 800.0 (was 400)

Cooklang Syntax Reference

Ingredients

@ingredient           # Simple ingredient
@eggs{3}              # With quantity
@flour{200%g}         # With quantity and unit
@ground black pepper{} # Multi-word name

Cookware

#pan                  # Simple cookware
#bowl{large}          # With size/description
#mixing bowl{}        # Multi-word name

Timers

~{10%minutes}         # Duration timer
~{30%seconds}         # Another timer
~name{5%minutes}      # Named timer

Metadata

>> servings: 4
>> source: https://example.com/recipe
>> time: 30 minutes

Comments

-- This is a comment
Add @salt{} -- inline comment

Steps

Steps are separated by blank lines:

First step here.

Second step here.

Third step here.

API Reference

CooklangEx.parse/2

Parse a Cooklang recipe string.

{:ok, recipe} = CooklangEx.parse(text)
{:ok, recipe} = CooklangEx.parse(text, all_extensions: false)

CooklangEx.parse!/2

Parse a recipe, raising on error.

recipe = CooklangEx.parse!(text)

CooklangEx.parse_and_scale/3

Parse and scale a recipe to target servings.

{:ok, recipe} = CooklangEx.parse_and_scale(text, 8)

CooklangEx.ingredients/1

Extract just the ingredients list.

{:ok, ingredients} = CooklangEx.ingredients(text)

CooklangEx.cookware/1

Extract just the cookware list.

{:ok, cookware} = CooklangEx.cookware(text)

CooklangEx.metadata/1

Extract just the metadata map.

{:ok, metadata} = CooklangEx.metadata(text)

Extensions

The parser supports several extensions to the base Cooklang specification. By default, all extensions are enabled. Disable them with:

CooklangEx.parse(text, all_extensions: false)

Extensions include:

See the cooklang-rs extensions documentation for details.

Release Process

Creating a new release is simple - just create and publish a GitHub release with a version tag:

  1. Create a new release on GitHub with a version tag (e.g., v0.1.0)
  2. Publish the release - The CI workflow automatically:
    • Updates the VERSION file
    • Runs validation and tests
    • Publishes to Hex.pm
    • Publishes documentation to HexDocs

The GitHub release tag is the source of truth for versioning. The VERSION file in the repository is automatically updated to match the release tag.

Development

# Clone the repo
git clone https://github.com/yourusername/cooklang_ex
cd cooklang_ex

# Optional: if you're using asdf, set up your versioning
cp .tool-versions.example .tool-versions

# Install dependencies
mix deps.get

# Compile (this will also compile the Rust NIF)
mix compile

# Run tests
mix test

License

MIT License - see LICENSE for details.

Acknowledgments