cadboss.dev

Rust

The cadboss crates

One Rust core in five published crates, MIT OR Apache-2.0. The CLI and the Python package call the same reader, so all three return the same document.

Layering

Each crate depends only on the ones above it.

CrateOwnsDepends on
cadboss-coreSource (mmap or owned bytes), budgets, report, errors, geometrynone
cadboss-modelThe format-independent document: metadata, layers, blocks, layouts, text groups, entitiescore
cadboss-dxfThe DXF reader: tape lexer, binary and ASCII, header, tables, blocks, layouts, in-place entity decoder, text groupscore, model
cadboss-outputMarkdown and JSON writerscore, model
cadboss-cliThe cadboss binary and its Claude Code skillcore, model, dxf, output

The Python package (crates/cadboss-py with python/cadboss, built with pyo3 and maturin) wraps the same reader and is published to PyPI.

Reading a drawing

Read a drawing with its report.

The README example: read the unified document with its report, then walk the decoded entities of the Model layout.

main.rs
use std::path::Path;

use cadboss_dxf::{open_reporting, DxfDocument};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let (doc, report) = open_reporting(Path::new("plan.dxf"))?;
    println!("{} {:?}", doc.metadata.version, doc.metadata.units);
    for item in &doc.text.title_block {
        println!("{} on layer {}", item.text, item.layer);
    }
    for skipped in &report.skipped {
        println!("{:?} {:?} x{}", skipped.kind, skipped.reason, skipped.count);
    }

    let dxf = DxfDocument::open(Path::new("plan.dxf"))?;
    for entity in dxf.entities("Model") {
        println!("{entity:?}");
    }
    Ok(())
}

cadboss_dxf::open maps the file, read loads it into one buffer, load takes bytes; each has a _reporting twin that returns the Report beside the Document, and a _with_options variant that takes &Options. DxfDocument is the layer underneath: the tape, the header, the tables, and an in-place entity decoder per layout or block.

Budget, Report, Skipped

Budgets in, a report out.

A Budget caps bytes, tokens, entities, layouts, nesting depth and string length; Budget::default() holds the defaults and Budget::set(key, value) sets one by the same names the CLI and Python accept. The reader stops at a cap and records it in the Report.

The report keeps one entry per kind and reason, with a count. It keeps up to 64 distinct entries and counts the rest in unlisted; summary() renders lines such as 4 entities skipped (entity_limit), the same text the CLI prints.

cadboss_core and cadboss_dxf, abridged
// cadboss_core
pub struct Budget {
    pub max_bytes: u64,
    pub max_entities: u64,
    pub max_layouts: u32,
    pub max_nesting: u32,
    pub max_string_bytes: u32,
    pub max_tokens: u64,
}

pub struct Report {
    pub skipped: Vec<Skipped>,
    pub unlisted: u64,
}

pub struct Skipped {
    pub kind: SkipKind,
    pub reason: SkipReason,
    pub count: u64,
}

pub enum SkipKind { Token, Section, Table, Entity, Text, Object }

pub enum SkipReason {
    Malformed, Truncated, Unknown, NestingLimit, EntityLimit,
    StringLimit, UnsupportedCodepage, Unsupported, TokenLimit,
}

// cadboss_dxf
pub struct Options {
    pub budget: Budget,
    pub threads: usize,
}

FAQ

Questions developers ask.

Which crate do I depend on?
cadboss-dxf to read a drawing; it returns the format-independent Document from cadboss-model. Add cadboss-output for the Markdown and JSON writers that the CLI and the Python package use.
What is the difference between open, read and load?
cadboss_dxf::open maps the file, read loads it into one buffer, and load takes bytes. Each has a _reporting twin that returns the Report beside the Document, and a _with_options variant that takes Options with a Budget.
When do I need DxfDocument?
When you want the layer underneath the unified Document: the tape, the header, the tables, and an in-place entity decoder per layout or block. DxfDocument::open(path) and dxf.entities("Model") walk the entities of one layout.
Can the reader panic on a broken file?
Panics are denied by the workspace lints. Malformed records, truncated sections and exceeded budgets become Skipped notes in the Report, and a cargo-fuzz target seeded from the fixtures runs on each push.