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_bytesturns sliced-file bytes into aSliceSummary;read_slice_filedoes the same straight from a path.parse_jobparses job TOML into aJob;read_jobreads it from a file.parse_printer/parse_materialparse profile TOML;read_printer/read_materialload the same from disk.parse_tomldoes 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_jobbuilds aJobfrom a sliced-file path (or an already-parsedSliceSummary) and an embedded printer profile — no lookups, can’t fail.auto_jobgoes 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. Seematching.calculatecosts a job, pulling its slice data viaJob::slice_summaryand resolvingEmbeddedprofile refs straight through andLinkedones against thebuiltinpresets. All of the above return the singleErrortype (exceptparse_slice_bytes, which returnsParseError).calculate_costis the pure core computation for callers supplying fully-resolved profiles themselves (asprintcalc-wasmdoes, 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(viainclude_str!) — plain data baked into the binary, no filesystem involved at runtime, so these are available toprintcalc-wasmtoo.crate::calculate(and anything that resolves aLinkedref 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
libprintcalcthat touches the filesystem — reading only; saving files, the profile library, and config persistence stay in the CLI (clifeature). The in-memory counterparts (crate::parse_slice_bytes,crate::parse_job) remain the right choice for callers without a filesystem (printcalc-wasm). - formats
SliceParsertrait, 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_jobworked 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. - Auto
Slot - 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
Jobfrom a sliced file alone, taking every value it can from the file itself. - calculate
- Costs a
jobend to end: pulls its slice data viaJob::slice_summary(cloned from anEmbeddedsource, or re-parsed from aPathsource), resolves its printer/material refs (Embeddedstraight through,Linkedagainst the built-in presets — there’s no on-disk library here; the CLI resolvesLinkednames against the local library before calling) and runscalculate_costwith the caller-supplieddefaults. - create_
material - Builds a material profile from its parts — the same shape
parse_materialproduces from TOML.priceis per kilogram forMaterialKind::Filament, per liter forMaterialKind::Resin. - create_
printer - Builds a printer profile from its parts — the same shape
parse_printerproduces from TOML. - new_job
- Builds a
Jobwith the printer embedded by value (no library or built-in preset lookup at calc time) andmaterialskeyed byFilamentUsage::slot_id— the single-material case is one entry underSINGLE_MATERIAL_SLOT.sourceis either a sliced-file path (re-parsed at calc time) or an already-parsedSliceSummaryembedded directly. The remaining fields default (plate_selection: None, no overrides), andlabor/hardwaremay beNone/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-wasmuses (no filesystem to load a job file from at all) and what the CLI’sload_jobcalls after reading the file. - parse_
material - Parses filament/resin profile TOML already in hand — the same format
read_materialloads from disk and the CLI saves into its profile library. - parse_
printer - Parses printer profile TOML already in hand — the same format
read_printerloads 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 ofTclaimed — a typo (purchase_pric), a key in the wrong table, or a setting from a version that knew more than this one.