How to Call Rust Code from React Native CLI Android App
How to call Rust code from a React Native CLI Android app
Suyash Singh
Posted by Suyash Singh
on May 1, 2024
Photo by Rahul on Unsplash

You would want to use Rust with React Native when you need high performance and safety in your mobile app. Rust is a fast and memory-efficient language that can handle complex computations and tasks. By integrating Rust with React Native, you can write performance-critical parts of your app in Rust, while using React Native for the user interface. This combination allows you to create apps that are both responsive and reliable. Rust’s safety features also help prevent bugs and crashes, making your app more stable and secure. For example with rust and react native you can develop high performance mobile apps for the use cases mentioned below:

  • High-Performance Games: Develop mobile games where the game engine and performance-critical logic are implemented in Rust for optimal speed and efficiency, while React Native is used for the user interface and touch interactions.

  • Real-Time Messaging Apps: Create real-time messaging applications that utilize Rust for handling concurrent network connections and message processing, ensuring low latency and high performance, while React Native manages the chat UI and user experience.

  • Augmented Reality (AR) Apps: Build AR applications where Rust handles the complex calculations and data processing for rendering AR objects and scenes, while React Native is used to develop the user interface and control elements.

  • Cryptographic Wallets: Develop secure cryptocurrency wallets where Rust is used for cryptographic operations and secure key management, providing enhanced security and performance, while React Native manages the user interface and user interactions.

  • Health and Fitness Trackers: Create health and fitness tracking apps that use Rust for processing and analyzing sensor data from devices such as heart rate monitors and accelerometers, while React Native provides a responsive and engaging user interface.

Setup Toolchain & Environment

Developing a React Native Android app that utilizes Rust code involves several moving parts. Below, we will cover all these components in detail. This guide will provide all the necessary plumbing required for calling Rust code from React Native, ensuring you have a smooth and efficient development experience.

First, I will cover how to set up the prerequisite Android tools, including the SDK, NDK, and Java, which are essential for developing Android applications. Next, I will guide you through configuring Gradle for React Native Android to use Kotlin, and also ensuring that the NDK is properly set up to handle the compiled Rust library.

After that, I will walk you through setting up the Rust toolchain, which includes installing Rust and configuring the necessary build tools. Following the Rust setup, I will explain how to register custom React Native modules in Kotlin, enabling seamless integration between Rust and React Native.

Finally, I will show you how to call the Rust functions from React Native TypeScript code. By following this comprehensive guide, you will be able to harness the power and performance of Rust in your React Native Android applications, combining the best of both worlds to create high-performance, secure, and user-friendly mobile apps.

Assumptions

Let’s move ahead in this comprehensive guide with the following assumptions:

  1. We are going to call our app as sample-app
  2. We are going to use:
    • Open JDK 17.0.10
    • NDK 25.2.9519653
    • Gradle 8.6-all
    • Rust rustc 1.77.2
    • Uniffi v0.28.0
    • Node v21.1.7
    • npm v10.5.0

Since, the technology for this setup is ever evolving, these assumptions are required for a successful build.

Android sdk

What is Android SDK and Its Significance for Developing React Native Apps

The Android Software Development Kit (SDK) is a set of tools and libraries provided by Google for developers to create applications for the Android platform. It includes a comprehensive set of development tools, such as libraries, debugger, emulator, and documentation, which are essential for building, testing, and debugging Android applications.

For developing React Native apps, the Android SDK is crucial because React Native leverages native components and APIs of the Android platform to build applications. React Native uses the Android SDK to compile JavaScript code into native code, interact with device hardware and sensors, handle user interface elements, and access various platform-specific features. In essence, the Android SDK acts as a bridge between the JavaScript code of a React Native app and the underlying Android platform, enabling developers to create high-quality, performant mobile applications.

Common Ways to Setup Android SDK

