herolib-derive

Crates.ioherolib-derive
lib.rsherolib-derive
version0.3.13
created_at2025-12-18 06:35:38.652334+00
updated_at2026-01-24 05:23:32.869144+00
descriptionDerive macros for herolib (ToSchema, ToHeroScript, FromHeroScript, Otoml, Actor, rpc_method, OpenRPC client)
homepage
repositoryhttps://github.com/herolib/herolib_rust
max_upload_size
id1991814
size144,751
kristof de spiegeleer (despiegk)

documentation

README

herolib-derive

Crates.io Documentation

Derive macros for herolib providing:

  • ToSchema - Generate JSON Schema from Rust structs
  • ToHeroScript - Serialize Rust structs to HeroScript format
  • FromHeroScript - Deserialize HeroScript into Rust structs
  • OsisObject - Implement OSIS database object trait for type-safe storage
  • Actor - Define RPC-capable actors with OpenRPC specification generation
  • RpcMethod - Mark methods as RPC endpoints

Documentation

Installation

[dependencies]
herolib-derive = "0.1"

Building

./build.sh

Actor System

The Actor system provides a standardized approach for building RPC services with automatic code generation.

Overview

An Actor is a self-contained unit that:

  • Encapsulates business logic
  • Exposes methods via OpenRPC
  • Communicates through Redis queues
  • Supports synchronous and asynchronous operations
  • Streams logs during execution

Quick Example

use herolib_derive::{Actor, ToSchema};
use serde::{Deserialize, Serialize};

/// Input for greeting.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct GreetInput {
    pub name: String,
}

/// Output after greeting.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct GreetOutput {
    pub message: String,
}

/// Greeter handles greeting operations.
#[derive(Actor)]
#[actor(name = "greeter")]
pub struct Greeter;

impl Greeter {
    /// Say hello to someone.
    #[rpc_method]
    #[rpc_example(
        input = r#"{ "name": "World" }"#,
        output = r#"{ "message": "Hello, World!" }"#
    )]
    pub async fn hello(
        &self,
        input: GreetInput,
        logger: &RequestLogger,
    ) -> Result<GreetOutput, ActorError> {
        logger.info(format!("Greeting {}", input.name)).await;
        Ok(GreetOutput {
            message: format!("Hello, {}!", input.name),
        })
    }
}

Generated Artifacts

For each actor, the macros generate:

Artifact Description
{actor}.openrpc.json OpenRPC 1.3 specification
{actor}_client.rs Type-safe async client
{actor}_handler.rs Redis queue handler/server
{actor}_api.md API documentation
{actor}_api_redis.md API documentation with Redis details

Redis Communication

Actors communicate via Redis queues:

actors:{actor_name}:{instance}:queue     # Request queue (List)
actors:{actor_name}:{instance}:result:{id}  # Results (String)
actors:{actor_name}:{instance}:logs:{id}    # Log stream (List)

Client sends request:

LPUSH actors:greeter:main:queue '{"jsonrpc":"2.0","id":"req-1","method":"greeter.hello","params":{"name":"World"}}'

Client reads result:

GET actors:greeter:main:result:req-1

Documentation

See the docs/ directory for detailed specifications:

  1. Actor System Architecture
  2. OpenRPC Generation
  3. Redis Queue Protocol
  4. Client Generation
  5. Handler Generation
  6. Documentation Generation
  7. Example Actor

OsisObject Macro

Implement the OsisObject trait for type-safe database storage with automatic SmartID generation:

use herolib_derive::OsisObject;
use herolib_osis::sid::SmartId;
use serde::{Serialize, Deserialize};

// Auto-generated type_name: "user"
#[derive(Default, Serialize, Deserialize, OsisObject)]
struct User {
    sid: SmartId,
    name: String,
}

// Auto-generated type_name: "user_profile"
#[derive(Default, Serialize, Deserialize, OsisObject)]
struct UserProfile {
    sid: SmartId,
    email: String,
}

// Custom type_name override
#[derive(Default, Serialize, Deserialize, OsisObject)]
#[osis(type_name = "members")]
struct Member {
    sid: SmartId,
    name: String,
}

Usage with DBTyped:

use herolib_osis::db::DBTyped;

let mut users: DBTyped<User> = DBTyped::new("/data", 0)?;

// Create and store (sid auto-generated)
let mut user = User::default();
user.name = "Alice".into();
users.set(&mut user)?;  // user.sid is now set

// Retrieve by SID
let loaded = users.get(&user.sid)?;

HeroScript Macros

ToHeroScript / FromHeroScript

Serialize and deserialize Rust structs to/from HeroScript format:

use herolib_derive::{ToHeroScript, FromHeroScript};

#[derive(ToHeroScript, FromHeroScript, Default)]
struct Person {
    name: String,
    age: u32,
    active: bool,
}

// Serialize to HeroScript
let person = Person { name: "John".into(), age: 30, active: true };
let hs = person.to_heroscript("person", "define");
// Output:
// !!person.define
//     name:John
//     age:30
//     active:true

// Deserialize from HeroScript
let script = "!!person.define name:Jane age:25 active:false";
let jane = Person::from_heroscript(script).unwrap();

ToSchema

Generate JSON Schema from Rust types:

use herolib_derive::ToSchema;

#[derive(ToSchema)]
struct Config {
    /// Server hostname.
    host: String,
    
    /// Server port.
    port: u16,
    
    /// Enable debug mode.
    #[serde(default)]
    debug: bool,
}

// Get JSON Schema
let schema = Config::schema();

License

Apache-2.0

Commit count: 0

cargo fmt