Skip to main content
Version: Next

Rust Integration

This guide explains how to integrate ABI generation with your Rust code, including attribute usage, macro patterns, and best practices for creating ABI-compatible applications.

Basic Integration

1.1 Application Structure

Start by creating a basic Calimero application structure:

// src/lib.rs
use calimero_abi_emitter::calimero_app;

#[calimero_app]
pub struct MyApp {
// Your application state
data: String,
count: u32,
}

impl MyApp {
pub fn new() -> Self {
Self {
data: String::new(),
count: 0,
}
}

pub fn process_data(&mut self, input: String) -> Result<String, String> {
self.data = input.clone();
self.count += 1;
Ok(format!("Processed: {}", input))
}

pub fn get_state(&self) -> AppState {
AppState {
data: self.data.clone(),
count: self.count,
}
}
}

#[derive(Clone, Debug)]
pub struct AppState {
pub data: String,
pub count: u32,
}

1.2 Cargo.toml Configuration

Add the required dependencies:

[package]
name = "my-calimero-app"
version = "0.1.0"
edition = "2021"

[build-dependencies]
calimero-abi-emitter = "0.1.0"

[dependencies]
calimero-abi-emitter = "0.1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

1.3 Build Script

Create a build.rs file:

// build.rs
use calimero_abi_emitter::emit_manifest;

fn main() {
emit_manifest().expect("Failed to generate ABI manifest");
}

Attribute Usage

2.1 Application Attributes

Use the #[calimero_app] attribute to mark your main application:

use calimero_abi_emitter::calimero_app;

#[calimero_app]
pub struct MyApp {
// Application state
}

// The attribute automatically generates ABI metadata for this struct

2.2 Method Attributes

Mark methods for ABI inclusion:

impl MyApp {
#[calimero_method]
pub fn public_api_method(&self, input: String) -> String {
// This method will be included in the ABI
format!("Processed: {}", input)
}

#[calimero_method]
pub fn get_data(&self) -> AppData {
// This method will be included in the ABI
AppData::default()
}

// This method will NOT be included in the ABI
fn internal_helper(&self) -> String {
"internal".to_string()
}
}

2.3 Event Attributes

Define events for your application:

use calimero_abi_emitter::{calimero_app, calimero_event};

#[calimero_app]
pub struct EventApp {
// Application state
}

#[calimero_event]
pub struct DataProcessed {
pub input: String,
pub output: String,
pub timestamp: u64,
}

#[calimero_event]
pub struct StateChanged {
pub old_state: AppState,
pub new_state: AppState,
}

impl EventApp {
pub fn process_data(&mut self, input: String) -> Result<String, String> {
let output = format!("Processed: {}", input);

// Emit event
self.emit_event(DataProcessed {
input: input.clone(),
output: output.clone(),
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
});

Ok(output)
}
}

2.4 Type Attributes

Configure type behavior for ABI generation:

use calimero_abi_emitter::{calimero_app, calimero_type};

#[calimero_app]
pub struct TypeApp {
// Application state
}

#[calimero_type]
#[derive(Clone, Debug)]
pub struct UserData {
pub id: u32,
pub name: String,
pub email: String,
pub created_at: u64,
}

#[calimero_type]
#[derive(Clone, Debug)]
pub enum UserStatus {
Active,
Inactive,
Suspended,
}

impl TypeApp {
pub fn create_user(&mut self, name: String, email: String) -> UserData {
UserData {
id: 1, // In real app, generate proper ID
name,
email,
created_at: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
}
}
}

Advanced Integration Patterns

3.1 Generic Applications

Handle generic types in your application:

use calimero_abi_emitter::{calimero_app, calimero_type};

#[calimero_app]
pub struct GenericApp<T> {
data: T,
}

impl<T> GenericApp<T>
where
T: Clone + Send + Sync + 'static,
{
pub fn new(data: T) -> Self {
Self { data }
}

pub fn get_data(&self) -> T {
self.data.clone()
}

pub fn set_data(&mut self, data: T) {
self.data = data;
}
}

// Specialize for specific types
pub type StringApp = GenericApp<String>;
pub type NumberApp = GenericApp<u32>;

3.2 Async Applications

Handle async methods in your application:

use calimero_abi_emitter::calimero_app;
use std::future::Future;

#[calimero_app]
pub struct AsyncApp {
// Application state
}

impl AsyncApp {
#[calimero_method]
pub async fn async_process(&self, input: String) -> Result<String, String> {
// Simulate async work
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
Ok(format!("Async processed: {}", input))
}

#[calimero_method]
pub fn sync_wrapper(&self, input: String) -> impl Future<Output = Result<String, String>> {
let future = self.async_process(input);
async move { future.await }
}
}

3.3 Error Handling

Implement proper error handling for ABI compatibility:

use calimero_abi_emitter::{calimero_app, calimero_type};

#[calimero_app]
pub struct ErrorHandlingApp {
// Application state
}