1. Using Android Studio:
  • Android Studio is the official Integrated Development Environment (IDE) for Android development, and it includes built-in support for installing and managing the Android SDK. To set up the Android SDK using Android Studio:
  • Download and install Android Studio from the official website.
  • Launch Android Studio and follow the setup wizard to install necessary components, including the Android SDK.
  • Android Studio will automatically download and install the required SDK components based on your selected configuration.
2. Using Command Line Interface (CLI) in Linux:
  • Alternatively, you can set up the Android SDK on Linux using the command line. Here’s a basic outline of the steps:
  • Download the Android SDK Command Line Tools from the official Android developer website.
  • Extract the downloaded archive to a suitable location on your system.
  • Set the ANDROID_HOME environment variable to the path where you extracted the SDK.
  • Add the SDK’s tools and platform-tools directories to your system’s PATH variable.
  • Optionally, you can use the sdkmanager command-line tool to install additional SDK components as needed.

Common Environment Variables for Android SDK

When setting up the Android SDK, several environment variables play a crucial role in configuring the development environment. Some of the common environment variables include:

  • ANDROID_HOME: This variable points to the root directory of the Android SDK installation. It is used by various tools and scripts to locate the SDK components.
  • PATH: It should include the tools and platform-tools directories within the Android SDK, allowing you to run Android development commands from the terminal.
  • JAVA_HOME: This variable points to the root directory of the Java Development Kit (JDK) installation. Android development requires Java, so setting this variable ensures that the Android SDK can find the necessary Java tools.
  • ANDROID_SDK_ROOT: Similar to ANDROID_HOME, this variable also points to the root directory of the Android SDK installation. It’s used by some tools as an alternative to ANDROID_HOME.

By correctly configuring these environment variables, developers can ensure that the Android SDK is properly integrated into their development environment, allowing for smooth and efficient development of React Native applications targeting the Android platform.

⚠️

Explanation for how to setup these variables is beyond the scope of this post.

JDK

The specific JDK version used for this sample-app development is jdk-17.0.10.

⚠️

Explanation for setting up JDK is beyond the scope of this post.

NDK - Native Development Kit

In order to successfully call rust code in our react native android app, we would need NDK to allow calling rust methods in our Java/kotlin code. NDK can be installed either manually using CLI or more conveniently through Android Studio. The specific version of NDK used is 25.2.9519653.

⚠️

Explanation for how to setup NDK is beyond the scope of this post.

rustup

To install rust on the system, rustup has been used. The version used is rustc 1.77.2.

⚠️

Explanation for how to setup rust using rustup is beyond the scope of this post. Please, refer https://rustup.rs/ for guidance.

cross

  • Cross is a utility used for “Zero setup” cross compilation and “cross testing” of Rust crates
  • Cross can be installed using this command:
cargo install cross --git https://github.com/cross-rs/cross

Later in this post, we would be using cross to cross compile our rust code for android specific architectures. Cross utilizes docker containers & emphasizes on isolation to cross compile rust code for different target platforms.

I say this with full responsibility, Cross has been a super time saver for me in different situations.

Toolchain & environment checklist

Essentially, the toolchain & environment necessary for calling Rust code using react native consist of:

  • Cross
  • ANDROID_SDK_ROOT environment variable
  • ANDROID_HOME environment variable
  • ANDROID_NDK_HOME environment variable
  • JAVA_HOME environment variable
  • adb utility
  • rustup with rust installed
  • NDK bundle
  • JDK
  • Node (v21.1.7)
  • npm (v10.5.0)
  • yarn

Setup Project — Frontend

React Native

  1. Scaffold a new react-native app using:
npx react-native@latest init SampleApp --directory sample-app --title HelloWorld
ℹ️

For our android layer, we are going to use kotlin instead of java.

  1. Configure android/gradle/wrapper/gradle-wrapper.properties if you want to customize gradle for successful kotlin setup for our android app.
android/gradle/wrapper/gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
  1. Update android/build.gradle. We are using NDK version 25.2.9519653 for our app.
