Files
continuum-proxy/examples/printer_polymorphism.rs
T
octoturge 6e18215af5 Add GenericPrinter::set_fan_speed() across all five printer kinds
Same override pattern as connect(): required, no default body, five
completely different bodies.

- BambuGenericPrinter::set_fan_speed_impl() is shared by BambuV1Printer and
  BambuV2Printer (composition reuse, same as connect_impl) - both speak
  the same MQTT gcode-injection mechanism.
- PrusaLinkPrinter and PrusaSerialPrinter both send the same M106 gcode
  but over different transports (HTTP command injection vs. raw serial
  bytes) - they share the percent->PWM conversion despite having unrelated
  connect() implementations.
- KlipperPrinter uses a hypothetical Moonraker-native endpoint that takes
  a percentage directly - no PWM conversion at all, since that math only
  applies to the gcode-speaking vendors.

percent_to_pwm() lives in mod.rs as a private free function rather than a
trait default: it's genuine shared logic, but only for the subset of
vendors that need it, which is exactly the case a trait default can't
express cleanly. Visible to bambu.rs/prusa.rs via super:: because Rust's
module privacy reaches into child modules.

Verified with cargo check --all-targets and a full run of
printer_polymorphism: 50% converges on the same PWM value (127) across all
three gcode-based printers, Klipper's native path takes 0.50 directly.
2026-08-28 21:40:11 +00:00

75 lines
3.1 KiB
Rust

//! Run with: cargo run --example printer_polymorphism
//!
//! Demonstrates the trait+enum pattern that stands in for inheritance:
//! - `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 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::{
BambuTls, BambuV1Printer, BambuV2Printer, GenericPrinter, KlipperPrinter, PrinterHandle, PrusaLinkPrinter,
PrusaSerialPrinter,
};
#[tokio::main]
async fn main() {
let mut fleet: Vec<PrinterHandle> = vec![
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")),
// No host configured — this one will hit the Err path.
PrinterHandle::Klipper(KlipperPrinter::new("p6", "Broken Voron", "")),
];
// Uniform call site: every printer connects the same way from the
// caller's point of view, even though each variant's `connect()` body
// is an unrelated implementation.
for printer in &mut fleet {
match printer.connect().await {
Ok(()) => println!(" -> connected: {}\n", printer.display_name()),
Err(err) => println!(" -> failed: {err}\n"),
}
}
// Vendor-only extension: reachable only after matching the concrete
// 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 {
match printer {
PrinterHandle::BambuV1(bambu) => bambu.dispatch_gcode("part.gcode.3mf"),
PrinterHandle::BambuV2(bambu) => bambu.dispatch_gcode("part.gcode.3mf"),
_ => {}
}
}
// A second trait method, same uniform call site as connect(): every
// vendor sends "50%" a completely different way — an MQTT gcode
// command, Moonraker's native API, gcode over HTTP, gcode over serial
// — but the caller doesn't need to know or care which.
println!();
for printer in &mut fleet {
let _ = printer.set_fan_speed(50).await;
}
}