Expand description
Safe Rust wrapper for musubi diagnostic renderer
This library provides a safe, ergonomic Rust API for the musubi C library, which renders beautiful diagnostic messages similar to rustc and other modern compilers.
§Quick Start
use musubi::{Report, Level};
let report = Report::new()
.with_title(Level::Error, "Invalid syntax")
.with_code("E001")
.with_label(8..10)
.with_message("Answer to the Ultimate Question here")
.render_to_string(("let x = 42;", "example.rs"))?;
println!("{}", report);§Core Concepts
§Sources and Cache
A Source provides the text content for diagnostics. Sources are managed through
a Cache, which can store multiple sources and be reused across multiple reports:
let cache = Cache::new()
.with_source(("let x = 42;", "main.rs"));
let mut report = Report::new()
.with_title(Level::Error, "Syntax error")
.with_label(0..3);
report.render_to_stdout(&cache)?;Sources are registered in order and assigned IDs: first source is ID 0, second is ID 1, etc.
For simple single-source diagnostics, you can pass content directly to rendering methods
without creating an explicit Cache:
Report::new()
.with_title(Level::Error, "Simple error")
.with_label(0..3)
.render_to_string(("let x", "main.rs"))?;§Lifetime Management
By default, source content must outlive the Report (borrowed sources like &str).
The Cache can also take ownership and manage the lifetime:
- Borrowed:
cache.with_source("code")- content must remain valid until rendering - Owned:
cache.with_source("code".to_string())-Stringhas built-in ownership - Custom buffers: Use
OwnedSourceforVec<u8>,Box<[u8]>, etc.
let cache = Cache::new()
.with_source("static str") // Borrowed
.with_source(("owned".to_string(), "file.rs")) // Owned by cache
.with_source((OwnedSource::new(vec![b'x']), "buf")); // Custom buffer
// Cache manages owned content lifetime until dropped§Multiple Sources
Display diagnostics that span multiple files:
let cache = Cache::new()
.with_source(("import foo", "main.rs")) // Source ID 0
.with_source(("pub fn foo() {}", "lib.rs")); // Source ID 1
let report = Report::new()
.with_title(Level::Error, "Import error")
.with_label((7..10, 0)) // Label in main.rs
.with_message("imported here")
.with_label((7..10, 1)) // Label in lib.rs
.with_message("defined here")
.render_to_string(&cache)?;
println!("{}", report);§Rendering Methods
Three rendering methods are available:
Report::render_to_string()- Capture output as a StringReport::render_to_stdout()- Write directly to stdout (most efficient)Report::render_to_writer()- Write to anystd::io::Writeimplementation
§Labels
Labels highlight specific spans in your source code. Each label can have:
- A span (byte or character range)
- A message explaining the issue
- Custom colors
- Display order and priority
let report = Report::new()
// ...
.with_label(0..3) // First label
.with_message("expected type here")
.with_label(4..5) // Second label
.with_message("found here")
// ...§Configuration
Customize rendering with Config:
- Character sets (ASCII vs Unicode)
- Color schemes
- Layout options (compact mode, tab width, line wrapping)
- Label attachment (start/middle/end of spans)
let config = Config::new()
.with_char_set_unicode() // Use box-drawing characters
.with_color_default() // Enable ANSI colors
.with_compact(true) // Compact output
.with_tab_width(4) // 4-space tabs
// ...
;
Report::new()
.with_config(config)
// ...§Custom Colors
Implement the Color trait to provide custom color schemes:
struct MyColors;
impl Color for MyColors {
fn color(&self, w: &mut dyn Write, kind: ColorKind) -> std::io::Result<()> {
match kind {
ColorKind::Error => write!(w, "\x1b[31m"), // Red
ColorKind::Warning => write!(w, "\x1b[33m"), // Yellow
ColorKind::Reset => write!(w, "\x1b[0m"), // Reset
_ => Ok(()),
}
}
}
let config = Config::new().with_color(&MyColors);§Custom Sources
Implement the Source trait for lazy file loading or special formatting:
struct LazyFileSource {
// ... your fields
}
impl Source for LazyFileSource {
fn init(&mut self) -> io::Result<()> {
// Initialize (e.g., open file, read metadata)
Ok(())
}
fn get_line(&self, line_no: usize) -> &[u8] {
// Return the requested line
}
fn get_line_info(&self, line_no: usize) -> Line {
// Return line metadata (offsets, lengths)
}
fn line_for_chars(&self, char_pos: usize) -> (usize, Line) {
// Map character position to line
}
fn line_for_bytes(&self, byte_pos: usize) -> (usize, Line) {
// Map byte position to line
}
}Structs§
- Cache
- A cache of diagnostic sources.
- CharSet
- Character set for rendering diagnostic output
- Color
Generator - Automatic color generator for creating visually distinct label colors.
- Config
- Configuration for the diagnostic renderer
- GenColor
- A pre-generated ANSI color code.
- Label
Span - A label span with optional source ID.
- Line
- Information about a line in source code.
- Owned
Source - Wrapper for owned source content.
- Report
- A diagnostic report builder.
- Title
Level - Internal representation of a title level for FFI.
Enums§
- Color
Kind - Color categories for diagnostic output
- Index
Type - Index type for span positions
- Label
Attach - Where labels attach to their spans
- Level
- Diagnostic severity level
- RawCache
- Internal representation of a cache for rendering.
Traits§
- AddTo
Cache - Trait for types that can be added to a cache.
- Color
- Trait for types that can provide color codes.
- Into
Color - Trait for types that can be used as raw color codes.
- Source
- A source of diagnostic content.