#[calimero_type]
#[derive(Clone, Debug)]
pub enum AppError {
ValidationError(String),
ProcessingError(String),
InternalError(String),
}

impl std::fmt::Display for AppError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AppError::ValidationError(msg) => write!(f, "Validation error: {}", msg),
AppError::ProcessingError(msg) => write!(f, "Processing error: {}", msg),
AppError::InternalError(msg) => write!(f, "Internal error: {}", msg),
}
}
}

impl std::error::Error for AppError {}

impl ErrorHandlingApp {
pub fn validate_input(&self, input: &str) -> Result<(), AppError> {
if input.is_empty() {
return Err(AppError::ValidationError("Input cannot be empty".to_string()));
}
if input.len() > 100 {
return Err(AppError::ValidationError("Input too long".to_string()));
}
Ok(())
}

pub fn process_data(&self, input: String) -> Result<String, AppError> {
self.validate_input(&input)?;

if input.contains("error") {
return Err(AppError::ProcessingError("Input contains error keyword".to_string()));
}

Ok(format!("Processed: {}", input))
}
}

Type System Integration

4.1 Standard Types

Use standard Rust types that map well to ABI types:

use calimero_abi_emitter::calimero_app;

#[calimero_app]
pub struct StandardTypeApp {
// Standard types that map well to ABI
pub id: u32,
pub name: String,
pub is_active: bool,
pub score: f64,
pub tags: Vec<String>,
pub metadata: std::collections::BTreeMap<String, String>,
}

4.2 Custom Types

Define custom types with proper ABI compatibility:

use calimero_abi_emitter::{calimero_app, calimero_type};
use serde::{Deserialize, Serialize};

#[calimero_app]
pub struct CustomTypeApp {
// Application state
}

#[calimero_type]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct User {
pub id: u32,
pub username: String,
pub email: String,
pub profile: UserProfile,
pub settings: UserSettings,
}

#[calimero_type]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct UserProfile {
pub first_name: String,
pub last_name: String,
pub bio: Option<String>,
pub avatar_url: Option<String>,
}

#[calimero_type]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct UserSettings {
pub theme: String,
pub notifications: bool,
pub privacy_level: PrivacyLevel,
}

#[calimero_type]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum PrivacyLevel {
Public,
Friends,
Private,
}

4.3 Collection Types

Handle collection types properly:

use calimero_abi_emitter::{calimero_app, calimero_type};
use std::collections::{BTreeMap, HashMap, VecDeque};

#[calimero_app]
pub struct CollectionApp {
// Application state
}

#[calimero_type]
#[derive(Clone, Debug)]
pub struct DataContainer {
// Vec maps to list<T> in ABI
pub items: Vec<String>,

// BTreeMap maps to map<K,V> in ABI
pub metadata: BTreeMap<String, String>,

// HashMap maps to map<K,V> in ABI
pub cache: HashMap<String, u32>,

// VecDeque maps to list<T> in ABI
pub queue: VecDeque<String>,
}

Protocol-Specific Integration

5.1 Ethereum Integration

Configure your application for Ethereum:

use calimero_abi_emitter::{calimero_app, Protocol, ProtocolFeatures};

#[calimero_app(
protocol = Protocol::Ethereum,
features = [
ProtocolFeatures::GasOptimization,
ProtocolFeatures::EventLogging
]
)]
pub struct EthereumApp {
// Ethereum-specific application
}

impl EthereumApp {
pub fn transfer_tokens(&mut self, to: String, amount: u64) -> Result<(), String> {
// Ethereum-specific logic
Ok(())
}
}

5.2 NEAR Integration

Configure your application for NEAR:

use calimero_abi_emitter::{calimero_app, Protocol, ProtocolFeatures};

#[calimero_app(
protocol = Protocol::NEAR,
features = [
ProtocolFeatures::AccountOptimization,
ProtocolFeatures::StorageOptimization
]
)]
pub struct NearApp {
// NEAR-specific application
}

impl NearApp {
pub fn call_contract(&self, contract_id: String, method: String, args: String) -> Result<String, String> {
// NEAR-specific logic
Ok("Success".to_string())
}
}

5.3 Multi-Protocol Integration

Support multiple protocols in a single application:

use calimero_abi_emitter::{calimero_app, Protocol, ProtocolConfig};

#[calimero_app(
protocols = [Protocol::Ethereum, Protocol::NEAR, Protocol::ICP],
protocol_config = ProtocolConfig {
ethereum: EthereumConfig {
gas_optimization: true,
},
near: NearConfig {
account_optimization: true,
},
icp: IcpConfig {
canister_optimization: true,
},
}
)]
pub struct MultiProtocolApp {
// Multi-protocol application
}

