Skip to main content

systemprompt_traits/
context.rs

1//! Application context, module registry, and request-context propagation.
2//!
3//! The traits in this module are the runtime entry points other crates use
4//! to discover configuration, the database handle, and the registered
5//! providers (analytics, fingerprint, user). [`ContextPropagation`] models
6//! how request-scoped state moves across HTTP boundaries.
7
8use async_trait::async_trait;
9use std::sync::Arc;
10
11use crate::analytics::{AnalyticsProvider, FingerprintProvider};
12use crate::auth::UserProvider;
13
14pub trait AppContext: Send + Sync {
15    fn config(&self) -> Arc<dyn ConfigProvider>;
16    fn database_handle(&self) -> Arc<dyn DatabaseHandle>;
17    fn analytics_provider(&self) -> Option<Arc<dyn AnalyticsProvider>>;
18    fn fingerprint_provider(&self) -> Option<Arc<dyn FingerprintProvider>>;
19    fn user_provider(&self) -> Option<Arc<dyn UserProvider>>;
20}
21
22pub trait InjectContextHeaders {
23    fn inject_headers(&self, headers: &mut http::HeaderMap);
24}
25
26pub type ContextPropagationResult<T> = Result<T, ContextPropagationError>;
27
28#[derive(Debug, thiserror::Error)]
29#[non_exhaustive]
30pub enum ContextPropagationError {
31    #[error("missing header: {0}")]
32    MissingHeader(String),
33
34    #[error("invalid header {name}: {message}")]
35    InvalidHeader { name: String, message: String },
36
37    #[error("invalid context: {0}")]
38    Invalid(String),
39}
40
41pub trait ContextPropagation {
42    fn from_headers(headers: &http::HeaderMap) -> ContextPropagationResult<Self>
43    where
44        Self: Sized;
45
46    fn to_headers(&self) -> http::HeaderMap;
47}
48
49pub trait ConfigProvider: Send + Sync {
50    fn get(&self, key: &str) -> Option<String>;
51    fn database_url(&self) -> &str;
52    fn database_write_url(&self) -> Option<&str> {
53        None
54    }
55    fn system_path(&self) -> &str;
56    fn api_port(&self) -> u16;
57    fn as_any(&self) -> &dyn std::any::Any;
58}
59
60pub trait ModuleRegistry: Send + Sync {
61    fn get_module(&self, name: &str) -> Option<Arc<dyn Module>>;
62    fn list_modules(&self) -> Vec<String>;
63}
64
65pub trait DatabaseHandle: Send + Sync {
66    fn is_connected(&self) -> bool;
67    fn as_any(&self) -> &dyn std::any::Any;
68}
69
70#[async_trait]
71pub trait Module: Send + Sync {
72    fn name(&self) -> &str;
73    fn version(&self) -> &str;
74    fn display_name(&self) -> &str;
75    async fn initialize(&self) -> Result<(), Box<dyn std::error::Error>>;
76}
77
78#[cfg(feature = "web")]
79#[async_trait]
80pub trait ApiModule: Module {
81    fn router(&self, ctx: Arc<dyn AppContext>) -> axum::Router;
82}