Bindings
This repository exports UniFFI difined bindings that can be used to call Rust Cooklang parser code from languages other than Rust: Kotlin, Swift, Ruby, Python and some other languages.
UniFFI
UniFFI is a brilliant way to define a cross-language interface and associated tools. Rust compiles a C-compatible library with UniFFI metadata baked. Based on this metadata UniFFI compiler can create snippets of code in foreign language that mirrors exposed Rust API.
This particular library employes new-ish procedural macroses to define exported methods and data-types.
Exposed API
This library exports methods:
// full parsing, returns full recipe object with meta
parse_recipe ;
// fast metadata parsing, recipe text is not parsed
parse_metadata ;
// parse aisle config to use in shopping list
parse_aisle_config ;
// dereferences component reference to component
// usage example:
// let ingredient = deref_component(recipe, Item::IngredientRef { index: 0 });
deref_component ;
// dereferences ingredient reference to ingredient
// usage example:
// let ingredient = deref_ingredient(recipe, 0);
deref_ingredient ;
// dereferences cookware reference to cookware
// usage example:
// let cookware = deref_cookware(recipe, 0);
deref_cookware ;
// dereferences timer reference to timer
// usage example:
// let timer = deref_timer(recipe, 0);
deref_timer ;
// combines ingredient lists into one
// usage example:
// let all_recipe_ingredients_combined = combine_ingredients(recipe.ingredients);
// if multiple recipes need to be combined, combine their ingredients lists and pass them to this method
combine_ingredients ;
// combines ingredient lists into one
// usage example:
// let combined_ingredients_from_section1 = combine_ingredients_selected(recipe.ingredients, section1.ingredient_refs);
// let combined_ingredients_from_step1 = combine_ingredients_selected(recipe.ingredients, step1.ingredient_refs);
combine_ingredients_selected ;
Exposed data structures
/// A recipe is a collection of sections, each containing blocks of content.
/// Represents a distinct section of a recipe, optionally with a title
/// A block can either be a cooking step or a note
/// Represents a single cooking instruction step
/// A text note within the recipe
/// Represents an ingredient in the recipe
/// Represents a piece of cookware used in the recipe
/// Represents a timer in the recipe
/// Represents an item in the recipe
/// Represents a quantity in the recipe
/// Represents a value in the recipe
/// Represents the metadata of the recipe
type CooklangMetadata = ;
/// Represents a list of ingredients that are grouped by name and quantity
type IngredientList = ;
/// Represents a grouped quantity for multiple unit types
// \
// |- <litre,Number> => 1.2
// |- <litre,Text> => half
// |- <,Text> => pinch
// |- <,Empty> => Some
type GroupedQuantity = ;
/// Represents a grouped quantity key
/// Represents the type of the grouped quantity
Shopping list usage example
Not all categories from AisleConfig are referenced in a shopping list. There could be "Other" category if not defined in the config.
// parse
let recipe = parse_recipe;
let config = parse_aisle_config;
// object which we'll use for rendering
let mut result = New;
// iterate over each recipe ingredients and fill results into result object.
let all_recipe_ingredients_combined = combine_ingredients;
all_recipe_ingredients_combined.iter.for_each;
Building for Android
Prepare
Install rustup https://www.rust-lang.org/tools/install.
Then add Android targets.
rustup target add aarch64-linux-android
rustup target add armv7-linux-androideabi
rustup target add i686-linux-android
rustup target add x86_64-linux-android
Install Android NDK https://developer.android.com/studio/projects/install-ndk#default-version.
Add ndk linkers to the PATH variable. Example for ~/.zshrc:
export PATH=$PATH:/Users/dubadub/Library/Android/sdk/ndk/25.2.9519653/toolchains/llvm/prebuilt/darwin-x86_64/bin/
Build
Build library:
cargo build --lib --target=x86_64-linux-android --release
Biuld foreight language bindings (this will output Kotlin code into ./out dir:
cargo run --features="uniffi/cli" \
--bin uniffi-bindgen generate \
--library target/x86_64-linux-android/release/libcooklang.so \
--language kotlin \
--out-dir out
See example of a Gradle config here with all required tasks.
Building for iOS
Prepare
Install rustup https://www.rust-lang.org/tools/install.
Then add iOS targets.
rustup target add aarch64-apple-ios
rustup target add x86_64-apple-ios
Install iOS SDK https://developer.apple.com/xcode/resources/.
Add ndk linkers to the PATH variable. Example for ~/.zshrc:
export PATH=$PATH:/Users/dubadub/Library/Android/sdk/ndk/25.2.9519653/toolchains/llvm/prebuilt/darwin-x86_64/bin/
Build
Build library:
cargo build --lib --target=x86_64-apple-ios --release
Biuld foreight language bindings (this will output Swift code into ./out dir:
cargo run --features="uniffi/cli" \
--bin uniffi-bindgen generate \
--config uniffi.toml \
--library ../target/x86_64-apple-ios/release/libcooklang_bindings.a \
--language swift \
--out-dir out
See example of a Xcode project here.
Combine into universal library:
mkdir -p ../target/universal/release
lipo -create -output ../target/universal/release/libcooklang_bindings.a \
../target/x86_64-apple-ios/release/libcooklang_bindings.a \
../target/aarch64-apple-ios/release/libcooklang_bindings.a
xcodebuild -create-xcframework
-library ../target/aarch64-apple-ios/release/libcooklang_bindings.a
-library ../target/x86_64-apple-ios/release/libcooklang_bindings.a
-output CooklangParserFFI.xcframework