diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..0f4d584 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,43 @@ +name: Check + +on: + pull_request: + push: + branches: main + +env: + CARGO_TERM_COLOR: always + +jobs: + formatting: + runs-on: "ubuntu-latest" + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: "stable" + components: rustfmt + - run: cargo fmt --check + + check: + runs-on: "ubuntu-latest" + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: "stable" + - uses: taiki-e/install-action@cargo-hack + - uses: Swatinem/rust-cache@v2 + - run: cargo hack check --feature-powerset + + test: + runs-on: "ubuntu-latest" + needs: check + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: "stable" + - uses: taiki-e/install-action@cargo-hack + - uses: Swatinem/rust-cache@v2 + - run: cargo hack test --feature-powerset --skip debug_trace diff --git a/Cargo.toml b/Cargo.toml index ab597f7..03382a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,11 @@ version = "0.1.0" edition = "2021" [dependencies] -async-trait = "0.1.71" -diesel-async = { version = "0.4.1", features = ["postgres", "deadpool"] } -serde = { version = "1.0.171", features = ["derive"] } -shuttle-service = { version = "0.36.0", default-features = false } +async-trait = "0.1.77" +diesel-async = { version = "0.4.1", features = ["postgres"] } +serde = { version = "1.0.197", features = ["derive"] } +shuttle-service = { version = "0.39.0", default-features = false } + +[features] +bb8 = ["diesel-async/bb8"] +deadpool = ["diesel-async/deadpool"] diff --git a/README.md b/README.md index df81891..44f573e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # shuttle-diesel-async -`diesel-async` resource for Shuttle services exporting a compatible crate for migrations. +`diesel-async` resource for Shuttle services ## License diff --git a/src/lib.rs b/src/lib.rs index e14a2d1..93acb33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,21 +1,32 @@ use async_trait::async_trait; -use diesel_async::{ - pooled_connection::{deadpool::Pool, AsyncDieselConnectionManager}, - AsyncPgConnection, -}; -use serde::Serialize; +use diesel_async::{AsyncConnection, AsyncPgConnection}; +use serde::{Deserialize, Serialize}; use shuttle_service::{ database::{SharedEngine, Type as DatabaseType}, - DbInput, DbOutput, Factory, ResourceBuilder, Type, + resource::Type, + DatabaseResource, DbInput, Factory, IntoResource, ResourceBuilder, }; +#[cfg(any(feature = "bb8", feature = "deadpool"))] +use diesel_async::pooled_connection::AsyncDieselConnectionManager; + +#[cfg(feature = "bb8")] +use diesel_async::pooled_connection::bb8; + +#[cfg(feature = "deadpool")] +use diesel_async::pooled_connection::deadpool; + pub use diesel_async; +#[cfg(any(feature = "bb8", feature = "deadpool"))] const MAX_POOL_SIZE: usize = 5; -#[derive(Default, Serialize)] +#[derive(Deserialize, Serialize)] +#[serde(transparent)] +pub struct Wrapper(DatabaseResource); + +#[derive(Default)] pub struct Postgres { - #[serde(flatten)] db_input: DbInput, } @@ -29,52 +40,80 @@ impl Postgres { } } -fn get_connection_string(db_output: &DbOutput) -> String { +#[inline] +#[cfg(any(feature = "bb8", feature = "deadpool"))] +fn get_pool_manager( + db_output: &DatabaseResource, +) -> AsyncDieselConnectionManager { + AsyncDieselConnectionManager::new(get_connection_string(db_output)) +} + +#[inline] +fn get_connection_string(db_output: &DatabaseResource) -> String { match db_output { - DbOutput::Info(ref info) => info.connection_string_private(), - DbOutput::Local(ref local) => local.clone(), + DatabaseResource::ConnectionString(conn_str) => conn_str.clone(), + DatabaseResource::Info(info) => info.connection_string_shuttle(), } } #[async_trait] -impl ResourceBuilder> for Postgres { +impl ResourceBuilder for Postgres { const TYPE: Type = Type::Database(DatabaseType::Shared(SharedEngine::Postgres)); - type Config = Self; - type Output = DbOutput; - - fn new() -> Self { - Self::default() - } + type Config = DbInput; + type Output = Wrapper; fn config(&self) -> &Self::Config { - self + &self.db_input } async fn output( self, factory: &mut dyn Factory, ) -> Result { - let db_output = if let Some(local_uri) = self.db_input.local_uri { - DbOutput::Local(local_uri) + let resource = if let Some(local_uri) = self.db_input.local_uri { + DatabaseResource::ConnectionString(local_uri) } else { - let conn_data = factory + let conn_info = factory .get_db_connection(DatabaseType::Shared(SharedEngine::Postgres)) .await?; - DbOutput::Info(conn_data) + DatabaseResource::Info(conn_info) }; - Ok(db_output) + Ok(Wrapper(resource)) + } +} + +#[async_trait] +impl IntoResource for Wrapper { + async fn into_resource(self) -> Result { + AsyncPgConnection::establish(&get_connection_string(&self.0)) + .await + .map_err(|err| shuttle_service::Error::Database(err.to_string())) + } +} + +#[async_trait] +#[cfg(feature = "bb8")] +impl IntoResource> for Wrapper { + async fn into_resource(self) -> Result, shuttle_service::Error> { + bb8::Pool::builder() + .max_size(MAX_POOL_SIZE as u32) + .build(get_pool_manager(&self.0)) + .await + .map_err(|err| shuttle_service::Error::Database(err.to_string())) } +} - async fn build( - db_output: &Self::Output, - ) -> Result, shuttle_service::Error> { - let conn_string = get_connection_string(db_output); - let config = AsyncDieselConnectionManager::new(conn_string); - Pool::builder(config) +#[async_trait] +#[cfg(feature = "deadpool")] +impl IntoResource> for Wrapper { + async fn into_resource( + self, + ) -> Result, shuttle_service::Error> { + deadpool::Pool::builder(get_pool_manager(&self.0)) .max_size(MAX_POOL_SIZE) .build() - .map_err(|err| shuttle_service::Error::Custom(err.into())) + .map_err(|err| shuttle_service::Error::Database(err.to_string())) } }