Calimero Rust SDK
This guide provides a comprehensive reference of the essential macros and functionality provided by the Calimero SDK for building P2P Rust applications.
Core Macros
#[app::state]
Marks a struct as the application state. The state struct must implement
BorshSerialize
and BorshDeserialize
. This macro automatically generates the
Self::external()
method for external proposal functionality.
use calimero_sdk::borsh::{BorshDeserialize, BorshSerialize};
#[app::state(emits = for<'a> MyEvent<'a>)]
#[derive(Default, BorshSerialize, BorshDeserialize)]
#[borsh(crate = "calimero_sdk::borsh")]
struct MyAppState {
// Your state fields here
users: calimero_storage::collections::UnorderedMap<String, UserProfile>,
}
Key Features:
- Automatically generates
Self::external()
method for proposal management - Integrates with Calimero's storage system
- Supports event emission specification
#[app::logic]
Marks an implementation block as containing the application logic. All public methods in this block become available as application endpoints.
#[app::logic]
impl MyAppState {
// Your methods here
pub fn add_user(&mut self, username: String, profile: UserProfile) -> app::Result<()> {
// Implementation
Ok(())
}
}
#[app::init]
Marks a method as the initializer, which is called when the application is first deployed.
#[app::logic]
impl MyAppState {
#[app::init]
pub fn init() -> Self {
Self {
users: calimero_storage::collections::UnorderedMap::new(),
}
}
// Example with init parameters
#[app::init]
pub fn init_with_params(
initial_admin: String,
max_users: u32,
is_public: bool,
) -> Self {
Self {
users: calimero_storage::collections::UnorderedMap::new(),
admin: initial_admin,
max_users,
is_public,
}
}
}
#[app::event]
Defines an event type that can be emitted by your application. Events support lifetime parameters for efficient string handling.
#[app::event]
pub enum MyEvent<'a> {
ValueUpdated { key: &'a str, value: &'a str },
ValueRemoved { key: &'a str },
UserAdded { username: &'a str },
}
Utility Macros
app::emit!
Emit events from your application. Events are only emitted if the transaction succeeds.
app::emit!(MyEvent::ValueUpdated {
key: &key,
value: &new_value
});
app::emit!(MyEvent::UserAdded { username: &username });
app::log!
Log messages for debugging and monitoring. These appear in the application logs.
app::log!("Setting key: {:?} to value: {:?}", key, value);
app::log!("User {} added successfully", username);
app::bail!
Return an error and exit the current function early.
if !self.users.contains_key(&username) {
app::bail!("User not found: {}", username);
}
app::err!
Create an error value for returning from functions.
return app::err!("Invalid input: {}", input);
Return Types
The SDK provides a convenient app::Result<T>
type alias:
use calimero_sdk::app;
pub fn my_function(&self) -> app::Result<String> {
// Your implementation
Ok("success".to_string())
}
Complete Example
Here's a complete example showing how these macros work together:
use calimero_sdk::borsh::{BorshDeserialize, BorshSerialize};
use calimero_sdk::{app, env};
use calimero_storage::collections::UnorderedMap;
#[app::event]
pub enum StoreEvent<'a> {
ValueSet { key: &'a str, value: &'a str },
ValueRemoved { key: &'a str },
}
#[app::state(emits = for<'a> StoreEvent<'a>)]
#[derive(Default, BorshSerialize, BorshDeserialize)]
#[borsh(crate = "calimero_sdk::borsh")]
struct Store {
values: UnorderedMap<String, String>,
}
#[app::logic]
impl Store {
#[app::init]
pub fn init() -> Self {
Self {
values: UnorderedMap::new(),
}
}
pub fn set(&mut self, key: String, value: String) -> app::Result<()> {
app::log!("Setting key: {:?} to value: {:?}", key, value);
if self.values.contains(&key)? {
app::emit!(StoreEvent::ValueSet {
key: &key,
value: &value
});
} else {
app::emit!(StoreEvent::ValueSet {
key: &key,
value: &value
});
}
self.values.insert(key, value)?;
Ok(())
}
pub fn get(&self, key: &str) -> app::Result<Option<String>> {
app::log!("Getting key: {:?}", key);
self.values.get(key).map_err(Into::into)
}
pub fn remove(&mut self, key: &str) -> app::Result<Option<String>> {
app::log!("Removing key: {:?}", key);
if let Some(value) = self.values.remove(key)? {
app::emit!(StoreEvent::ValueRemoved { key });
Ok(Some(value))
} else {
Ok(None)
}
}
}
Important Notes
State changes are atomic - if a method fails, all changes are rolled back
- Events are only emitted if the transaction succeeds
- Read-only operations have no network overhead
All public methods in the
#[app::logic]
block become available as application endpointsCollections return
Result<T, StoreError>
for most operations - handle errors appropriatelyThe
Self::external()
method is automatically generated by#[app::state]
for proposal management
Next Steps
- Learn about Calimero Collections for data storage
- See Rust SDK Deep Dive for advanced patterns