Bambu TLS trust config + finish PrusaLink/PrusaSerial split

Certs:
- Vendor Bambu's shared LAN-mode root CA (certs/bambu_ca2.pem, verified
  self-signed CA:TRUE, see certs/README.md for provenance/fingerprint).
- Config gains bambu_ca_cert_path (per-printer override) and
  bambu_require_valid_cert (the allow/reject flag); printer::bambu::BambuTls
  turns those into BundledCa/Custom/Insecure. connect() doesn't perform a
  real handshake yet (no TLS-capable MQTT client wired in), just reports
  which trust mode it would use.

Also reconciles a rename in flight (Printer -> GenericPrinter trait) and
finishes the PrusaPrinter -> PrusaLinkPrinter/PrusaSerialPrinter split:
added the missing GenericPrinter impl for PrusaSerialPrinter, fixed
PrinterHandle's variant payload types, updated mod.rs's pub use list and
doc comments, and updated the example to the new 5-variant shape.

Verified with cargo check --all-targets (0 errors) and a full run of
cargo run --example printer_polymorphism.
This commit is contained in:
2026-08-28 20:38:10 +00:00
parent d0a17ee50e
commit 9eab5e5a04
8 changed files with 302 additions and 45 deletions
+31 -17
View File
@@ -1,5 +1,5 @@
//! Printer hierarchy — the Rust answer to
//! `GenericPrinter -> BambuPrinter/PrusaPrinter/KlipperPrinter`-style
//! `GenericPrinter -> BambuV1/BambuV2/PrusaLink/PrusaSerial/Klipper`-style
//! inheritance, since Rust structs can't `extend` each other.
//!
//! Three pieces, each doing one job an OOP base class would normally do:
@@ -7,10 +7,10 @@
//! 1. `PrinterBase` — a plain struct holding the shared *fields*. Every
//! printer struct below *has* one of these (composition) instead of
//! *extending* one.
//! 2. `Printer` — a trait holding the shared *behavior*. `connect()` has no
//! default body, so every printer type is forced to write its own —
//! that's what "overriding" looks like when there's no inherited body to
//! override in the first place.
//! 2. `GenericPrinter` — a trait holding the shared *behavior*. `connect()`
//! has no default body, so every printer type is forced to write its
//! own — that's what "overriding" looks like when there's no inherited
//! body to override in the first place.
//! 3. `PrinterHandle` — an enum listing the closed set of printer kinds.
//! Stands in for "any subclass of GenericPrinter".
//!
@@ -25,9 +25,9 @@ mod bambu;
mod klipper;
mod prusa;
pub use bambu::BambuPrinter;
pub use bambu::{BambuPrinter, BambuTls};
pub use klipper::KlipperPrinter;
pub use prusa::PrusaPrinter;
pub use prusa::{PrusaLinkPrinter, PrusaSerialPrinter};
/// A tiny error type: just a message. `String` would work too — this exists
/// mainly so `?` has something concrete to convert into everywhere.
@@ -43,9 +43,9 @@ pub struct PrinterBase {
pub host: String,
}
/// The shared interface — think `interface Printer` (Java/TS) or an ABC
/// (Python). Every vendor's struct implements this.
pub trait Printer {
/// The shared interface — think `interface GenericPrinter` (Java/TS) or an
/// ABC (Python). Every vendor's struct implements this.
pub trait GenericPrinter {
/// Read-only access to the shared fields.
fn base(&self) -> &PrinterBase;
@@ -69,10 +69,20 @@ pub trait Printer {
}
}
pub enum PrinterFlavour {
BambuV1Printer,
BambuV2Printer,
PrusaLinkPrinter,
PrusaSerialPrinter,
KlipperPrinter,
}
/// The closed set of printer kinds this fleet can talk to.
pub enum PrinterHandle {
Bambu(BambuPrinter),
Prusa(PrusaPrinter),
BambuV1(BambuPrinter),
BambuV2(BambuPrinter),
PrusaLink(PrusaLinkPrinter),
PrusaSerial(PrusaSerialPrinter),
Klipper(KlipperPrinter),
}
@@ -80,19 +90,23 @@ pub enum PrinterHandle {
// and forwards to that variant's own implementation. This one block is the
// only "boilerplate tax" for not having inheritance — everything else reads
// like normal code.
impl Printer for PrinterHandle {
impl GenericPrinter for PrinterHandle {
fn base(&self) -> &PrinterBase {
match self {
PrinterHandle::Bambu(p) => p.base(),
PrinterHandle::Prusa(p) => p.base(),
PrinterHandle::BambuV1(p) => p.base(),
PrinterHandle::BambuV2(p) => p.base(),
PrinterHandle::PrusaLink(p) => p.base(),
PrinterHandle::PrusaSerial(p) => p.base(),
PrinterHandle::Klipper(p) => p.base(),
}
}
async fn connect(&mut self) -> Result<(), PrinterError> {
match self {
PrinterHandle::Bambu(p) => p.connect().await,
PrinterHandle::Prusa(p) => p.connect().await,
PrinterHandle::BambuV1(p) => p.connect().await,
PrinterHandle::BambuV2(p) => p.connect().await,
PrinterHandle::PrusaLink(p) => p.connect().await,
PrinterHandle::PrusaSerial(p) => p.connect().await,
PrinterHandle::Klipper(p) => p.connect().await,
}
}