use std::marker::PhantomData;
use std::sync::{Arc, OnceLock};
use crate::core::exceptions::OperonError;
use crate::core::registry::ResourceHub;
use crate::core::tracing::trace_filter::TraceFilter;
pub struct ConfigurableInner<Cfg, Client> {
pub config: Option<Cfg>,
pub resource: Option<String>,
pub tags: Vec<String>,
pub trace_filter: Option<TraceFilter>,
pub client: OnceLock<Arc<Client>>,
_phantom: PhantomData<Client>,
}
impl<Cfg, Client> std::fmt::Debug for ConfigurableInner<Cfg, Client>
where
Cfg: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ConfigurableInner")
.field("config", &self.config.as_ref().map(|_| "<set>"))
.field("resource", &self.resource)
.field("tags", &self.tags)
.field("has_client", &self.client.get().is_some())
.finish()
}
}
impl<Cfg, Client> ConfigurableInner<Cfg, Client> {
pub fn new(
config: Option<Cfg>,
resource: Option<String>,
tags: Vec<String>,
trace_filter: Option<TraceFilter>,
) -> Result<Self, OperonError> {
match (&config, &resource) {
(None, None) => Err(OperonError::Config(
"tracer must be constructed with either `config` or `resource`".into(),
)),
(Some(_), Some(_)) => Err(OperonError::Config(
"tracer cannot be constructed with both `config` and `resource`".into(),
)),
_ => Ok(Self {
config,
resource,
tags,
trace_filter,
client: OnceLock::new(),
_phantom: PhantomData,
}),
}
}
pub fn get_or_init_client<F>(&self, make_client: F) -> Result<Arc<Client>, OperonError>
where
F: FnOnce(&Cfg) -> Result<Client, OperonError>,
Client: 'static,
{
if let Some(c) = self.client.get() {
return Ok(c.clone());
}
let built = if let Some(cfg) = &self.config {
Arc::new(make_client(cfg)?)
} else {
return Err(OperonError::ResourceHub(format!(
"{}: resource-based tracer construction is supported but the concrete client downcast \
is resolved in each tracer's flush path (Phase 7b)",
std::any::type_name::<Client>()
)));
};
Ok(self.client.get_or_init(|| built).clone())
}
pub fn resolve_resource_client(
&self,
category_prefix: &str,
) -> Result<Arc<dyn std::any::Any + Send + Sync>, OperonError>
where
Client: 'static,
{
let resource = self
.resource
.as_deref()
.ok_or_else(|| OperonError::Config("no resource key set".into()))?;
let full_key = if resource.starts_with(&format!("{}:", category_prefix)) {
resource.to_string()
} else {
format!("{}:{}", category_prefix, resource)
};
ResourceHub::instance()?.get(&full_key)
}
}