5 releases

new 0.3.1 May 26, 2025
0.2.6 May 24, 2025
0.2.5 May 23, 2025
0.2.4 May 23, 2025
0.2.3 Apr 12, 2025

#1251 in Parser implementations

Download history 87/week @ 2025-04-07 25/week @ 2025-04-14 5/week @ 2025-04-21 1/week @ 2025-04-28 2/week @ 2025-05-05 16/week @ 2025-05-12 326/week @ 2025-05-19

345 downloads per month

MIT/Apache

100KB
2K SLoC

Zenobuf

A simple ROS-like framework in Rust, using Zenoh for transport and Protocol Buffers for serialization.

Overview

Zenobuf is a lightweight, ergonomic framework for building distributed systems in Rust. It provides a publish-subscribe messaging system, a service-based RPC system, and a parameter system, similar to ROS (Robot Operating System) but with a more Rust-idiomatic API.

Features

  • Publish-Subscribe Messaging: Send and receive messages on topics
  • Service-Based RPC: Request-response communication between nodes
  • Parameter System: Store and retrieve configuration values
  • Type-Safe API: Leverage Rust's type system for compile-time guarantees
  • Protocol Buffers Integration: Use Protocol Buffers for message serialization
  • Zenoh Transport: Efficient pub/sub and query/reply using Zenoh

Getting Started

Prerequisites

  • Rust 1.70 or later
  • Protocol Buffers compiler (protoc) 3.0 or later

Installation

Add the following to your Cargo.toml:

[dependencies]
zenobuf-core = "0.1.0"
zenobuf-macros = "0.1.0"

Usage

Defining Messages

Define your messages using Protocol Buffers:

syntax = "proto3";

package my_messages;

message Point {
  float x = 1;
  float y = 2;
  float z = 3;
}

Step 1: Add dependencies to your Cargo.toml:

[dependencies]
zenobuf-core = "0.2"
zenobuf-macros = "0.2"
prost = "0.13"
tokio = { version = "1", features = ["full"] }

[build-dependencies]
prost-build = "0.13"

Step 2: Create a build.rs file that automatically adds the derive macro:

fn main() -> std::io::Result<()> {
    prost_build::Config::new()
        .type_attribute(".", "#[derive(zenobuf_macros::ZenobufMessage)]")
        .compile_protos(&["protos/my_messages.proto"], &["protos"])?;
    Ok(())
}

Step 3: Include the generated code in your lib.rs or main.rs:

pub mod proto {
    include!(concat!(env!("OUT_DIR"), "/my_messages.rs"));
}

That's it! The ZenobufMessage derive macro is automatically added to all your protobuf types.

🚀 Quick Start Template

For the fastest way to get started, copy the starter template which includes:

  • Pre-configured Cargo.toml and build.rs
  • Example protobuf definitions
  • Complete working example with publisher, subscriber, service, and client
  • Step-by-step customization guide
cp -r starter-template my-zenobuf-app
cd my-zenobuf-app
cargo run

Creating a Node

use zenobuf_core::Node;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create a node
    let node = Node::new("my_node").await?;

    // ...

    Ok(())
}

Publishing Messages

use zenobuf_core::{Node, QosProfile};
use my_crate::proto::Point;

// Create a publisher
let publisher = node
    .publisher::<Point>("point_topic")
    .build()
    .await?;

// Create a message
let point = Point {
    x: 1.0,
    y: 2.0,
    z: 3.0,
};

// Publish the message
publisher.publish(&point)?;

Subscribing to Messages

use zenobuf_core::{Node, QosProfile};
use my_crate::proto::Point;

// Create a subscriber
let _subscriber = node
    .subscriber::<Point>("point_topic")
    .build(|point| {
        println!("Received point: ({}, {}, {})", point.x, point.y, point.z);
    })
    .await?;

// Spin the node
node.spin().await?;

Creating a Service

use zenobuf_core::{Node, Result};
use my_crate::proto::{AddRequest, AddResponse};

// Create a service
let _service = node
    .service::<AddRequest, AddResponse>("add_service")
    .build(|request| {
        Ok(AddResponse {
            sum: request.a + request.b,
        })
    })
    .await?;

// Spin the node
node.spin().await?;

Calling a Service

use zenobuf_core::{Node, Result};
use my_crate::proto::{AddRequest, AddResponse};

// Create a client
let client = node.create_client::<AddRequest, AddResponse>("add_service")?;

// Create a request
let request = AddRequest {
    a: 2,
    b: 3,
};

// Call the service
let response = client.call(&request)?;
println!("Sum: {}", response.sum);

Using Parameters

use zenobuf_core::{Node, Result};

// Set a parameter
node.set_parameter("my_param", 42)?;

// Get a parameter
let value: i32 = node.get_parameter("my_param")?;
println!("Parameter value: {}", value);

📚 Documentation

Examples

See the zenobuf-examples crate for complete examples:

CLI Tools

Install the CLI tools for development and debugging:

cargo install zenobuf-cli

# Monitor topics
zenobuf-cli monitor sensor_data

# List system components
zenobuf-cli list topics
zenobuf-cli list services
zenobuf-cli list nodes

# Call services
zenobuf-cli call add_service --data '{"a": 5, "b": 3}'

# Manage parameters
zenobuf-cli param get max_speed
zenobuf-cli param set max_speed 15.0

License

This project is licensed under either of

at your option.

Dependencies

~30–43MB
~653K SLoC