From 44041ac8cf82d940cfeba9930b3b270448d6f696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ey=C3=BCp=20Can=20Akman?= Date: Sat, 6 Jun 2026 13:43:27 +0300 Subject: [PATCH 1/2] feat(d1): add #[must_use] to D1PreparedStatement A statement that never runs compiles fine but does nothing. Mark the type #[must_use] so that case is a compile warning, matching the KV builders. --- worker/src/d1/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/worker/src/d1/mod.rs b/worker/src/d1/mod.rs index e29f0044..5fd74c1a 100644 --- a/worker/src/d1/mod.rs +++ b/worker/src/d1/mod.rs @@ -332,6 +332,7 @@ impl D1Argument for D1PreparedArgument<'_> { // A D1 prepared query statement. #[derive(Debug, Clone)] +#[must_use = "D1PreparedStatement does nothing until you 'run' it"] pub struct D1PreparedStatement(D1PreparedStatementSys); impl D1PreparedStatement { From cb213d1db5a85e4132035e7331bab32e1b5b1037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ey=C3=BCp=20Can=20Akman?= Date: Tue, 16 Jun 2026 18:11:39 +0300 Subject: [PATCH 2/2] feat(d1): implement IntoFuture for D1PreparedStatement Awaiting a prepared statement now runs it through run(), so callers can write `stmt.await?` instead of `stmt.run().await?`. Update the must_use note to mention await. --- test/src/d1.rs | 3 ++- worker/src/d1/mod.rs | 30 +++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/test/src/d1.rs b/test/src/d1.rs index a38c3625..519deeb9 100644 --- a/test/src/d1.rs +++ b/test/src/d1.rs @@ -297,7 +297,8 @@ pub async fn insert_and_retrieve_optional_none( &None::, &None:: )?; - query.run().await?; + // Run the insert by awaiting the statement directly (exercises IntoFuture). + query.await?; let stmt = worker::query!(&db, "SELECT * FROM nullable_people WHERE id = 3"); let person = stmt.first::(None).await?.unwrap(); diff --git a/worker/src/d1/mod.rs b/worker/src/d1/mod.rs index 5fd74c1a..47799065 100644 --- a/worker/src/d1/mod.rs +++ b/worker/src/d1/mod.rs @@ -1,7 +1,9 @@ use std::fmt::Display; use std::fmt::Formatter; +use std::future::{Future, IntoFuture}; use std::iter::{once, Once}; use std::ops::Deref; +use std::pin::Pin; use std::result::Result as StdResult; use js_sys::futures::JsFuture; @@ -332,7 +334,7 @@ impl D1Argument for D1PreparedArgument<'_> { // A D1 prepared query statement. #[derive(Debug, Clone)] -#[must_use = "D1PreparedStatement does nothing until you 'run' it"] +#[must_use = "D1PreparedStatement does nothing until you run or await it"] pub struct D1PreparedStatement(D1PreparedStatementSys); impl D1PreparedStatement { @@ -446,6 +448,17 @@ impl D1PreparedStatement { } } +impl IntoFuture for D1PreparedStatement { + type Output = Result; + type IntoFuture = Pin>>; + + /// Awaiting runs the statement via [`run`](D1PreparedStatement::run) and returns only metadata. + /// Use [`all`](D1PreparedStatement::all) or [`first`](D1PreparedStatement::first) to retrieve rows. + fn into_future(self) -> Self::IntoFuture { + Box::pin(async move { self.run().await }) + } +} + impl From for D1PreparedStatement { fn from(inner: D1PreparedStatementSys) -> Self { Self(inner) @@ -589,3 +602,18 @@ mod tests { ); } } + +#[cfg(test)] +mod into_future_check { + // Awaiting a statement resolves through `run`, so the impl must yield + // `Result`. This compile-time check guards that contract. + use super::{D1PreparedStatement, D1Result}; + use crate::Result; + use std::future::IntoFuture; + + fn _assert_into_future>>() {} + #[allow(dead_code)] + fn _check() { + _assert_into_future::(); + } +}