use blvm_node::Hash;
use blvm_sdk::module::prelude::*;
use blvm_sdk::module::MigrationContext;
use blvm_sdk::run_module_main;
use serde_json::Value;
use std::sync::Arc;
use tracing::info;
#[derive(Clone, Default, serde::Serialize, serde::Deserialize)]
#[config(name = "demo")]
pub struct DemoConfig {
#[serde(default)]
pub prefix: String,
#[serde(default)]
pub max_items: u32,
}
#[migration(version = 1)]
fn up_initial(ctx: &MigrationContext) -> anyhow::Result<()> {
ctx.put(b"schema_version", b"1")?;
Ok(())
}
#[migration(version = 2)]
fn up_add_items_tree(_ctx: &MigrationContext) -> anyhow::Result<()> {
Ok(())
}
const DATA_TREE: &str = "data";
#[derive(Clone)]
#[module(migrations = ((1, up_initial), (2, up_add_items_tree)))]
pub struct DemoModule {
#[allow(dead_code)]
config: DemoConfig,
}
#[module]
impl DemoModule {
#[command]
fn set(
&self,
ctx: &InvocationContext,
key: String,
value: String,
) -> Result<String, ModuleError> {
let tree = ctx
.db()
.open_tree(DATA_TREE)
.map_err(|e| ModuleError::Other(e.to_string()))?;
tree.insert(key.as_bytes(), value.as_bytes())
.map_err(|e| ModuleError::Other(e.to_string()))?;
Ok(format!("Set {}={}\n", key, value))
}
#[command]
fn get(&self, ctx: &InvocationContext, key: String) -> Result<String, ModuleError> {
let tree = ctx
.db()
.open_tree(DATA_TREE)
.map_err(|e| ModuleError::Other(e.to_string()))?;
let value = tree
.get(key.as_bytes())
.map_err(|e| ModuleError::Other(e.to_string()))?
.map(|v| String::from_utf8_lossy(&v).into_owned())
.unwrap_or_else(|| "<not found>".into());
Ok(format!("{}={}\n", key, value))
}
fn list(&self, ctx: &InvocationContext) -> Result<String, ModuleError> {
let tree = ctx
.db()
.open_tree(DATA_TREE)
.map_err(|e| ModuleError::Other(e.to_string()))?;
let items: Vec<String> = tree
.iter()
.filter_map(|r| r.ok())
.map(|(k, v)| {
format!(
"{}={}",
String::from_utf8_lossy(&k),
String::from_utf8_lossy(&v)
)
})
.collect();
Ok(if items.is_empty() {
"(empty)\n".into()
} else {
items.join("\n") + "\n"
})
}
#[command]
fn delete(&self, ctx: &InvocationContext, key: String) -> Result<String, ModuleError> {
let tree = ctx
.db()
.open_tree(DATA_TREE)
.map_err(|e| ModuleError::Other(e.to_string()))?;
tree.remove(key.as_bytes())
.map_err(|e| ModuleError::Other(e.to_string()))?;
Ok(format!("Deleted {}\n", key))
}
#[command]
fn limit(
&self,
_ctx: &InvocationContext,
count: i32,
verbose: bool,
) -> Result<String, ModuleError> {
Ok(format!(
"count={} (i32), verbose={} (bool)\n",
count, verbose
))
}
#[rpc_method]
fn demo_set(
&self,
params: &Value,
db: &Arc<dyn blvm_node::storage::database::Database>,
) -> Result<Value, ModuleError> {
let key = params
.get("key")
.and_then(|v| v.as_str())
.ok_or_else(|| ModuleError::Other("missing key".into()))?;
let value = params.get("value").and_then(|v| v.as_str()).unwrap_or("");
let tree = db
.open_tree(DATA_TREE)
.map_err(|e| ModuleError::Other(e.to_string()))?;
tree.insert(key.as_bytes(), value.as_bytes())
.map_err(|e| ModuleError::Other(e.to_string()))?;
Ok(serde_json::json!({ "ok": true, "key": key }))
}
#[rpc_method]
fn demo_get(
&self,
params: &Value,
db: &Arc<dyn blvm_node::storage::database::Database>,
) -> Result<Value, ModuleError> {
let key = params
.get("key")
.and_then(|v| v.as_str())
.ok_or_else(|| ModuleError::Other("missing key".into()))?;
let tree = db
.open_tree(DATA_TREE)
.map_err(|e| ModuleError::Other(e.to_string()))?;
let value = tree
.get(key.as_bytes())
.map_err(|e| ModuleError::Other(e.to_string()))?;
Ok(serde_json::json!({
"key": key,
"value": value.map(|v| String::from_utf8_lossy(&v).into_owned())
}))
}
#[rpc_method]
fn demo_list(
&self,
_params: &Value,
db: &Arc<dyn blvm_node::storage::database::Database>,
) -> Result<Value, ModuleError> {
let tree = db
.open_tree(DATA_TREE)
.map_err(|e| ModuleError::Other(e.to_string()))?;
let items: Vec<_> = tree
.iter()
.filter_map(|r| r.ok())
.map(|(k, v)| {
(
String::from_utf8_lossy(&k).into_owned(),
String::from_utf8_lossy(&v).into_owned(),
)
})
.collect();
Ok(serde_json::json!({ "items": items }))
}
#[on_event(ModuleLoaded)]
async fn on_module_loaded(&self, module_name: &str, version: &str) -> Result<(), ModuleError> {
info!("Demo module: {} v{} loaded", module_name, version);
Ok(())
}
#[on_event(NewBlock)]
async fn on_new_block(&self, block_hash: &Hash, height: u64) -> Result<(), ModuleError> {
info!(
"Demo module: new block {:?} at height {}",
block_hash, height
);
Ok(())
}
}
run_module_main!(DemoModule);