Fix build errors: FTPS AsyncRead bridge, TLS connector type, unsafe env::set_var, dead code

Verified with cargo check + cargo run --example printer_polymorphism.
This commit is contained in:
2026-08-28 17:09:11 +00:00
parent 03bd07fbd7
commit 9b3815fd25
7 changed files with 27 additions and 23 deletions
+2 -2
View File
@@ -20,12 +20,11 @@ path = "examples/printer_polymorphism.rs"
[dependencies] [dependencies]
tokio = { version = "1.40", features = ["full"] } tokio = { version = "1.40", features = ["full"] }
tokio-tungstenite = { version = "0.24", features = ["native-tls"] } 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" futures-util = "0.3"
rumqttc = "0.24" rumqttc = "0.24"
reqwest = { version = "0.12", features = ["stream", "json"] } reqwest = { version = "0.12", features = ["stream", "json"] }
suppaftp = { version = "6", features = ["async-native-tls"] } suppaftp = { version = "6", features = ["async-native-tls"] }
native-tls = "0.2"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
rusqlite = { version = "0.32", features = ["bundled"] } rusqlite = { version = "0.32", features = ["bundled"] }
@@ -36,6 +35,7 @@ anyhow = "1"
thiserror = "1" thiserror = "1"
uuid = { version = "1", features = ["v4", "serde"] } uuid = { version = "1", features = ["v4", "serde"] }
rand = "0.8" rand = "0.8"
dotenvy = "0.15"
# continuum-common's rust-core crate (error types, tracing init, G-code helpers) # 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 # lives in the sibling `continuum-common` repo. Once that crate is published to
+7 -2
View File
@@ -3,8 +3,10 @@ use std::time::Duration;
use rumqttc::{AsyncClient, Event, MqttOptions, Packet, QoS, TlsConfiguration, Transport}; use rumqttc::{AsyncClient, Event, MqttOptions, Packet, QoS, TlsConfiguration, Transport};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use suppaftp::async_native_tls::TlsConnector;
use suppaftp::{AsyncNativeTlsConnector, AsyncNativeTlsFtpStream}; use suppaftp::{AsyncNativeTlsConnector, AsyncNativeTlsFtpStream};
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tokio_util::compat::TokioAsyncReadCompatExt;
use tracing::{debug, info, warn}; use tracing::{debug, info, warn};
const MQTT_PORT: u16 = 8883; 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 ftp = AsyncNativeTlsFtpStream::connect(format!("{}:{FTPS_PORT}", printer.host)).await?;
let mut ftp = ftp let mut ftp = ftp
.into_secure(AsyncNativeTlsConnector::from(native_tls::TlsConnector::new()?), &printer.host) .into_secure(AsyncNativeTlsConnector::from(TlsConnector::new()), &printer.host)
.await?; .await?;
ftp.login("bblp", &printer.access_code).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.put_file(remote_name, &mut file).await?;
ftp.quit().await?; ftp.quit().await?;
+3 -15
View File
@@ -111,20 +111,8 @@ async fn main() -> anyhow::Result<()> {
Ok(()) Ok(())
} }
/// Loads a `.env` file if present, without pulling in a heavyweight config /// Loads `.env` if present; a missing file is fine (env vars may already be
/// crate. No-op (and safe to ignore errors) when the file doesn't exist. /// set by the process supervisor), so the error is deliberately ignored.
fn dotenvy_load() { fn dotenvy_load() {
if let Ok(contents) = std::fs::read_to_string(".env") { let _ = dotenvy::dotenv();
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);
}
}
}
}
} }
-2
View File
@@ -1,8 +1,6 @@
mod gpio; mod gpio;
mod serial; mod serial;
pub use gpio::SensorState;
use thiserror::Error; use thiserror::Error;
use tracing::{info, warn}; use tracing::{info, warn};
+6 -1
View File
@@ -1,3 +1,5 @@
use std::time::Duration;
use tracing::info; use tracing::info;
use super::{Printer, PrinterBase, PrinterError}; use super::{Printer, PrinterBase, PrinterError};
@@ -27,7 +29,10 @@ impl Printer for KlipperPrinter {
async fn connect(&mut self) -> Result<(), PrinterError> { async fn connect(&mut self) -> Result<(), PrinterError> {
info!(printer = %self.base.id, host = %self.base.host, "probing Moonraker over HTTP"); 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 .await
.and_then(|resp| resp.error_for_status()) .and_then(|resp| resp.error_for_status())
.map_err(|err| PrinterError::Connection(self.base.id.clone(), err.to_string()))?; .map_err(|err| PrinterError::Connection(self.base.id.clone(), err.to_string()))?;
+6 -1
View File
@@ -54,7 +54,12 @@ pub trait Printer {
/// REQUIRED, no default body: every printer vendor speaks a different /// REQUIRED, no default body: every printer vendor speaks a different
/// protocol, so there's nothing sensible to share here. Each `impl` /// protocol, so there's nothing sensible to share here. Each `impl`
/// below provides a completely different body — that's the "override". /// 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<Output = Result<(), PrinterError>> + Send;
/// OPTIONAL: a default body every printer gets for free unless it /// OPTIONAL: a default body every printer gets for free unless it
/// chooses to provide its own. None of ours do, so all three vendors /// chooses to provide its own. None of ours do, so all three vendors
+3
View File
@@ -1,3 +1,5 @@
use std::time::Duration;
use tracing::info; use tracing::info;
use super::{Printer, PrinterBase, PrinterError}; use super::{Printer, PrinterBase, PrinterError};
@@ -30,6 +32,7 @@ impl Printer for PrusaPrinter {
reqwest::Client::new() reqwest::Client::new()
.get(format!("http://{}/api/v1/status", self.base.host)) .get(format!("http://{}/api/v1/status", self.base.host))
.header("X-Api-Key", &self.api_key) .header("X-Api-Key", &self.api_key)
.timeout(Duration::from_secs(5))
.send() .send()
.await .await
.and_then(|resp| resp.error_for_status()) .and_then(|resp| resp.error_for_status())