9eab5e5a04
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.
114 lines
4.3 KiB
Rust
114 lines
4.3 KiB
Rust
//! Printer hierarchy — the Rust answer to
|
|
//! `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:
|
|
//!
|
|
//! 1. `PrinterBase` — a plain struct holding the shared *fields*. Every
|
|
//! printer struct below *has* one of these (composition) instead of
|
|
//! *extending* one.
|
|
//! 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".
|
|
//!
|
|
//! Run `cargo run --example printer_polymorphism` to see it in action.
|
|
//!
|
|
//! Deliberately left out of this module for now, to keep it focused on one
|
|
//! idea at a time: real MQTT/HTTP/FTPS calls (that's `src/adapters/`) and
|
|
//! `Send`-bound futures (matters once this is wired into a multi-threaded
|
|
//! `tokio::spawn`, not yet).
|
|
|
|
mod bambu;
|
|
mod klipper;
|
|
mod prusa;
|
|
|
|
pub use bambu::{BambuPrinter, BambuTls};
|
|
pub use klipper::KlipperPrinter;
|
|
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.
|
|
#[derive(Debug, thiserror::Error)]
|
|
#[error("{0}")]
|
|
pub struct PrinterError(pub String);
|
|
|
|
/// Fields every printer has, regardless of vendor.
|
|
#[derive(Debug, Clone)]
|
|
pub struct PrinterBase {
|
|
pub id: String,
|
|
pub name: String,
|
|
pub host: String,
|
|
}
|
|
|
|
/// 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;
|
|
|
|
/// REQUIRED, no default body: every vendor speaks a different protocol,
|
|
/// so there's nothing sensible to share here. Each `impl` in
|
|
/// `bambu.rs`/`prusa.rs`/`klipper.rs` provides a totally different
|
|
/// body — that's the override.
|
|
///
|
|
/// `cargo check` will warn about `async fn` in a public trait here.
|
|
/// That's about a more advanced concern (whether the resulting work can
|
|
/// safely move across OS threads) that only matters once this is wired
|
|
/// into the multi-threaded daemon — safe to ignore for now.
|
|
async fn connect(&mut self) -> Result<(), PrinterError>;
|
|
|
|
/// OPTIONAL: a default body every printer gets for free unless it
|
|
/// writes its own. None of ours do below, so all three vendors share
|
|
/// this exact implementation — the trait-method equivalent of
|
|
/// inheriting a base-class method unchanged.
|
|
fn display_name(&self) -> String {
|
|
format!("{} ({})", self.base().name, self.base().id)
|
|
}
|
|
}
|
|
|
|
pub enum PrinterFlavour {
|
|
BambuV1Printer,
|
|
BambuV2Printer,
|
|
PrusaLinkPrinter,
|
|
PrusaSerialPrinter,
|
|
KlipperPrinter,
|
|
}
|
|
|
|
/// The closed set of printer kinds this fleet can talk to.
|
|
pub enum PrinterHandle {
|
|
BambuV1(BambuPrinter),
|
|
BambuV2(BambuPrinter),
|
|
PrusaLink(PrusaLinkPrinter),
|
|
PrusaSerial(PrusaSerialPrinter),
|
|
Klipper(KlipperPrinter),
|
|
}
|
|
|
|
// Hand-written delegation: each trait method just matches on the variant
|
|
// 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 GenericPrinter for PrinterHandle {
|
|
fn base(&self) -> &PrinterBase {
|
|
match self {
|
|
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::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,
|
|
}
|
|
}
|
|
}
|