From 4ab95ff7ce46688a23ec6b4662b369606d08b37d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iwo=20Strzebo=C5=84ski?= Date: Sat, 3 Oct 2026 12:16:48 +0000 Subject: [PATCH] Compile the modules once, and clear the warning backlog `cargo check --all-targets` emitted 32 warnings; it now emits none. Almost all of them came from one cause: main.rs declared `mod config/fleet/printer/uplink` while lib.rs declared the same modules, so every file was compiled twice, once per crate. In the binary copy nothing is externally reachable, so every `pub use` re-export and every method that only an example or a test calls was genuinely dead - and the warnings were real about the binary while being wrong about the library's actual public API. main.rs now uses the library crate and keeps only `mod go2rtc`, which nothing else needs. The rest, individually: - `ModuleInfo` was re-exported from bambu_commands but not from printer, so it was unreachable even though `GetVersionResponse.module` is a public field of type `Vec` - a caller could not name the type of a field it can read. Re-exported rather than dropped. - Three stub `publish` impls take a payload they do not use yet; renamed to `_payload`. - One unused import in examples/test_bambu_certs.rs. That leaves the `async_fn_in_trait` lint on GenericPrinter, which is now `allow`ed with the reasoning written down rather than left to fire on every build. The old comment said it "only matters once this is wired into the multi-threaded daemon", which has since happened - so the question got answered instead of deferred: the futures cannot be required to be `Send`, and that is fine because every printer is reached through one Arc>> from the uplink's own task, so they are created and polled in the same place. The note says what to do if a printer ever gets its own task. No behaviour change. Tests still pass and the daemon was re-verified against the live P1P and H2C through the full backend path. Co-Authored-By: Claude Opus 5 --- examples/test_bambu_certs.rs | 2 +- src/lib.rs | 11 +++++++---- src/main.rs | 15 +++++++++------ src/printer/klipper.rs | 2 +- src/printer/mod.rs | 24 +++++++++++++++++------- src/printer/prusa.rs | 4 ++-- 6 files changed, 37 insertions(+), 21 deletions(-) diff --git a/examples/test_bambu_certs.rs b/examples/test_bambu_certs.rs index e23c367..ae29aa3 100644 --- a/examples/test_bambu_certs.rs +++ b/examples/test_bambu_certs.rs @@ -7,7 +7,7 @@ //! this trust question. use continuum_proxy::fleet; -use continuum_proxy::printer::{GenericPrinter, PrinterHandle}; +use continuum_proxy::printer::PrinterHandle; fn main() -> anyhow::Result<()> { let printers = fleet::load(std::path::Path::new("printers.toml"))?; diff --git a/src/lib.rs b/src/lib.rs index 8adebf5..1f9c07e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,10 @@ -//! Library surface for `continuum-proxy`. The daemon itself is a binary -//! (`src/main.rs`); this crate root exists so standalone examples (see -//! `examples/`) and future integration tests can `use continuum_proxy::...` -//! without duplicating module declarations. +//! Library surface for `continuum-proxy`. The daemon itself is a thin binary +//! (`src/main.rs`) that uses this crate; the examples in `examples/` and the +//! integration tests in `tests/` use it too. +//! +//! Everything the daemon needs lives here rather than in the binary, so it is +//! compiled once. `go2rtc` is the one exception: the camera-restreaming +//! watchdog is the daemon's own concern and nothing else calls it. pub mod config; pub mod fleet; diff --git a/src/main.rs b/src/main.rs index c2dc816..f57fb09 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,16 @@ -mod config; -mod fleet; +// Everything but go2rtc comes from the library crate (src/lib.rs) rather than +// being re-declared here. Declaring `mod printer;` in both places compiles the +// same files twice, once per crate, and the binary copy warned about every +// `pub use` re-export and every method only examples or tests call - "unused" +// is true inside a binary, where nothing is externally reachable, and wrong +// about the library's actual public API. One copy, no false warnings. mod go2rtc; -mod printer; -mod uplink; use tracing_subscriber::EnvFilter; -use crate::config::Config; -use crate::printer::{GenericPrinter, PrinterHandle}; +use continuum_proxy::config::Config; +use continuum_proxy::printer::{GenericPrinter, PrinterHandle}; +use continuum_proxy::{fleet, uplink}; #[tokio::main] async fn main() -> anyhow::Result<()> { diff --git a/src/printer/klipper.rs b/src/printer/klipper.rs index 107d629..0faf4be 100644 --- a/src/printer/klipper.rs +++ b/src/printer/klipper.rs @@ -29,7 +29,7 @@ impl GenericPrinter for KlipperPrinter { Ok(()) } - async fn publish(&mut self, payload: String) -> Result<(), PrinterError> { + async fn publish(&mut self, _payload: String) -> Result<(), PrinterError> { // TODO: add publishing via HTTP Ok(()) } diff --git a/src/printer/mod.rs b/src/printer/mod.rs index 936afea..b4c5600 100644 --- a/src/printer/mod.rs +++ b/src/printer/mod.rs @@ -38,7 +38,7 @@ mod prusa; pub use bambu::{BambuPrinter, BambuProtocolPreference, BambuProtocolVersion, BambuTls}; pub use bambu_commands::{ AmsState, AmsTray, AmsUnit, BambuReport, BambuRequest, CommandEnvelope, EmptyTray, GetVersionRequest, - GetVersionResponse, LoadedTray, PrintReport, PushallRequest, XcamSettings, + GetVersionResponse, LoadedTray, ModuleInfo, PrintReport, PushallRequest, XcamSettings, }; pub use klipper::KlipperPrinter; pub use prusa::{PrusaLinkPrinter, PrusaSerialPrinter}; @@ -59,6 +59,19 @@ pub struct PrinterBase { /// The shared interface - think `interface GenericPrinter` (Java/TS) or an /// ABC (Python). Every vendor's struct implements this. +/// +/// The `allow` is a considered decision, not a brush-off. `async fn` in a +/// public trait means callers cannot require the returned futures to be +/// `Send`, so such a future cannot be moved to another OS thread. That is +/// fine here, and checked: the daemon does run on tokio's multi-threaded +/// runtime, but every printer is reached through one `Arc>>` +/// from the uplink's own task (see `uplink::SharedFleet`), so these futures +/// are created and polled in the same place. Revisit if a printer ever gets +/// its own task - the fix is to desugar to +/// `fn connect(&mut self) -> impl Future + Send`, which cannot +/// be done without changing every implementation, which is why it is not +/// done pre-emptively. +#[allow(async_fn_in_trait)] pub trait GenericPrinter { /// Read-only access to the shared fields. fn base(&self) -> &PrinterBase; @@ -67,14 +80,11 @@ pub trait GenericPrinter { /// 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>; - + /// Sends one already-encoded message by whatever transport this vendor + /// uses. Lower level than the methods below and mostly a building block + /// for them - `bambu.rs` publishes MQTT with it; the stubs only log. async fn publish(&mut self, payload: String) -> Result<(), PrinterError>; /// REQUIRED, no default body: `percent` is 0-100, but how it actually diff --git a/src/printer/prusa.rs b/src/printer/prusa.rs index dc72aa6..ff635e0 100644 --- a/src/printer/prusa.rs +++ b/src/printer/prusa.rs @@ -29,7 +29,7 @@ impl GenericPrinter for PrusaLinkPrinter { Ok(()) } - async fn publish(&mut self, payload: String) -> Result<(), PrinterError> { + async fn publish(&mut self, _payload: String) -> Result<(), PrinterError> { // TODO: add publishing via HTTP Ok(()) } @@ -100,7 +100,7 @@ impl GenericPrinter for PrusaSerialPrinter { Ok(()) } - async fn publish(&mut self, payload: String) -> Result<(), PrinterError> { + async fn publish(&mut self, _payload: String) -> Result<(), PrinterError> { // TODO: add publishing via serial Ok(()) }