android/build.gradle
buildscript {
    ext {
        buildToolsVersion = "34.0.0"
        minSdkVersion = 23
        compileSdkVersion = 34
        targetSdkVersion = 34
 
        ndkVersion = "25.2.9519653"
        kotlinVersion = "1.9.22"
    }
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath("com.android.tools.build:gradle")
        classpath("com.facebook.react:react-native-gradle-plugin")
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
    }
}
 
apply plugin: "com.facebook.react.rootproject"
  1. Update android/app/build.gradle to correctly set defaultConfig.ndk.abiFilters for the target devices’ architectures. We are also creating a task named build_backend_for_android at the bottom of this file for compiling our rust code using cross we have setup above. This task sets the working directory and executes the build_backend_for_android.sh script.
android/app/build.gradle
 
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'idea'
}
 
apply plugin: "com.android.application"
apply plugin: "com.facebook.react"
apply plugin: "kotlin-android"
 
react {
    
}
 
def enableProguardInReleaseBuilds = false
 
def jscFlavor = 'org.webkit:android-jsc:+'
 
android {
    ndkVersion rootProject.ext.ndkVersion
    compileSdkVersion rootProject.ext.compileSdkVersion
 
    namespace "com.sampleapp"
    defaultConfig {
        applicationId "com.sampleapp"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode 1
        versionName "1.0"
        ndk {
            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', 'aarch'
        }
    }
    signingConfigs {
        debug {
            storeFile file('debug.keystore')
            storePassword 'android'
            keyAlias 'androiddebugkey'
            keyPassword 'android'
        }
    }
    buildTypes {
        debug {
            signingConfig signingConfigs.debug
        }
        release {
            
            
            signingConfig signingConfigs.debug
            minifyEnabled enableProguardInReleaseBuilds
            proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
        }
    }
    buildFeatures {
        buildConfig = true
    }
 
    ndkVersion '25.2.9519653'
}
 
dependencies {
    
    implementation("com.facebook.react:react-android")
    implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
    implementation "net.java.dev.jna:jna:5.13.0@aar"
 
    if (hermesEnabled.toBoolean()) {
        implementation("com.facebook.react:hermes-android")
    } else {
        implementation jscFlavor
    }
}
 
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
 
task build_backend_for_android(type: Exec) {
    def uniffiPath = "${buildDir}/generated/source/uniffi/java"
    def rustBackendDir =  "${project.projectDir}/../../backend"
    workingDir(rustBackendDir)
    commandLine './build_backend_for_android.sh'
 
}
 
preBuild.dependsOn 'build_backend_for_android'
 

[bindings.kotlin] package_name = “com.sampleapp.uniffi” cdylib_name = “backend”

Setup Project — Backend

create new rust lib

Now let’s finally write the rust code which will be called in our react native android sample-app. We are going to store our rust code in the project root under the backend directory.

# Run this in the root of the project
cargo new --lib backend

Build script

Update backend/build_backend_for_android.sh. In this script we are targeting android aarch64, if the device/emulator you have setup on your system is different, change them accordingly.

backend/build_backend_for_android.sh
#!/usr/bin/bash
 
######################################
###### Compile & setup for ARM 64 android
###### Generate kotlin bindings from code
######################################
cross run --target=aarch64-linux-android --features=uniffi/cli --bin=uniffi-bindgen generate src/backend.udl --language kotlin --out-dir=.
cross build --target=aarch64-linux-android --release --lib
rm -rf ../android/app/src/main/jniLibs 
mkdir -p ../android/app/src/main/jniLibs/arm64-v8a 
cp target/aarch64-linux-android/release/libbackend.so ../android/app/src/main/jniLibs/arm64-v8a/libbackend.so
######################################
 
######################################
###### Generate Kotlin bindings from library
######################################
# cross run --target=aarch64-linux-android --bin uniffi-bindgen generate --library target/aarch64-linux-android/release/libbackend.so --language kotlin --out-dir .
######################################
 
