From fd7903ba0b58328652fe9f0ebced96b7d0a63c44 Mon Sep 17 00:00:00 2001 From: Spencer Ferris <3319370+spencewenski@users.noreply.github.com> Date: Fri, 31 Jan 2025 00:18:06 -0800 Subject: [PATCH] docs: Add to `AppContext` book docs --- book/examples/app-context/Cargo.toml | 4 +- book/examples/app-context/src/app.rs | 57 ++++++++++++++++++++++++++ book/examples/app-context/src/lib.rs | 19 +-------- book/examples/app-context/src/state.rs | 8 ++++ book/src/features/app-context.md | 24 ++++++++++- 5 files changed, 93 insertions(+), 19 deletions(-) create mode 100644 book/examples/app-context/src/app.rs create mode 100644 book/examples/app-context/src/state.rs diff --git a/book/examples/app-context/Cargo.toml b/book/examples/app-context/Cargo.toml index 37a34da..eb29b40 100644 --- a/book/examples/app-context/Cargo.toml +++ b/book/examples/app-context/Cargo.toml @@ -6,4 +6,6 @@ publish = false [dependencies] roadster = { path = "../../.." } -axum = { workspace = true } \ No newline at end of file +axum = { workspace = true } +async-trait = { workspace = true } +anyhow = { workspace = true } \ No newline at end of file diff --git a/book/examples/app-context/src/app.rs b/book/examples/app-context/src/app.rs new file mode 100644 index 0000000..c874090 --- /dev/null +++ b/book/examples/app-context/src/app.rs @@ -0,0 +1,57 @@ +use crate::state::CustomState; +use anyhow::anyhow; +use async_trait::async_trait; +use axum::extract::FromRef; +use roadster::app::context::{AppContext, AppContextWeak}; +use roadster::app::RoadsterApp; +use roadster::error::RoadsterResult; +use roadster::health::check::{CheckResponse, HealthCheck, Status}; +use roadster::util::empty::Empty; +use std::time::Duration; + +pub type App = RoadsterApp; + +pub struct ExampleHealthCheck { + // Prevent reference cycle because the `ExampleHealthCheck` is also stored in the `AppContext` + context: AppContextWeak, +} + +#[async_trait] +impl HealthCheck for ExampleHealthCheck { + fn name(&self) -> String { + "example".to_string() + } + + fn enabled(&self) -> bool { + true + } + + async fn check(&self) -> RoadsterResult { + // Upgrade the `AppContext` in order to use it + let _context = self + .context + .upgrade() + .ok_or_else(|| anyhow!("Could not upgrade AppContextWeak"))?; + + Ok(CheckResponse::builder() + .status(Status::Ok) + .latency(Duration::from_secs(0)) + .build()) + } +} + +pub fn build_app() -> App { + RoadsterApp::builder() + .state_provider(|context| { + Ok(CustomState { + context, + custom_field: "Custom Field".to_string(), + }) + }) + .add_health_check_provider(|registry, state| { + // Downgrade the context before providing it to the `ExampleHealthCheck` + let context = AppContext::from_ref(state).downgrade(); + registry.register(ExampleHealthCheck { context }) + }) + .build() +} diff --git a/book/examples/app-context/src/lib.rs b/book/examples/app-context/src/lib.rs index 631b48d..1c16bbc 100644 --- a/book/examples/app-context/src/lib.rs +++ b/book/examples/app-context/src/lib.rs @@ -1,17 +1,2 @@ -use axum::extract::FromRef; -use roadster::app::context::AppContext; - -#[derive(FromRef)] -pub struct CustomState { - custom_field: String, - context: AppContext, -} - -impl CustomState { - pub fn new(custom_field: String, context: AppContext) -> Self { - Self { - custom_field, - context, - } - } -} +pub mod app; +pub mod state; diff --git a/book/examples/app-context/src/state.rs b/book/examples/app-context/src/state.rs new file mode 100644 index 0000000..091b97c --- /dev/null +++ b/book/examples/app-context/src/state.rs @@ -0,0 +1,8 @@ +use axum::extract::FromRef; +use roadster::app::context::AppContext; + +#[derive(Clone, FromRef)] +pub struct CustomState { + pub context: AppContext, + pub custom_field: String, +} diff --git a/book/src/features/app-context.md b/book/src/features/app-context.md index 32fe98d..ce8a19d 100644 --- a/book/src/features/app-context.md +++ b/book/src/features/app-context.md @@ -6,7 +6,29 @@ directly, or you can define your own state type that can be used by both Roadste implementing [FromRef](https://docs.rs/axum-core/latest/axum_core/extract/trait.FromRef.html). ```rust,ignore -{{#include ../../examples/app-context/src/lib.rs}} +{{#include ../../examples/app-context/src/state.rs}} +``` + +## `Provide` and `ProvideRef` + +🛠️ todo 🛠️ + +- https://docs.rs/roadster/latest/roadster/app/context/trait.Provide.html +- https://docs.rs/roadster/latest/roadster/app/context/trait.ProvideRef.html + +## Weak reference + +In some cases, it can be useful to have a weak reference to the `AppContext` state in order to prevent reference cycles +for things that are included in the `AppContext` but also need a reference to the `AppContext`. For example, the +`AppContext` keeps a reference to the `HealthCheck`s, and most `HealthCheck`s need to use the `AppContext`. + +To get a weak reference to the `AppContext`'s state, +use [AppContext#downgrade](https://docs.rs/roadster/latest/roadster/app/context/struct.AppContext.html#method.downgrade) +to get a new instance +of [AppContextWeak](https://docs.rs/roadster/latest/roadster/app/context/struct.AppContextWeak.html). + +```rust,ignore +{{#include ../../examples/app-context/src/app.rs:12:}} ``` ## Docs.rs links