Skip to content

Commit

Permalink
feat: Add more builder methods for RoadsterApp (#370)
Browse files Browse the repository at this point in the history
Allow providing most fields of the `RoadsterApp` directly instead of in
provider callback methods. This can be useful if the consumer doesn't
need the app context/state to build an item and they don't want to deal
with creating a callback lambda/method.

The `app-builder` example was updated to use all the new methods.
  • Loading branch information
spencewenski authored Sep 15, 2024
1 parent c07f57f commit c134d6e
Show file tree
Hide file tree
Showing 8 changed files with 473 additions and 86 deletions.
14 changes: 12 additions & 2 deletions examples/app-builder/src/health_check/example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,22 @@ use roadster::error::RoadsterResult;
use roadster::health_check::{CheckResponse, HealthCheck, Status};
use std::time::Duration;

pub struct ExampleHealthCheck;
pub struct ExampleHealthCheck {
pub name: String,
}

impl ExampleHealthCheck {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
}
}
}

#[async_trait]
impl HealthCheck for ExampleHealthCheck {
fn name(&self) -> String {
"example".to_string()
self.name.clone()
}

fn enabled(&self) -> bool {
Expand Down
14 changes: 12 additions & 2 deletions examples/app-builder/src/lifecycle/example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,20 @@ use crate::app_state::AppState;
use crate::App;
use roadster::lifecycle::AppLifecycleHandler;

pub struct ExampleLifecycleHandler;
pub struct ExampleLifecycleHandler {
name: String,
}

impl ExampleLifecycleHandler {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
}
}
}

impl AppLifecycleHandler<App, AppState> for ExampleLifecycleHandler {
fn name(&self) -> String {
"example".to_string()
self.name.clone()
}
}
103 changes: 74 additions & 29 deletions examples/app-builder/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,80 @@ use app_builder::App;
use roadster::app::metadata::AppMetadata;
use roadster::app::RoadsterApp;
use roadster::error::RoadsterResult;
use roadster::service::function::service::FunctionService;
use roadster::service::http::service::HttpService;
use roadster::service::worker::sidekiq::app_worker::AppWorker;
use roadster::service::worker::sidekiq::service::SidekiqWorkerService;
use std::future;
use tokio_util::sync::CancellationToken;

const BASE: &str = "/api";

fn metadata() -> AppMetadata {
AppMetadata::builder()
.version(env!("VERGEN_GIT_SHA").to_string())
.build()
}

#[tokio::main]
async fn main() -> RoadsterResult<()> {
let custom_state = "custom".to_string();

let builder = RoadsterApp::builder()
.tracing_initializer(|config| roadster::tracing::init_tracing(config, &metadata()))
.tracing_initializer(|config| roadster::tracing::init_tracing(config, &metadata()));

// Metadata can either be provided directly or via a provider callback. Note that the two
// approaches are mutually exclusive, with the `metadata` method taking priority.
let builder = builder
.metadata(metadata())
.metadata_provider(move |_config| Ok(metadata()));

// Db connection options can either be provided directly or via a provider callback. Note that
// the two approaches are mutually exclusive, with the `db_conn_options` method taking priority.
#[cfg(feature = "db-sql")]
let builder = builder
.db_conn_options_provider(|config| Ok(sea_orm::ConnectOptions::from(&config.database)));
let builder = {
let mut db_conn_options =
sea_orm::ConnectOptions::new("postgres://roadster:roadster@localhost:5432/example_dev");
db_conn_options.connect_lazy(true);
builder
.db_conn_options(db_conn_options)
.db_conn_options_provider(|config| Ok(sea_orm::ConnectOptions::from(&config.database)))
};

let app: App = builder
.state_provider(move |app_context| {
Ok(AppState {
app_context,
custom_state: custom_state.clone(),
})
// Provide your custom state via the `state_provider` method.
let builder = builder.state_provider(move |app_context| {
Ok(AppState {
app_context,
custom_state: custom_state.clone(),
})
.lifecycle_handler_provider(|registry, _state| {
registry.register(ExampleLifecycleHandler)?;
});

// Lifecycle handlers can either be provided directly or via a provider callback. Each can be
// called multiple times to register multiple handlers (however, registering duplicate handlers
// will cause an error).
let builder = builder
.add_lifecycle_handler(ExampleLifecycleHandler::new("example1"))
.add_lifecycle_handler_provider(|registry, _state| {
registry.register(ExampleLifecycleHandler::new("example2"))?;
Ok(())
})
.health_check_provider(|registry, _state| {
registry.register(ExampleHealthCheck)?;
});

// Health checks can either be provided directly or via a provider callback. Each can be called
// multiple times to register multiple health checks (however, registering duplicate checks
// will cause an error).
let builder = builder
.add_health_check(ExampleHealthCheck::new("example1"))
.add_health_check(ExampleHealthCheck::new("example2"))
.add_health_check_provider(|registry, _state| {
registry.register(ExampleHealthCheck::new("example3"))?;
Ok(())
})
.service_provider(|registry, state| {
});

// Services can either be provided directly or via a provider callback. Each can be called
// multiple times to register multiple services (however, registering duplicate services
// will cause an error).
let builder = builder
.add_service(
FunctionService::builder()
.name("example".to_string())
.function(example_fn_service)
.build(),
)
.add_service_provider(|registry, state| {
Box::pin(async {
registry
.register_builder(
Expand All @@ -57,7 +90,7 @@ async fn main() -> RoadsterResult<()> {
Ok(())
})
})
.service_provider(|registry, state| {
.add_service_provider(|registry, state| {
Box::pin(async {
registry
.register_builder(
Expand All @@ -68,13 +101,25 @@ async fn main() -> RoadsterResult<()> {
.await?;
Ok(())
})
});

let builder = builder.graceful_shutdown_signal_provider(|_state| {
Box::pin(async {
let _output: () = future::pending().await;
})
.provide_graceful_shutdown_signal(|_state| {
Box::pin(async {
let _output: () = future::pending().await;
})
})
.build();
});

let app: App = builder.build();

app.run().await
}

fn metadata() -> AppMetadata {
AppMetadata::builder()
.version(env!("VERGEN_GIT_SHA").to_string())
.build()
}

async fn example_fn_service(_state: AppState, _token: CancellationToken) -> RoadsterResult<()> {
Ok(())
}
6 changes: 6 additions & 0 deletions src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ mod roadster_app;

/// A default implementation of [`App`] that is customizable via a builder-style API.
///
/// See <https://github.com/roadster-rs/roadster/tree/main/examples/app-builder/src/main.rs> for
/// an example of how to use the [`RoadsterApp`].
///
/// The `Cli` and `M` type parameters are only required when the `cli` and `db-sql` features are
/// enabled, respectively.
pub use roadster_app::RoadsterApp;

/// Builder-style API to build/customize a [`RoadsterApp`].
///
/// See <https://github.com/roadster-rs/roadster/tree/main/examples/app-builder/src/main.rs> for
/// an example of how to use the [`RoadsterAppBuilder`].
///
/// The `Cli` and `M` type parameters are only required when the `cli` and `db-sql` features are
/// enabled, respectively.
pub use roadster_app::RoadsterAppBuilder;
Expand Down
Loading

0 comments on commit c134d6e

Please sign in to comment.