# useful when using udl definitions
# cross run --target=aarch64-linux-android --bin=uniffi-bindgen generate src/backend.udl --language kotlin --out-dir=.
 
 
######################################
###### Cleanup
######################################
rm -rf ../android/app/src/main/java/com/sampleapp/uniffi
cp -r com/sampleapp/uniffi ../android/app/src/main/java/com/sampleapp/uniffi
rm -rf com
######################################
 

uniffi setup

We are going to use Mozilla’s UniFFI. UniFFI is a tool that automatically generates foreign-language bindings targeting Rust libraries. We would be using it to generate Kotlin bindings for our Rust code. Using these kotlin bindings we would be able to call the methods exposed through the rust library, in our react native android kotlin layer.

backend/cargo.toml
[package]
name = "backend"
version = "0.1.0"
edition = "2021"
 
[lib]
crate-type = ["cdylib"]
name = "backend"
 
[dependencies]
uniffi = { version = "0.28.0", features = ["cli"] }
 
[build-dependencies]
uniffi = { version = "0.28.0", features = ["build"]}
uniffi_build = { version = "0.28.0", features = ["builtin-bindgen"] }
 
[[bin]]
name = "uniffi-bindgen"
path = "uniffi-bindgen.rs"

UniFFI relies on Interface Definition Language UDL file which describes the methods we want to expose in our rust code. uniffi build dependencies read this file and links our library methods in the final rust library that is created. Additionally, using this UDL kotlin bindings are generated.

ℹ️

Learn more about the schema UDL supports on the official docs for it.

backend/src/backend.udl
namespace backend {
  string rusty_hello(string text);
};

We then need to tell rust in our build.rs to use the uniffi_build crate to generate our kotlin bindings.

backend/build.rs
fn main() {
    uniffi_build::generate_scaffolding("src/backend.udl").unwrap();
}

If you take a look at our cargo.toml file line 17-20, for generating the kotlin bindings, we have instructed cargo to register a binary for path uniffi-bindgen.rs. Let’s create this file for triggering the FFI bindings generation.

backend/uniffi-bindgen.rs
fn main() {
    uniffi::uniffi_bindgen_main()
}

We can configure UniFFI to mark the generated kotlin bindings with specific package we want to use, and also the name of the library that will be available to the Android app using jniLibs. For that we need to create this toml file:

backend/uniffi.toml
[bindings.kotlin]
package_name = "com.sampleapp.uniffi"
cdylib_name = "backend"

Let’s finally include the scaffolding generated using uniffi in our library, and write a hello_world rust function.

backend/src/lib.rs
#![allow(unused)]
uniffi::include_scaffolding!("backend");
 
pub fn rusty_hello(text: String) -> String {
    format!("Hello {text}")
}
⚠️

The name of the functions in backend/src/lib.rs that we want to call from react native should match with their UDL namespace definitions.

kotlin setup

Finally, we need some kotlin glue code to be able to call our rust code from react native. React native provides native modules, we can register our own custom react native module for our react native app.

⚠️

Learn more about React Native Modules using the official docs.

The build_backend_for_android.sh script would move the UniFFI Kotlin FFI bindings to android/app/src/main/java/com/sampleapp/uniffi/backend.kt. We can then create a custom module which calls these rust library methods exposed via the kotlin FFI. For reference, here’s the backend/build_backend_for_android.sh file below:

backend/build_backend_for_android.sh
#!/usr/bin/bash
 
######################################
###### Compile & setup for ARM 64 android
###### Generate kotlin bindings from code
######################################
cross run --target=aarch64-linux-android --features=uniffi/cli --bin=uniffi-bindgen generate src/backend.udl --language kotlin --out-dir=.
cross build --target=aarch64-linux-android --release --lib
rm -rf ../android/app/src/main/jniLibs 
mkdir -p ../android/app/src/main/jniLibs/arm64-v8a 
cp target/aarch64-linux-android/release/libbackend.so ../android/app/src/main/jniLibs/arm64-v8a/libbackend.so
######################################
 
