Skip to content

Commit 44bbd9f

Browse files
authored
refactor!: make Project require Send (#472)
* refactor: make `Project` require `Send` Not requiring this probably isn't very useful (as there are most likely not many use cases where `Project` needs to contain any data; yet alone data that cannot be `Send`). With `Project: Send`, the bootstrapper is `Send` as well which is useful when you want to run the server in tokio::spawn (for instance, as part of an already running async program) instead of tokio::block_on. * arguably it's better to add the Send bound on usages, not the trait itself
1 parent 97d4b3e commit 44bbd9f

3 files changed

Lines changed: 10 additions & 30 deletions

File tree

cot/src/middleware/live_reload.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,6 @@ mod tests {
169169
test_live_reload_from_context(false).await;
170170
}
171171

172-
#[expect(clippy::future_not_send, reason = "test function using Bootstrapper")]
173172
async fn test_live_reload_from_context(enabled: bool) {
174173
struct TestProject;
175174
impl Project for TestProject {}

cot/src/project.rs

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -849,7 +849,7 @@ async fn default_error_handler(error: RequestOuterError) -> crate::Result<impl I
849849
#[derive(Debug)]
850850
pub struct Bootstrapper<S: BootstrapPhase = Initialized> {
851851
#[debug("..")]
852-
project: Box<dyn Project>,
852+
project: Box<dyn Project + Send>,
853853
context: ProjectContext<S>,
854854
handler: S::RequestHandler,
855855
error_handler: S::ErrorHandler,
@@ -874,7 +874,7 @@ impl Bootstrapper<Uninitialized> {
874874
/// # }
875875
/// ```
876876
#[must_use]
877-
pub fn new<P: Project + 'static>(project: P) -> Self {
877+
pub fn new<P: Project + Send + 'static>(project: P) -> Self {
878878
Self {
879879
project: Box::new(project),
880880
context: ProjectContext::new(),
@@ -1085,8 +1085,6 @@ impl Bootstrapper<WithConfig> {
10851085
/// # Ok(())
10861086
/// # }
10871087
/// ```
1088-
// Send not needed; Bootstrapper is run async in a single thread
1089-
#[expect(clippy::future_not_send)]
10901088
pub async fn boot(self) -> cot::Result<Bootstrapper<Initialized>> {
10911089
self.with_apps().boot().await
10921090
}
@@ -1171,8 +1169,6 @@ impl Bootstrapper<WithApps> {
11711169
/// # Ok(())
11721170
/// # }
11731171
/// ```
1174-
// Send not needed; Bootstrapper is run async in a single thread
1175-
#[expect(clippy::future_not_send)]
11761172
pub async fn boot(self) -> cot::Result<Bootstrapper<Initialized>> {
11771173
self.with_database().await?.boot().await
11781174
}
@@ -1209,8 +1205,6 @@ impl Bootstrapper<WithApps> {
12091205
/// # Ok(())
12101206
/// # }
12111207
/// ```
1212-
// Send not needed; Bootstrapper is run async in a single thread
1213-
#[expect(clippy::future_not_send)]
12141208
pub async fn with_database(self) -> cot::Result<Bootstrapper<WithDatabase>> {
12151209
#[cfg(feature = "db")]
12161210
let database = Self::init_database(&self.context.config.database).await?;
@@ -1275,8 +1269,6 @@ impl Bootstrapper<WithDatabase> {
12751269
/// # }
12761270
/// ```
12771271
// Function marked `async` to be consistent with the other `boot` methods
1278-
// Send not needed; Bootstrapper is run async in a single thread
1279-
#[expect(clippy::future_not_send)]
12801272
pub async fn boot(self) -> cot::Result<Bootstrapper<Initialized>> {
12811273
self.with_cache().await?.boot().await
12821274
}
@@ -1314,7 +1306,6 @@ impl Bootstrapper<WithDatabase> {
13141306
/// # Ok(())
13151307
/// # }
13161308
/// ```
1317-
#[expect(clippy::future_not_send)]
13181309
#[allow(
13191310
clippy::unused_async,
13201311
clippy::allow_attributes,
@@ -1379,7 +1370,10 @@ impl Bootstrapper<WithCache> {
13791370
/// # Ok(())
13801371
/// # }
13811372
/// ```
1382-
#[expect(clippy::unused_async, clippy::future_not_send)]
1373+
#[expect(
1374+
clippy::unused_async,
1375+
reason = "for consistency with other Bootstrapper::boot methods"
1376+
)]
13831377
pub async fn boot(self) -> cot::Result<Bootstrapper<Initialized>> {
13841378
let router_service = RouterService::new(Arc::clone(&self.context.router));
13851379
let handler_builder = RootHandlerBuilder {
@@ -2057,10 +2051,6 @@ impl<S: BootstrapPhase<Database = Option<Database>>> ProjectContext<S> {
20572051
/// # Errors
20582052
///
20592053
/// This function returns an error if the server fails to start.
2060-
#[expect(
2061-
clippy::future_not_send,
2062-
reason = "Send not needed; Bootstrapper/CLI is run async in a single thread"
2063-
)]
20642054
pub async fn run(bootstrapper: Bootstrapper<Initialized>, address_str: &str) -> cot::Result<()> {
20652055
let listener = tokio::net::TcpListener::bind(address_str)
20662056
.await
@@ -2082,10 +2072,6 @@ pub async fn run(bootstrapper: Bootstrapper<Initialized>, address_str: &str) ->
20822072
/// # Errors
20832073
///
20842074
/// This function returns an error if the server fails to start.
2085-
#[expect(
2086-
clippy::future_not_send,
2087-
reason = "Send not needed; Bootstrapper/CLI is run async in a single thread"
2088-
)]
20892075
pub async fn run_at(
20902076
bootstrapper: Bootstrapper<Initialized>,
20912077
listener: tokio::net::TcpListener,
@@ -2106,10 +2092,6 @@ pub async fn run_at(
21062092
/// # Errors
21072093
///
21082094
/// This function returns an error if the server fails to start.
2109-
#[expect(
2110-
clippy::future_not_send,
2111-
reason = "Send not needed; Bootstrapper/CLI is run async in a single thread"
2112-
)]
21132095
pub async fn run_at_with_shutdown(
21142096
bootstrapper: Bootstrapper<Initialized>,
21152097
listener: tokio::net::TcpListener,
@@ -2326,7 +2308,7 @@ pub(crate) fn prepare_request_for_error_handler(request_head: &mut RequestHead,
23262308
/// # }
23272309
/// ```
23282310
#[expect(clippy::future_not_send)] // Send not needed; CLI is run async in a single thread
2329-
pub async fn run_cli(project: impl Project + 'static) -> cot::Result<()> {
2311+
pub async fn run_cli(project: impl Project + Send + 'static) -> cot::Result<()> {
23302312
Bootstrapper::new(project).run_cli().await
23312313
}
23322314

cot/src/test.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,9 @@ impl Client {
9292
/// }
9393
/// ```
9494
#[must_use]
95-
#[expect(clippy::future_not_send)] // used in the test code
9695
pub async fn new<P>(project: P) -> Self
9796
where
98-
P: Project + 'static,
97+
P: Project + Send + 'static,
9998
{
10099
let config = project.config("test").expect("Could not get test config");
101100
let bootstrapper = Bootstrapper::new(project)
@@ -1351,7 +1350,7 @@ pub struct TestServerBuilder<T> {
13511350
project: T,
13521351
}
13531352

1354-
impl<T: Project + 'static> TestServerBuilder<T> {
1353+
impl<T: Project + Send + 'static> TestServerBuilder<T> {
13551354
/// Create a new test server.
13561355
///
13571356
/// # Examples
@@ -1444,7 +1443,7 @@ pub struct TestServer<T> {
14441443
project: PhantomData<fn() -> T>,
14451444
}
14461445

1447-
impl<T: Project + 'static> TestServer<T> {
1446+
impl<T: Project + Send + 'static> TestServer<T> {
14481447
async fn start(project: T) -> Self {
14491448
let tcp_listener = TcpListener::bind("0.0.0.0:0")
14501449
.await

0 commit comments

Comments
 (0)