Crate musubi

Crate musubi 

Source
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()) - String has built-in ownership
  • Custom buffers: Use OwnedSource for Vec<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:

§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
ColorGenerator
Automatic color generator for creating visually distinct label colors.
Config
Configuration for the diagnostic renderer
GenColor
A pre-generated ANSI color code.
LabelSpan
A label span with optional source ID.
Line
Information about a line in source code.
OwnedSource
Wrapper for owned source content.
Report
A diagnostic report builder.
TitleLevel
Internal representation of a title level for FFI.

Enums§

ColorKind
Color categories for diagnostic output
IndexType
Index type for span positions
LabelAttach
Where labels attach to their spans
Level
Diagnostic severity level
RawCache
Internal representation of a cache for rendering.

Traits§

AddToCache
Trait for types that can be added to a cache.
Color
Trait for types that can provide color codes.
IntoColor
Trait for types that can be used as raw color codes.
Source
A source of diagnostic content.