######################################
###### Generate Kotlin bindings from library
######################################
# cross run --target=aarch64-linux-android --bin uniffi-bindgen generate --library target/aarch64-linux-android/release/libbackend.so --language kotlin --out-dir .
######################################
 
# useful when using udl definitions
# cross run --target=aarch64-linux-android --bin=uniffi-bindgen generate src/backend.udl --language kotlin --out-dir=.
 
 
######################################
###### Cleanup
######################################
rm -rf ../android/app/src/main/java/com/sampleapp/uniffi
cp -r com/sampleapp/uniffi ../android/app/src/main/java/com/sampleapp/uniffi
rm -rf com
######################################
 
⚠️

Observe, using cross we are running the uniffi-bindgen utility for generating the Kotlin FFIs. Also, using cross we are generating libbackend.so which is being copied to android/app/src/main/jniLibs/arm64-v8a/. The way this works is, depending on the platform architecture, we need to copy the library .so files to their respective jniLibs directory. So, pay attention to the platform architecture you are targeting and accordingly modify the script as necessary.

⚠️

There are several ways through which JVM can call external libraries: JNI, JNA & JNR. The terms JNA, JNI, and JNR refer to different ways of interfacing Java with native code (c++, rust compiled code).

In layman terms, JNA is really easy to use since it doesn’t require the developers to write extra JNI code. But, generally speaking, it’s slower than JNI. Then, we have JNR which sits in between JNA and JNI in terms of performance and ease of use. To go in depth for these is out of scope for this post. You can read more about these here 1 2.

Coming back to the glue code we talked about above, we need to do the following:

  1. Create a new React Native Module, let’s call it BackendModule.
  2. Call the automatically generated FFI method helloWorld (this is generated automatically using our UDL definition).
  3. Create a new kotlin package.
  4. Register our BackendModule in our custom MyAppPackage.
  5. Register MyAppPackage with React Native Modules in kotlin.
android/app/src/main/java/com/sampleapp/BackendModule.kt
package com.sampleapp
import com.facebook.react.bridge.Promise
import android.util.Log
import com.sampleapp.uniffi.rustyHello
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
 
class BackendModule internal constructor(context: ReactApplicationContext?) : ReactContextBaseJavaModule(context) {
 
    override fun getName(): String {
        return "BackendModule"
    }
 
    @ReactMethod
    fun hello(text: String, promise: Promise) {
        var response = rustyHello(text)             
        Log.d("Kotlin BackendModule says:", "${response}")
        promise.resolve(response)
    }
}
 

In the file mentioned above android/app/src/main/java/com/sampleapp/BackendModule.kt, we are exposing the hello kotlin function. Part of this function signature, we are calling the helloWorld function which is being automatically mapped to our rust library hello_world method by UniFFI. If you think about it, it’s awesome how UniFFI is doing all this FFI heavy-lifting for us in a clean abstracted away! To understand how it does it, just take a look at the android/app/src/main/java/com/sampleapp/uniffi/backend.kt file, it’s using JNA!

We now need to create a new package. Using this package we will be able to register our newly created custom BackendModule with react native modules, and then it will be available in our react native app to consume.

ℹ️

Notice, we are using Promise as part of fn hello signature. There are several ways for interfacing react native java/kotlin layer and JS layer. You can read about other possibilities here.

android/app/src/main/java/com/sampleapp/MyAppPackage.kt
package com.sampleapp
import android.view.View
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ReactShadowNode
import com.facebook.react.uimanager.ViewManager
 
class MyAppPackage : ReactPackage {
    override fun createViewManagers(
            reactContext: ReactApplicationContext
    ): MutableList<ViewManager<View, ReactShadowNode<*>>> = mutableListOf()
    override fun createNativeModules(
            reactContext: ReactApplicationContext
    ): MutableList<NativeModule> = listOf(BackendModule(reactContext)).toMutableList()
}
 

