use std::sync::Arc;
use rtb_assets::Assets;
use rtb_config::Config;
use rtb_credentials::CredentialRef;
use tokio_util::sync::CancellationToken;
use crate::credentials::{list_or_empty, CredentialProvider};
use crate::metadata::ToolMetadata;
use crate::typed_config::{erase, ErasedConfig, TypedConfigOps};
use crate::version::VersionInfo;
#[derive(Clone)]
pub struct App {
pub metadata: Arc<ToolMetadata>,
pub version: Arc<VersionInfo>,
pub(crate) config: ErasedConfig,
pub assets: Arc<Assets>,
pub shutdown: CancellationToken,
pub credentials_provider: Option<Arc<dyn CredentialProvider>>,
pub(crate) typed_config_ops: Option<Arc<TypedConfigOps>>,
}
impl App {
#[must_use]
pub fn new<C>(
metadata: ToolMetadata,
version: VersionInfo,
config: Config<C>,
assets: Assets,
credentials_provider: Option<Arc<dyn CredentialProvider>>,
) -> Self
where
C: serde::de::DeserializeOwned + Send + Sync + 'static,
{
Self {
metadata: Arc::new(metadata),
version: Arc::new(version),
config: erase(config),
assets: Arc::new(assets),
shutdown: CancellationToken::new(),
credentials_provider,
typed_config_ops: None,
}
}
#[must_use]
pub fn with_typed_config(mut self, erased: ErasedConfig, ops: Arc<TypedConfigOps>) -> Self {
self.config = erased;
self.typed_config_ops = Some(ops);
self
}
#[doc(hidden)]
#[must_use]
pub fn for_testing(metadata: ToolMetadata, version: VersionInfo) -> Self {
Self::new(metadata, version, Config::<()>::default(), Assets::default(), None)
}
#[must_use]
pub fn credentials(&self) -> Vec<(String, CredentialRef)> {
list_or_empty(self.credentials_provider.as_ref())
}
#[must_use]
pub fn typed_config<C>(&self) -> Option<Arc<Config<C>>>
where
C: serde::de::DeserializeOwned + Send + Sync + 'static,
{
Arc::clone(&self.config).downcast::<Config<C>>().ok()
}
#[must_use]
#[track_caller]
pub fn config_as<C>(&self) -> Arc<Config<C>>
where
C: serde::de::DeserializeOwned + Send + Sync + 'static,
{
self.typed_config::<C>().unwrap_or_else(|| {
panic!(
"App::config_as::<{}>() — no matching typed config wired \
(did `Application::builder().config(...)` get called \
with the right type?)",
std::any::type_name::<C>(),
)
})
}
#[must_use]
pub fn config_schema(&self) -> Option<&serde_json::Value> {
self.typed_config_ops.as_ref().map(|ops| &ops.schema)
}
#[must_use]
pub fn config_value(&self) -> Option<serde_json::Value> {
self.typed_config_ops.as_ref().and_then(|ops| ops.render(&self.config))
}
}