Skip to main content

Crate libprintcalc

Crate libprintcalc 

Source
Expand description

Core library for calculating 3D print job costs from sliced-file output.

libprintcalc is the single home for all I/O-free logic — slice-file parsing, profile matching, cost calculation, and the job-tier data structures — plus a thin read-only disk helper for loading them from files. The printcalc CLI (its own crate) and printcalc-wasm are thin frontends over it, so they cannot disagree about what a job costs; the CLI additionally owns everything write- or config-backed (the profile library, job file saving, config persistence).

The arithmetic every one of them shares is written out in docs/reference/pricing.md. Suggested sale prices are gross margins, not markups: a 60% margin is cost / 0.40.

§Entry points

Everything below is re-exported at the crate root — no deep module paths needed for the common cases:

  • parse_slice_bytes turns sliced-file bytes into a SliceSummary; read_slice_file does the same straight from a path.
  • parse_job parses job TOML into a Job; read_job reads it from a file. parse_printer/parse_material parse profile TOML; read_printer/read_material load the same from disk. parse_toml does the same for any of them and additionally reports every key the file carried that no field claimed, so a frontend can warn about a typo instead of letting it silently take a default.
  • new_job builds a Job from a sliced-file path (or an already-parsed SliceSummary) and an embedded printer profile — no lookups, can’t fail.
  • auto_job goes further and builds one from the sliced file alone, matching the printer and each filament slot against the presets from the names the slicer recorded, and falling back to the file’s own stated filament price when nothing matches. See matching.
  • calculate costs a job, pulling its slice data via Job::slice_summary and resolving Embedded profile refs straight through and Linked ones against the builtin presets. All of the above return the single Error type (except parse_slice_bytes, which returns ParseError).
  • calculate_cost is the pure core computation for callers supplying fully-resolved profiles themselves (as printcalc-wasm does, with no filesystem to read from).

The example below shows the calculate_cost path — the core computation with no I/O at all, since that’s the part that needs no real files on disk to demonstrate:

use std::collections::HashMap;

use libprintcalc::{
    calculate_cost, CostDefaults, FilamentUsage, Job, JobSource, LaborCost, MaterialKind,
    MaterialProfile, PlateSummary, PrinterKind, PrinterProfile, ProfileRef, SliceSummary,
    SourceFormat, WearProfile, SINGLE_MATERIAL_SLOT,
};

// Normally the output of `libprintcalc::parse_slice_bytes(bytes, filename)`.
let slice = SliceSummary::new(
    SourceFormat::Bambu3mf,
    vec![PlateSummary::new(
        1,
        1037.0,
        vec![FilamentUsage::grams(SINGLE_MATERIAL_SLOT, 6.66).with_material_type("PLA")],
    )],
);

let printer = PrinterProfile::new(
    "bambu-a1",
    PrinterKind::Fdm,
    WearProfile::new()
        .with_purchase_price(800.0)
        .with_power_consumption_watts(160.0),
);
let filament = MaterialProfile::new("pla-black", MaterialKind::Filament).with_price(18.99);

let mut job = Job::new(
    "widget",
    JobSource::Embedded(slice.clone()),
    ProfileRef::Linked { name: "bambu-a1".to_string() },
)
.with_labor(LaborCost::new(10.0).with_rate_per_hour(15.0));
job.add_material(SINGLE_MATERIAL_SLOT, ProfileRef::Linked { name: "pla-black".to_string() });

// In a real app, the caller resolves `job.printer`/`job.material`
// (which may be `Linked` refs) first; here they're already resolved,
// so `calculate_cost` is called directly.
let material_profiles = HashMap::from([(SINGLE_MATERIAL_SLOT.to_string(), filament)]);
let breakdown = calculate_cost(
    &job,
    &slice,
    &printer,
    &material_profiles,
    None, // no `default_material` — every used slot is mapped above
    &CostDefaults::default(),
)
.expect("printer/filament kinds match the sliced file, and every used slot is priced");
assert!(breakdown.total_cost > 0.0);

Re-exports§