Now, we need to register the package MyAppPackage we created above.

android/app/src/main/java/com/sampleapp/MainApplication.kt
 
package com.sampleapp
 
import android.app.Application
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.soloader.SoLoader
 
class MainApplication : Application(), ReactApplication {
 
  override val reactNativeHost: ReactNativeHost =
      object : DefaultReactNativeHost(this) {
        override fun getPackages(): List<ReactPackage> =
            PackageList(this).packages.apply {
              add(MyAppPackage())
            }
 
        override fun getJSMainModuleName(): String = "index"
 
        override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
 
        override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
        override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
      }
 
  override val reactHost: ReactHost
    get() = getDefaultReactHost(applicationContext, reactNativeHost)
 
  override fun onCreate() {
    super.onCreate()
    SoLoader.init(this, false)
    if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
      load()
    }
  }
}
 

Frontend - Final pieces coming together

Finally, all that’s left is to consume our very own BackendModule React Native Module in our typescript react native layer.

package json

This is how the package.json file looks for our project sample-app.

package.json
{
  "name": "sampleapp",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "android": "react-native run-android",
    "ios": "react-native run-ios",
    "lint": "eslint .",
    "start": "react-native start",
    "test": "jest"
  },
  "dependencies": {
    "react": "18.2.0",
    "react-native": "0.74.1"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0",
    "@babel/preset-env": "^7.20.0",
    "@babel/runtime": "^7.20.0",
    "@react-native/babel-preset": "0.74.83",
    "@react-native/eslint-config": "0.74.83",
    "@react-native/metro-config": "0.74.83",
    "@react-native/typescript-config": "0.74.83",
    "@types/react": "^18.2.6",
    "@types/react-test-renderer": "^18.0.0",
    "babel-jest": "^29.6.3",
    "eslint": "^8.19.0",
    "jest": "^29.6.3",
    "prettier": "2.8.8",
    "react-test-renderer": "18.2.0",
    "typescript": "5.0.4"
  },
  "engines": {
    "node": ">=18"
  },
  "packageManager": "yarn@3.6.4"
}

React native Frontend Typescript code

Now, in our frontend code we can call these custom react native modules methods (added by our package MyAppPackage). To do so we need to import NativeModules from react-native package. And, then we can obtain our custom module by using TS destructuring for convenience const {BackendModule} = NativeModules;. Finally, we can now call our Kotlin hello method to return response from Rust’s hello_world function.

App.tsx
import React, {useState} from 'react';
import {Button, NativeModules, SafeAreaView, Text} from 'react-native';
 
const {BackendModule} = NativeModules;
 
const HelloFromRust = () => {
  const [value, setValue] = useState('');
  const onPress = async () => {
    let helloFromRust = await BackendModule.hello('from rust!');
    setValue(helloFromRust);
  };
  return (
    <>
      <Button
        title="Click To invoke Kotlin Native Module Code"
        color="#241584"
        onPress={onPress}
      />
      <Text style={{fontSize: 40}}>{value}</Text>
    </>
  );
};
 
function App(): JSX.Element {
  return (
    <SafeAreaView>
      <HelloFromRust />
    </SafeAreaView>
  );
}
export default App;
⚠️

We can use adb logcat to view logs printed in our kotlin layer.

adb logcat | grep -e BackendModule

Additionally, we can also directly print logs in our rust layer. For that we need to import certain crates in our library, which is beyond the scope of this post.

Conclusion

Without a doubt, this has been a really long comprehensive guide. Thank you for your patience for following through all along. I hope with this guide, you can successfully and swiftly create the skeleton for a react native + rust app.

As a todo, whenever I get the time for it, I will create a mason bricks scaffold for this entire guide. Or, if you can do it, please let me know about it. I would be more than happy to consume it! 😉