plcbundle-rs Documentation#
Overview#
This directory contains the design and architecture documentation for the plcbundle-rs library.
Documents#
π API.md - High-Level API Design#
Purpose: Complete specification of the BundleManager API.
Contents:
- All public methods with signatures, purposes, and examples
- Option structs and result types
- Common patterns and design principles
- FFI mapping guidelines
- Implementation status
Read this to: Understand the complete API surface and design philosophy.
π§ REFACTORING.md - Migration Roadmap#
Purpose: Actionable plan to move CLI from direct file access to high-level API.
Contents:
- Current problems and anti-patterns
- Step-by-step refactoring guide
- Code examples (before/after)
- Testing strategy
- Timeline and checklist
Read this to: Understand what needs to be done to complete the API unification.
Key Principles#
1. Single Source of Truth#
All functionality lives in BundleManager:
βββββββββββββββββββββββββββββββββββββββββββ
β BundleManager (Rust) β β Single Implementation
β β’ Bundle loading β
β β’ Operation access β
β β’ Query/Export β
β β’ Verification β
βββββββββββββββββββ¬ββββββββββββββββββββββββ
β
βββββββββββββ΄ββββββββββββ
β β
βΌ βΌ
βββββββββββββββ ββββββββββββ
β Rust CLI β β C/Go FFI β
β (consumer) β β(consumer)β
βββββββββββββββ ββββββββββββ
2. No Direct File Access#
Bad β:
// CLI directly opens files
let file = std::fs::File::open("bundle.zst")?;
Good β :
// CLI uses high-level API
let op = manager.get_operation_raw(bundle, pos)?;
3. Options Pattern#
Complex operations use dedicated option structs:
pub struct ExportSpec {
pub bundles: BundleRange,
pub format: ExportFormat,
pub filter: Option<OperationFilter>,
pub count: Option<usize>,
}
manager.export(spec)?;
4. Streaming by Default#
Large datasets use iterators:
let iter = manager.query(spec)?;
for result in iter {
println!("{}", result?);
}
5. FFI-Friendly Design#
Every Rust API has a clean C mapping:
// Rust
manager.get_operation_raw(bundle, pos) -> Result<String>
// C
bundle_manager_get_operation_raw(mgr, bundle, pos, &out_json, &out_len) -> int
// Go
manager.GetOperationRaw(bundle, pos) (string, error)
Current Status#
β
Implemented in BundleManager#
- Bundle loading (
load_bundle) - Query operations (
query) - Export operations (
export,export_to_writer) - Verification (
verify_bundle,verify_chain) - Statistics (
get_stats) - DID indexing (
rebuild_did_index)
π§ Needs Refactoring#
- Operation access - Currently in CLI only
get_operation()- Should be inBundleManagerget_operation_raw()- Should be inBundleManager- See REFACTORING.md for details
π To Implement#
- Batch operations (
get_operations_batch) - Range operations (
get_operations_range) - DID lookup (
get_did_operations,batch_resolve_dids) - Cache warming (
prefetch_bundles,warm_up)
API Philosophy#
Performance First#
The API is designed for zero-cost abstractions:
- Direct line reading for
get_operation_raw()- 68Β΅s - Streaming for large exports - constant memory
- Parallel query execution - scales with cores
Flexibility#
The API supports multiple use cases:
// Fast: Get raw JSON (preserves field order)
let json = manager.get_operation_raw(1, 0)?;
// Structured: Get parsed operation
let op = manager.get_operation(1, 0)?;
// Batch: Get multiple operations efficiently
let ops = manager.get_operations_batch(requests)?;
// Stream: Export millions of operations
let iter = manager.export(spec)?;
for line in iter { /* ... */ }
Safety#
Type-safe interfaces prevent common errors:
pub enum BundleRange {
Single(u32),
Range(u32, u32),
List(Vec<u32>),
All,
}
// Can't accidentally pass invalid range
manager.export(ExportSpec {
bundles: BundleRange::Range(1, 100),
// ...
})?;
CLI Relationship#
The CLI is a thin wrapper around BundleManager:
// CLI command handler (thin)
fn cmd_export(...) -> Result<()> {
let manager = BundleManager::new(dir)?;
let spec = ExportSpec {
bundles: parse_range(range)?,
format: format,
// ...
};
let iter = manager.export(spec)?;
for line in iter {
println!("{}", line?);
}
Ok(())
}
Benefits:
- CLI is simple and testable
- Logic in
BundleManageris reusable - FFI can expose same functionality
- Easy to add new CLIs (Python, Node.js, etc.)
FFI Architecture#
ββββββββββββββββββββββββββββββββββββββββββββββββββ
β Rust Core β
β (src/manager.rs) β
β β
β pub struct BundleManager { ... } β
β impl BundleManager { β
β pub fn get_operation_raw(...) -> Result<..> β
β pub fn export(...) -> Result<...> β
β // ... all high-level APIs β
β } β
ββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ
β
β Direct Rust API
β
ββββββββββββββΌβββββββββββββ
β β
βΌ βΌ
βββββββββββββββ ββββββββββββββββββββ
β Rust CLI β β FFI Layer β
β β β (src/ffi.rs) β
β Uses Rust β β β
β API directlyβ β Exposes C API β
βββββββββββββββ ββββββββββ¬ββββββββββ
β
β C ABI
β
ββββββββββΌβββββββββββ
β Go Bindings β
β(bindings/go/) β
β β
β Wraps C API in β
β idiomatic Go β
βββββββββββββββββββββ
Testing Strategy#
Unit Tests (Rust)#
Test BundleManager methods directly:
#[test]
fn test_get_operation_raw() {
let mgr = BundleManager::new("test_bundles").unwrap();
let json = mgr.get_operation_raw(1, 0).unwrap();
assert!(json.contains("did:plc:"));
}
Integration Tests (FFI)#
Test C API:
#[test]
fn test_ffi_operation_access() {
let mgr = bundle_manager_new(c_str!("test_bundles"));
let ret = bundle_manager_get_operation_raw(mgr, 1, 0, &mut json, &mut len);
assert_eq!(ret, 0);
}
Integration Tests (Go)#
Test Go bindings:
func TestGetOperation(t *testing.T) {
mgr, _ := NewBundleManager("test_bundles")
json, err := mgr.GetOperationRaw(1, 0)
require.NoError(t, err)
}
Next Steps#
See REFACTORING.md for the detailed plan, but the immediate priorities are:
- Move
get_operation()toBundleManagerβ Most urgent - Update CLI to use new API
- Add FFI bindings
- Add Go bindings
- Write tests
This ensures:
- β No direct file access in CLI
- β Operation access available in Go
- β Single source of truth
- β Consistent API across languages
Questions?#
For specific implementation details:
- API design: See API.md
- Refactoring: See REFACTORING.md
- Code: See
src/manager.rsfor current implementation