From 9b3815fd25c4e0af007e24fbd1da5f0dbaeeb651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iwo=20Strzebo=C5=84ski?= Date: Fri, 28 Aug 2026 17:09:11 +0000 Subject: [PATCH] Fix build errors: FTPS AsyncRead bridge, TLS connector type, unsafe env::set_var, dead code Verified with cargo check + cargo run --example printer_polymorphism. --- Cargo.toml | 4 ++-- src/adapters/bambu.rs | 9 +++++++-- src/main.rs | 18 +++--------------- src/plate_changer/mod.rs | 2 -- src/printer/klipper.rs | 7 ++++++- src/printer/mod.rs | 7 ++++++- src/printer/prusa.rs | 3 +++ 7 files changed, 27 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b42d275..bb845ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,12 +20,11 @@ path = "examples/printer_polymorphism.rs" [dependencies] tokio = { version = "1.40", features = ["full"] } tokio-tungstenite = { version = "0.24", features = ["native-tls"] } -tokio-util = { version = "0.7", features = ["codec"] } +tokio-util = { version = "0.7", features = ["codec", "compat"] } futures-util = "0.3" rumqttc = "0.24" reqwest = { version = "0.12", features = ["stream", "json"] } suppaftp = { version = "6", features = ["async-native-tls"] } -native-tls = "0.2" serde = { version = "1", features = ["derive"] } serde_json = "1" rusqlite = { version = "0.32", features = ["bundled"] } @@ -36,6 +35,7 @@ anyhow = "1" thiserror = "1" uuid = { version = "1", features = ["v4", "serde"] } rand = "0.8" +dotenvy = "0.15" # continuum-common's rust-core crate (error types, tracing init, G-code helpers) # lives in the sibling `continuum-common` repo. Once that crate is published to diff --git a/src/adapters/bambu.rs b/src/adapters/bambu.rs index 808be97..3c03d67 100644 --- a/src/adapters/bambu.rs +++ b/src/adapters/bambu.rs @@ -3,8 +3,10 @@ use std::time::Duration; use rumqttc::{AsyncClient, Event, MqttOptions, Packet, QoS, TlsConfiguration, Transport}; use serde::{Deserialize, Serialize}; +use suppaftp::async_native_tls::TlsConnector; use suppaftp::{AsyncNativeTlsConnector, AsyncNativeTlsFtpStream}; use tokio::sync::mpsc; +use tokio_util::compat::TokioAsyncReadCompatExt; use tracing::{debug, info, warn}; const MQTT_PORT: u16 = 8883; @@ -127,11 +129,14 @@ pub async fn dispatch_file(printer: &BambuPrinter, local_path: &Path, remote_nam let ftp = AsyncNativeTlsFtpStream::connect(format!("{}:{FTPS_PORT}", printer.host)).await?; let mut ftp = ftp - .into_secure(AsyncNativeTlsConnector::from(native_tls::TlsConnector::new()?), &printer.host) + .into_secure(AsyncNativeTlsConnector::from(TlsConnector::new()), &printer.host) .await?; ftp.login("bblp", &printer.access_code).await?; - let mut file = tokio::fs::File::open(local_path).await?; + // suppaftp wants a `futures::io::AsyncRead`; tokio::fs::File only + // implements tokio's own AsyncRead, so bridge it with tokio-util's + // `.compat()` adapter rather than pulling in a second async-fs stack. + let mut file = tokio::fs::File::open(local_path).await?.compat(); ftp.put_file(remote_name, &mut file).await?; ftp.quit().await?; diff --git a/src/main.rs b/src/main.rs index 86d171b..ebc9ff2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -111,20 +111,8 @@ async fn main() -> anyhow::Result<()> { Ok(()) } -/// Loads a `.env` file if present, without pulling in a heavyweight config -/// crate. No-op (and safe to ignore errors) when the file doesn't exist. +/// Loads `.env` if present; a missing file is fine (env vars may already be +/// set by the process supervisor), so the error is deliberately ignored. fn dotenvy_load() { - if let Ok(contents) = std::fs::read_to_string(".env") { - for line in contents.lines() { - let line = line.trim(); - if line.is_empty() || line.starts_with('#') { - continue; - } - if let Some((key, value)) = line.split_once('=') { - if std::env::var(key).is_err() { - std::env::set_var(key, value); - } - } - } - } + let _ = dotenvy::dotenv(); } diff --git a/src/plate_changer/mod.rs b/src/plate_changer/mod.rs index d611e66..c82e316 100644 --- a/src/plate_changer/mod.rs +++ b/src/plate_changer/mod.rs @@ -1,8 +1,6 @@ mod gpio; mod serial; -pub use gpio::SensorState; - use thiserror::Error; use tracing::{info, warn}; diff --git a/src/printer/klipper.rs b/src/printer/klipper.rs index 9d2f77f..dc4d3ba 100644 --- a/src/printer/klipper.rs +++ b/src/printer/klipper.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use tracing::info; use super::{Printer, PrinterBase, PrinterError}; @@ -27,7 +29,10 @@ impl Printer for KlipperPrinter { async fn connect(&mut self) -> Result<(), PrinterError> { info!(printer = %self.base.id, host = %self.base.host, "probing Moonraker over HTTP"); - reqwest::get(format!("http://{}/printer/info", self.base.host)) + reqwest::Client::new() + .get(format!("http://{}/printer/info", self.base.host)) + .timeout(Duration::from_secs(5)) + .send() .await .and_then(|resp| resp.error_for_status()) .map_err(|err| PrinterError::Connection(self.base.id.clone(), err.to_string()))?; diff --git a/src/printer/mod.rs b/src/printer/mod.rs index 777f325..25fd936 100644 --- a/src/printer/mod.rs +++ b/src/printer/mod.rs @@ -54,7 +54,12 @@ pub trait Printer { /// REQUIRED, no default body: every printer vendor speaks a different /// protocol, so there's nothing sensible to share here. Each `impl` /// below provides a completely different body — that's the "override". - async fn connect(&mut self) -> Result<(), PrinterError>; + /// + /// Spelled as `-> impl Future<..> + Send` rather than plain `async fn` + /// so the resulting future is `Send` and can be `.await`ed inside a + /// `tokio::spawn`ed task; each `impl` below still just writes a normal + /// `async fn` body, which satisfies this signature automatically. + fn connect(&mut self) -> impl std::future::Future> + Send; /// OPTIONAL: a default body every printer gets for free unless it /// chooses to provide its own. None of ours do, so all three vendors diff --git a/src/printer/prusa.rs b/src/printer/prusa.rs index b5f34dd..1427d6f 100644 --- a/src/printer/prusa.rs +++ b/src/printer/prusa.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use tracing::info; use super::{Printer, PrinterBase, PrinterError}; @@ -30,6 +32,7 @@ impl Printer for PrusaPrinter { reqwest::Client::new() .get(format!("http://{}/api/v1/status", self.base.host)) .header("X-Api-Key", &self.api_key) + .timeout(Duration::from_secs(5)) .send() .await .and_then(|resp| resp.error_for_status())