Split BambuPrinter into BambuGenericPrinter -> BambuV1Printer/BambuV2Printer

Completes the GenericPrinter -> BambuGenericPrinter -> BambuV1/V2 chain this
project wanted from the start. BambuGenericPrinter holds the fields and
behavior every Bambu printer shares (access code, serial number, CA trust,
the TLS test/fetch methods); BambuV1Printer and BambuV2Printer each *have*
one (composition) and are now genuinely separate types, ready to carry
flavour-specific report-schema fields later. Also threads through a new
'sn' (serial number) field the real MQTT topics will need.

Adds real 'fetch the CA automatically' capability, verified against a live
TLS server (not just compiled):
- BambuGenericPrinter::fetch_certificate() does trust-on-first-connect —
  connects once with verification disabled, captures the certificate the
  printer actually presents via native_tls's peer_certificate()/to_der(),
  and returns it as PEM. Deliberately a method you call once by hand
  (examples/fetch_bambu_cert.rs), not something connect() falls back to
  silently, since TOFU trusts whoever's on the network the moment you run
  it. Verified end-to-end against a local openssl s_server: the fetched
  PEM's SHA-256 fingerprint exactly matched the server's real certificate.
- Verified the other direction too: test_bambu_certs (bundled-CA mode)
  correctly REJECTS that same test server's cert, since it wasn't signed
  by the real Bambu CA.

Splitting BambuV1Printer/BambuV2Printer into distinct types broke the
PrinterHandle::BambuV1(x) | PrinterHandle::BambuV2(x) or-pattern in both
examples (or-patterns require every alternative to bind the same type) —
fixed by giving each variant its own match arm.
This commit is contained in:
2026-08-28 21:04:34 +00:00
parent d8b7430296
commit 8572d564b1
8 changed files with 259 additions and 69 deletions
+30 -10
View File
@@ -4,19 +4,34 @@
//! - `connect()` is called the same way regardless of vendor (polymorphism),
//! but each variant's `impl GenericPrinter` runs completely different
//! code (override).
//! - `dispatch_gcode()` only exists on `BambuPrinter` (extension) — you
//! have to `match` the enum back down to the concrete type to reach it,
//! which is the trade-off for not having implicit downcasting.
//! - `dispatch_gcode()` only exists on the two Bambu structs (extension) —
//! you have to `match` the enum back down to the concrete type to reach
//! it, which is the trade-off for not having implicit downcasting.
use continuum_proxy::printer::{
BambuPrinter, BambuTls, GenericPrinter, KlipperPrinter, PrinterHandle, PrusaLinkPrinter, PrusaSerialPrinter,
BambuTls, BambuV1Printer, BambuV2Printer, GenericPrinter, KlipperPrinter, PrinterHandle, PrusaLinkPrinter,
PrusaSerialPrinter,
};
#[tokio::main]
async fn main() {
let mut fleet: Vec<PrinterHandle> = vec![
PrinterHandle::BambuV1(BambuPrinter::new("p1", "P1S", "192.168.1.49", "87654321", BambuTls::BundledCa)),
PrinterHandle::BambuV2(BambuPrinter::new("p2", "X1 Carbon", "192.168.1.50", "12345678", BambuTls::BundledCa)),
PrinterHandle::BambuV1(BambuV1Printer::new(
"p1",
"P1S",
"192.168.1.49",
"87654321",
"01P00A000000000",
BambuTls::BundledCa,
)),
PrinterHandle::BambuV2(BambuV2Printer::new(
"p2",
"X1 Carbon",
"192.168.1.50",
"12345678",
"00M00A000000000",
BambuTls::BundledCa,
)),
PrinterHandle::PrusaLink(PrusaLinkPrinter::new("p3", "MK4", "192.168.1.51", "prusa-api-key")),
PrinterHandle::PrusaSerial(PrusaSerialPrinter::new("p4", "MK3S+", 0)),
PrinterHandle::Klipper(KlipperPrinter::new("p5", "Voron 2.4", "192.168.1.52")),
@@ -35,11 +50,16 @@ async fn main() {
}
// Vendor-only extension: reachable only after matching the concrete
// variant back out of the enum. Both Bambu variants wrap the same
// BambuPrinter struct right now, so both get dispatch_gcode().
// variant back out of the enum. BambuV1Printer and BambuV2Printer are
// separate types now (each *has* a BambuGenericPrinter rather than
// being the same struct), so — unlike before the split — this needs
// two arms instead of one `A(x) | B(x)` pattern: an or-pattern requires
// every alternative to bind the same type, and these no longer do.
for printer in &fleet {
if let PrinterHandle::BambuV1(bambu) | PrinterHandle::BambuV2(bambu) = printer {
bambu.dispatch_gcode("part.gcode.3mf");
match printer {
PrinterHandle::BambuV1(bambu) => bambu.dispatch_gcode("part.gcode.3mf"),
PrinterHandle::BambuV2(bambu) => bambu.dispatch_gcode("part.gcode.3mf"),
_ => {}
}
}
}