pub use disk::read_job;
pub use disk::read_material;
pub use disk::read_printer;
pub use disk::read_slice_file;
pub use formats::ParseError;
pub use formats::parse_slice_bytes;
pub use formats::slice_summary::FilamentUsage;
pub use formats::slice_summary::PlateSummary;
pub use formats::slice_summary::SliceMeta;
pub use formats::slice_summary::SliceSummary;
pub use formats::slice_summary::SlotUsage;
pub use formats::slice_summary::SourceFormat;
pub use matching::Match;
pub use matching::match_material;
pub use matching::match_printer;
pub use matching::material_from_slot;
pub use model::cost::CalcError;
pub use model::cost::CostBreakdown;
pub use model::cost::CostDefaults;
pub use model::cost::SuggestedPrice;
pub use model::cost::UnmappedSlot;
pub use model::cost::calculate_cost;
pub use model::filament::MaterialKind;
pub use model::filament::MaterialProfile;
pub use model::job::HardwareItem;
pub use model::job::Job;
pub use model::job::JobSource;
pub use model::job::JobTier;
pub use model::job::LaborCost;
pub use model::job::ProfileRef;
pub use model::job::SINGLE_MATERIAL_SLOT;
pub use model::printer::PrinterKind;
pub use model::printer::PrinterProfile;
pub use model::printer::WearProfile;

Modules§

builtin
Built-in printer/filament presets, embedded at compile time from builtin/*.toml (via include_str!) — plain data baked into the binary, no filesystem involved at runtime, so these are available to printcalc-wasm too. crate::calculate (and anything that resolves a Linked ref through it) falls back to these when a name isn’t an embedded profile; the CLI additionally checks its on-disk profile library first.
disk
Thin read-only disk helpers: load a sliced file, job, or profile from a path. This is the only part of libprintcalc that touches the filesystem — reading only; saving files, the profile library, and config persistence stay in the CLI (cli feature). The in-memory counterparts (crate::parse_slice_bytes, crate::parse_job) remain the right choice for callers without a filesystem (printcalc-wasm).
formats
SliceParser trait, format detection, and per-slicer parsers.
matching
Guessing printer and material profiles from what a sliced file already says about itself.
model
Data model: printer/filament profiles, jobs, and cost breakdowns.

Structs§

AutoJob
What auto_job worked out about a sliced file, alongside the job it built. Everything here is a guess from the file’s own metadata — the caller should show it and let the user override before costing.
AutoSlot
How one filament slot got priced by auto_job.

Enums§

Error
Why a crate-root call failed — wraps every error type the functions below can hit, so callers (the CLI, the WASM bindings) only need one error type to handle.

Functions§

auto_job
Builds a Job from a sliced file alone, taking every value it can from the file itself.
calculate
Costs a job end to end: pulls its slice data via Job::slice_summary (cloned from an Embedded source, or re-parsed from a Path source), resolves its printer/material refs (Embedded straight through, Linked against the built-in presets — there’s no on-disk library here; the CLI resolves Linked names against the local library before calling) and runs calculate_cost with the caller-supplied defaults.
create_material
Builds a material profile from its parts — the same shape parse_material produces from TOML. price is per kilogram for MaterialKind::Filament, per liter for MaterialKind::Resin.
create_printer
Builds a printer profile from its parts — the same shape parse_printer produces from TOML.
new_job
Builds a Job with the printer embedded by value (no library or built-in preset lookup at calc time) and materials keyed by FilamentUsage::slot_id — the single-material case is one entry under SINGLE_MATERIAL_SLOT. source is either a sliced-file path (re-parsed at calc time) or an already-parsed SliceSummary embedded directly. The remaining fields default (plate_selection: None, no overrides), and labor/hardware may be None/empty. Nothing is looked up or read here, so this can’t fail.
parse_job
Parses job TOML already in hand — no filesystem access, so this is what printcalc-wasm uses (no filesystem to load a job file from at all) and what the CLI’s load_job calls after reading the file.
parse_material
Parses filament/resin profile TOML already in hand — the same format read_material loads from disk and the CLI saves into its profile library.
parse_printer
Parses printer profile TOML already in hand — the same format read_printer loads from disk and the CLI saves into its profile library.
parse_toml
Parses TOML into T, also reporting every key in it that no field of T claimed — a typo (purchase_pric), a key in the wrong table, or a setting from a version that knew more than this one.