impl MultiProtocolApp {
pub fn process_cross_chain(&self, data: String, target_protocol: String) -> Result<String, String> {
match target_protocol.as_str() {
"ethereum" => self.process_ethereum(data),
"near" => self.process_near(data),
"icp" => self.process_icp(data),
_ => Err("Unsupported protocol".to_string()),
}
}

fn process_ethereum(&self, data: String) -> Result<String, String> {
// Ethereum-specific processing
Ok(format!("Ethereum: {}", data))
}

fn process_near(&self, data: String) -> Result<String, String> {
// NEAR-specific processing
Ok(format!("NEAR: {}", data))
}

fn process_icp(&self, data: String) -> Result<String, String> {
// ICP-specific processing
Ok(format!("ICP: {}", data))
}
}

Testing Integration

6.1 Unit Testing

Write unit tests for your ABI-compatible code:

#[cfg(test)]
mod tests {
use super::*;
use calimero_abi_emitter::validate_abi;

#[test]
fn test_app_creation() {
let app = MyApp::new();
assert_eq!(app.get_state().count, 0);
}

#[test]
fn test_data_processing() {
let mut app = MyApp::new();
let result = app.process_data("test".to_string());
assert!(result.is_ok());
assert_eq!(result.unwrap(), "Processed: test");
}

#[test]
fn test_abi_validation() {
// This test ensures the ABI is valid
let abi = include_bytes!("../target/abi_conformance.abi.json");
validate_abi(abi).expect("ABI validation failed");
}
}

6.2 Integration Testing

Test your application with ABI validation:

#[cfg(test)]
mod integration_tests {
use super::*;
use calimero_abi_emitter::{validate_abi, AbiManifest};

#[test]
fn test_abi_conformance() {
let abi_json = include_str!("../target/abi_conformance.abi.json");
let abi: AbiManifest = serde_json::from_str(abi_json).unwrap();

// Validate that all expected methods are present
let method_names: Vec<&String> = abi.methods.iter().map(|m| &m.name).collect();
assert!(method_names.contains(&"process_data".to_string()));
assert!(method_names.contains(&"get_state".to_string()));

// Validate that all expected types are present
let type_names: Vec<&String> = abi.types.keys().collect();
assert!(type_names.contains(&"AppState".to_string()));
}
}

Best Practices

7.1 Code Organization

Organize your code for ABI compatibility:

// src/lib.rs
use calimero_abi_emitter::calimero_app;

// Main application
#[calimero_app]
pub struct MyApp {
// Application state
}

impl MyApp {
// Public API methods
}

// src/types.rs
use calimero_abi_emitter::calimero_type;

// Type definitions
#[calimero_type]
pub struct AppState {
// State definition
}

// src/events.rs
use calimero_abi_emitter::calimero_event;

// Event definitions
#[calimero_event]
pub struct DataProcessed {
// Event definition
}

7.2 Error Handling

Implement consistent error handling:

use calimero_abi_emitter::{calimero_app, calimero_type};

#[calimero_app]
pub struct ErrorHandlingApp {
// Application state
}

#[calimero_type]
#[derive(Clone, Debug)]
pub enum AppError {
ValidationError(String),
ProcessingError(String),
InternalError(String),
}

impl AppError {
pub fn validation(msg: impl Into<String>) -> Self {
Self::ValidationError(msg.into())
}

pub fn processing(msg: impl Into<String>) -> Self {
Self::ProcessingError(msg.into())
}

pub fn internal(msg: impl Into<String>) -> Self {
Self::InternalError(msg.into())
}
}

7.3 Documentation

Document your ABI-compatible code:

use calimero_abi_emitter::calimero_app;

#[calimero_app]
/// Main application struct for data processing
pub struct MyApp {
/// Application data storage
data: String,
/// Processing counter
count: u32,
}

impl MyApp {
/// Creates a new instance of the application
pub fn new() -> Self {
Self {
data: String::new(),
count: 0,
}
}

/// Processes input data and returns the result
///
/// # Arguments
/// * `input` - The input data to process
///
/// # Returns
/// * `Ok(String)` - The processed data
/// * `Err(String)` - Error message if processing fails
pub fn process_data(&mut self, input: String) -> Result<String, String> {
self.data = input.clone();
self.count += 1;
Ok(format!("Processed: {}", input))
}
}

Troubleshooting

8.1 Common Issues

Build Failures: Ensure all required dependencies are included in Cargo.toml

Type Mismatches: Check that your type definitions are compatible with the target protocol

Attribute Errors: Verify that you're using the correct attribute syntax

8.2 Debug Tips

Enable debug mode to troubleshoot issues:

// build.rs
use calimero_abi_emitter::{emit_manifest_with_config, AbiConfig, DebugConfig};

fn main() {
let config = AbiConfig {
debug: DebugConfig {
enabled: true,
verbose_logging: true,
save_intermediate_files: true,
},
// ... other configuration
};

emit_manifest_with_config(config)
.expect("Failed to generate ABI manifest");
}

Next Steps

Now that you understand Rust integration:

  • Validation - Set up ABI validation and testing
  • Protocol Support - Configure support for specific protocols
  • Tools - Use ABI tools for debugging and validation
  • Examples - See real-world examples and patterns
Was this page helpful?
Need some help? Check Support page