rustbridge-logging 0.6.1

Tracing to FFI callback bridge for rustbridge
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
# rustbridge

[![CI](https://github.com/jrobhoward/rustbridge/actions/workflows/ci.yml/badge.svg)](https://github.com/jrobhoward/rustbridge/actions/workflows/ci.yml)
[![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](LICENSE-MIT)
[![Rust](https://img.shields.io/badge/rust-1.90%2B-orange.svg)](https://www.rust-lang.org)
[![Java](https://img.shields.io/badge/java-17%2B-red.svg)](https://openjdk.org)
[![.NET](https://img.shields.io/badge/.NET-8.0%2B-purple.svg)](https://dotnet.microsoft.com)
[![Python](https://img.shields.io/badge/python-3.10%2B-green.svg)](https://www.python.org)

> [!NOTE]
> This project is in beta. The core API is stable, but some features may change before 1.0.

A framework for developing Rust shared libraries callable from other languages. Uses C ABI under the hood but abstracts the complexity, providing OSGI-like lifecycle, mandatory async (Tokio), logging callbacks, and JSON-based data transport with optional binary transport for performance-critical paths.

## The .rbp Bundle

rustbridge plugins are distributed as `.rbp` (rustbridge plugin) bundles - portable ZIP archives containing:

- **Multi-platform libraries** - Native libraries for all target platforms in one file
- **Manifest** - Plugin metadata, version info, and SHA256 checksums
- **Optional signatures** - Minisign code signing for production security
- **Optional schemas** - JSON Schema and C headers for API documentation

```bash
# Create a bundle
rustbridge bundle create \
  --name my-plugin --version 1.0.0 \
  --lib linux-x86_64:target/release/libmyplugin.so \
  --lib darwin-aarch64:target/release/libmyplugin.dylib \
  --output my-plugin-1.0.0.rbp

# Load from any language - auto-detects platform
plugin = BundleLoader.load("my-plugin-1.0.0.rbp")
```

See [docs/BUNDLE_FORMAT.md](https://github.com/jrobhoward/rustbridge/blob/main/docs/BUNDLE_FORMAT.md) for the complete specification.

## Overview

rustbridge lowers the barrier for creating Rust plugins that can be loaded and called from Java, C#, Python, and other languages. Instead of manually managing FFI complexity, you implement a simple `Plugin` trait and rustbridge handles:

- **Memory management**: Safe buffer allocation and deallocation across FFI boundary
- **Async runtime**: Tokio runtime included in every plugin
- **Lifecycle management**: OSGI-inspired state machine (Installed → Starting → Active → Stopping → Stopped)
- **Logging**: Tracing integration with callbacks to host language
- **Serialization**: JSON-based message transport with typed envelopes

## Features

- **Cross-language interoperability**: Call Rust code from Java, Kotlin, C#, Python, and more
- **Multiple JVM implementations**: FFM for Java 21+ (modern, fast) and JNI for Java 17+ (compatibility)
- **Kotlin-friendly**: Idiomatic Kotlin usage with data classes, extension functions, and type-safe DSL
- **JSON-based transport**: Simple, universal data serialization
- **OSGI-inspired lifecycle**: Structured plugin startup and shutdown
- **Async-first**: Built on Tokio with mandatory async runtime
- **FFI logging**: Tracing integration with host language callbacks
- **Type-safe macros**: Procedural macros for reduced boilerplate
- **CLI tooling**: Project scaffolding and code generation

## Project Structure

```
rustbridge/
├── Cargo.toml                    # Workspace root
├── crates/
│   ├── rustbridge-core/          # Core traits, types, lifecycle
│   ├── rustbridge-transport/     # JSON codec, message envelopes
│   ├── rustbridge-ffi/           # C ABI exports, buffer management
│   ├── rustbridge-jni/           # JNI bindings for Java 17+
│   ├── rustbridge-runtime/       # Tokio integration
│   ├── rustbridge-logging/       # Tracing → FFI callback bridge
│   ├── rustbridge-macros/        # Procedural macros
│   ├── rustbridge-bundle/        # .rbp bundle creation and parsing
│   └── rustbridge-cli/           # Build tool and CLI
├── rustbridge-java/              # Java/Kotlin bindings
│   ├── rustbridge-core/          # Core interfaces
│   ├── rustbridge-ffm/           # FFM implementation (Java 21+)
│   └── rustbridge-kotlin/        # Kotlin extensions
├── rustbridge-csharp/            # C# bindings (.NET 8.0+)
├── rustbridge-python/            # Python bindings (3.10+)
├── examples/
│   ├── hello-plugin/             # Example Rust plugin
│   └── kotlin-examples/          # Kotlin usage examples
├── docs/
│   ├── ARCHITECTURE.md           # System architecture
│   ├── SKILLS.md                 # Development best practices
│   ├── TESTING.md                # Testing conventions
│   └── TASKS.md                  # Project roadmap
└── CLAUDE.md                     # Project instructions for Claude Code
```

## Quick Start

> 📖 **New to rustbridge?** See the [complete Getting Started guide]https://github.com/jrobhoward/rustbridge/blob/main/docs/GETTING_STARTED.md for a step-by-step tutorial.

### Creating a Plugin (Rust)

```rust
use async_trait::async_trait;
use rustbridge_core::{Plugin, PluginContext, PluginError, PluginResult};
use rustbridge_macros::{rustbridge_entry, Message};
use serde::{Deserialize, Serialize};

// Define message types
#[derive(Debug, Serialize, Deserialize, Message)]
#[message(tag = "echo")]
pub struct EchoRequest {
    pub message: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct EchoResponse {
    pub message: String,
}

// Implement the plugin
#[derive(Default)]
pub struct MyPlugin;

#[async_trait]
impl Plugin for MyPlugin {
    async fn on_start(&self, _ctx: &PluginContext) -> PluginResult<()> {
        tracing::info!("Plugin started");
        Ok(())
    }

    async fn handle_request(
        &self,
        _ctx: &PluginContext,
        type_tag: &str,
        payload: &[u8],
    ) -> PluginResult<Vec<u8>> {
        match type_tag {
            "echo" => {
                let req: EchoRequest = serde_json::from_slice(payload)?;
                let resp = EchoResponse { message: req.message };
                Ok(serde_json::to_vec(&resp)?)
            }
            _ => Err(PluginError::UnknownMessageType(type_tag.to_string())),
        }
    }

    async fn on_stop(&self, _ctx: &PluginContext) -> PluginResult<()> {
        tracing::info!("Plugin stopped");
        Ok(())
    }
}

// Generate FFI entry point
rustbridge_entry!(MyPlugin::default);

// Re-export FFI functions
pub use rustbridge_ffi::{
    plugin_init, plugin_call, plugin_free_buffer, plugin_shutdown,
    plugin_set_log_level, plugin_get_state,
};
```

### Using from Java (FFM, Java 21+)

```java
import com.rustbridge.ffm.FfmPluginLoader;
import com.rustbridge.Plugin;
import com.rustbridge.PluginConfig;

try (Plugin plugin = FfmPluginLoader.load("libmyplugin.so")) {
    String response = plugin.call("echo", "{\"message\": \"Hello, World!\"}");
    System.out.println(response);  // {"message": "Hello, World!"}
}
```

### Using from Java (JNI, Java 17+)

```java
import com.rustbridge.jni.JniPluginLoader;
import com.rustbridge.Plugin;

try (Plugin plugin = JniPluginLoader.load("libmyplugin.so")) {
    String response = plugin.call("echo", "{\"message\": \"Hello!\"}");
    System.out.println(response);
}
```

### Using from Kotlin

```kotlin
import com.rustbridge.ffm.FfmPluginLoader

// Data classes for type-safe requests
data class EchoRequest(val message: String)
data class EchoResponse(val message: String, val length: Int)

// Extension function for typed calls
inline fun <reified T> Plugin.callTyped(messageType: String, request: Any): T {
    val mapper = ObjectMapper()
    val responseJson = call(messageType, mapper.writeValueAsString(request))
    return mapper.readValue(responseJson, T::class.java)
}

// Use block for automatic cleanup
FfmPluginLoader.load("libmyplugin.so").use { plugin ->
    val response = plugin.callTyped<EchoResponse>("echo", EchoRequest("Hello!"))
    println(response.message)
}
```

See [examples/kotlin-examples](https://github.com/jrobhoward/rustbridge/blob/main/examples/kotlin-examples) for complete examples.

## FFI API

The following C functions are exported by plugins:

```c
// Create plugin instance (called by plugin_init internally)
void* plugin_create();

// Initialize plugin with config and optional log callback
void* plugin_init(
    void* plugin_ptr,
    const uint8_t* config_json,
    size_t config_len,
    void (*log_callback)(uint8_t level, const char* target, const uint8_t* msg, size_t len)
);

// Make a synchronous request
FfiBuffer plugin_call(
    void* handle,
    const char* type_tag,      // null-terminated
    const uint8_t* request,
    size_t request_len
);

// Free a buffer returned by plugin_call
void plugin_free_buffer(FfiBuffer* buffer);

// Shutdown the plugin
bool plugin_shutdown(void* handle);

// Set log level (0=Trace, 1=Debug, 2=Info, 3=Warn, 4=Error, 5=Off)
void plugin_set_log_level(void* handle, uint8_t level);

// Get current lifecycle state
uint8_t plugin_get_state(void* handle);

// Get count of rejected requests (due to concurrency limit)
uint64_t plugin_get_rejected_count(void* handle);

// Async API (placeholder for future - returns 0/false)
uint64_t plugin_call_async(...);
bool plugin_cancel_async(void* handle, uint64_t request_id);
```

### FfiBuffer Structure

```c
typedef struct {
    uint8_t* data;      // Pointer to data
    size_t len;         // Data length
    size_t capacity;    // Allocation capacity
    uint32_t error_code; // 0 = success, non-zero = error
} FfiBuffer;
```

## Lifecycle States

```
Installed → Starting → Active → Stopping → Stopped
               ↑                    │
               └────────────────────┘ (restart)
           Any state → Failed (on error)
```

| State | Description |
|-------|-------------|
| `Installed` | Plugin created but not initialized |
| `Starting` | Initializing runtime, resources |
| `Active` | Ready to handle requests |
| `Stopping` | Graceful shutdown in progress |
| `Stopped` | Shutdown complete |
| `Failed` | Error occurred |

## Building

```bash
# Build all crates
cargo build

# Build in release mode
cargo build --release

# Build a specific plugin
cargo build -p hello-plugin

# Run tests
cargo test

# Build CLI tool
cargo build -p rustbridge-cli
```

## CLI Usage

```bash
# Create a new plugin project
rustbridge new my-plugin

# Build a plugin
rustbridge build --release

# Generate JSON Schema from Rust message types
rustbridge generate json-schema -i src/messages.rs -o schema.json

# Validate manifest
rustbridge check

# Create a bundle for distribution
rustbridge bundle create \
  --name my-plugin --version 1.0.0 \
  --lib linux-x86_64:target/release/libmyplugin.so \
  --output my-plugin.rbp

# Generate signing keys
rustbridge keygen --output signing.key
```

## Configuration

### Plugin Configuration (PluginConfig)

```json
{
  "worker_threads": 4,
  "log_level": "info",
  "max_concurrent_ops": 1000,
  "shutdown_timeout_ms": 5000,
  "data": {
    "custom_key": "custom_value"
  }
}
```

**Configuration Options:**

- **`worker_threads`** (optional): Number of async worker threads (default: number of CPU cores)
- **`log_level`**: Initial log level - "trace", "debug", "info", "warn", "error", "off" (default: "info")
- **`max_concurrent_ops`**: Maximum concurrent requests (default: 1000)
  - Set to `0` for unlimited (use with caution - can cause memory exhaustion)
  - Requests exceeding this limit are immediately rejected with error code 13 (TooManyRequests)
  - Monitor rejected requests using `plugin.getRejectedRequestCount()` (Java) or `handle.rejected_request_count()` (Rust)
- **`shutdown_timeout_ms`**: Maximum milliseconds to wait during shutdown (default: 5000)
- **`data`** (optional): Plugin-specific configuration data (JSON object)

**Example: Configuring concurrency limits in Java:**

```java
PluginConfig config = PluginConfig.defaults()
    .maxConcurrentOps(100)  // Limit to 100 concurrent requests
    .workerThreads(4)
    .logLevel(LogLevel.INFO);

try (Plugin plugin = FfmPluginLoader.load(pluginPath, config)) {
    // Make calls...

    // Monitor rejected requests
    long rejectedCount = plugin.getRejectedRequestCount();
    if (rejectedCount > 0) {
        System.out.println("Rejected " + rejectedCount + " requests due to concurrency limit");
    }
}
```

### Project Manifest (rustbridge.toml)

The `rustbridge.toml` file is a development-time configuration used by CLI tools for validation, code generation, and bundle creation. It is **not** included in `.rbp` bundles directly - bundles contain a separate `manifest.json` file (see [docs/BUNDLE_FORMAT.md](https://github.com/jrobhoward/rustbridge/blob/main/docs/BUNDLE_FORMAT.md) for the bundle manifest schema).

```toml
[plugin]
name = "my-plugin"
version = "1.0.0"
description = "My awesome plugin"
authors = ["Your Name"]

[messages."user.create"]
description = "Create a new user"
request_schema = "schemas/CreateUserRequest.json"
response_schema = "schemas/CreateUserResponse.json"

[messages."user.delete"]
description = "Delete a user"

[platforms]
linux-x86_64 = "libmyplugin.so"
linux-aarch64 = "libmyplugin.so"
darwin-x86_64 = "libmyplugin.dylib"
darwin-aarch64 = "libmyplugin.dylib"
windows-x86_64 = "myplugin.dll"
```

## Error Handling

Errors are represented with stable numeric codes:

| Code | Error Type |
|------|-----------|
| 0 | Success |
| 1 | Invalid State |
| 2 | Initialization Failed |
| 3 | Shutdown Failed |
| 4 | Config Error |
| 5 | Serialization Error |
| 6 | Unknown Message Type |
| 7 | Handler Error |
| 8 | Runtime Error |
| 9 | Cancelled |
| 10 | Timeout |
| 11 | Internal Error |
| 12 | FFI Error |
| 13 | Too Many Requests (concurrency limit exceeded) |

## Target Languages

| Language | Status | Implementation |
|----------|--------|---------------|
| Java/Kotlin | Tier 1 | FFM (Java 21+, recommended) + JNI (Java 17+) |
| C# | Tier 2 | P/Invoke (.NET 8.0+) |
| Python | Tier 2 | ctypes (Python 3.10+) |

## Documentation

### Getting Started
- [docs/GETTING_STARTED.md]https://github.com/jrobhoward/rustbridge/blob/main/docs/GETTING_STARTED.md - Tutorial for creating your first plugin
- [docs/using-plugins/JAVA_FFM.md]https://github.com/jrobhoward/rustbridge/blob/main/docs/using-plugins/JAVA_FFM.md - Java 21+ FFM guide
- [docs/using-plugins/JAVA_JNI.md]https://github.com/jrobhoward/rustbridge/blob/main/docs/using-plugins/JAVA_JNI.md - Java 17+ JNI guide
- [docs/using-plugins/KOTLIN.md]https://github.com/jrobhoward/rustbridge/blob/main/docs/using-plugins/KOTLIN.md - Kotlin guide
- [docs/using-plugins/CSHARP.md]https://github.com/jrobhoward/rustbridge/blob/main/docs/using-plugins/CSHARP.md - C# guide
- [docs/using-plugins/PYTHON.md]https://github.com/jrobhoward/rustbridge/blob/main/docs/using-plugins/PYTHON.md - Python guide

### Architecture & Design
- [docs/ARCHITECTURE.md]https://github.com/jrobhoward/rustbridge/blob/main/docs/ARCHITECTURE.md - System architecture and design decisions
- [docs/BUNDLE_FORMAT.md]https://github.com/jrobhoward/rustbridge/blob/main/docs/BUNDLE_FORMAT.md - .rbp bundle specification
- [docs/TRANSPORT.md]https://github.com/jrobhoward/rustbridge/blob/main/docs/TRANSPORT.md - JSON and binary transport layer
- [docs/MEMORY_MODEL.md]https://github.com/jrobhoward/rustbridge/blob/main/docs/MEMORY_MODEL.md - Memory ownership patterns

### Development
- [docs/SKILLS.md]https://github.com/jrobhoward/rustbridge/blob/main/docs/SKILLS.md - Development best practices and coding conventions
- [docs/TESTING.md]https://github.com/jrobhoward/rustbridge/blob/main/docs/TESTING.md - Testing conventions and guidelines
- [docs/ERROR_HANDLING.md]https://github.com/jrobhoward/rustbridge/blob/main/docs/ERROR_HANDLING.md - Error handling patterns
- [docs/DEBUGGING.md]https://github.com/jrobhoward/rustbridge/blob/main/docs/DEBUGGING.md - Debugging techniques

## Contributing

We welcome contributions! Please see [CONTRIBUTING.md](https://github.com/jrobhoward/rustbridge/blob/main/CONTRIBUTING.md) for guidelines.

**Quick start:**
1. Read [docs/SKILLS.md]https://github.com/jrobhoward/rustbridge/blob/main/docs/SKILLS.md for coding conventions
2. Read [docs/TESTING.md]https://github.com/jrobhoward/rustbridge/blob/main/docs/TESTING.md for testing guidelines
3. Check [docs/TASKS.md]https://github.com/jrobhoward/rustbridge/blob/main/docs/TASKS.md for open tasks

## Changelog

See [CHANGELOG.md](https://github.com/jrobhoward/rustbridge/blob/main/CHANGELOG.md) for version history and release notes.

## License

MIT OR Apache-2.0

### Attribution

This project includes software licensed under the Unicode License (Unicode-3.0). See [NOTICES](https://github.com/jrobhoward/rustbridge/blob/main/NOTICES) for details.