khora_io/asset/
registry.rs1use super::{AssetDecoder, AssetService};
18use anyhow::{anyhow, Result};
19use khora_core::asset::Asset;
20use khora_telemetry::{
21 metrics::registry::{CounterHandle, HistogramHandle},
22 MetricsRegistry, ScopedMetricTimer,
23};
24use std::{any::Any, collections::HashMap, sync::Arc};
25
26pub struct DecoderRegistration {
49 pub type_name: &'static str,
53 pub register: fn(&mut AssetService),
57}
58
59inventory::collect!(DecoderRegistration);
60
61trait AnyDecoder: Send + Sync {
62 fn decode_any(&self, bytes: &[u8], metrics: &DecoderMetrics) -> Result<Box<dyn Any + Send>>;
63}
64
65struct DecoderWrapper<A: Asset, L: AssetDecoder<A>>(L, std::marker::PhantomData<A>);
66
67impl<A: Asset, L: AssetDecoder<A> + Send + Sync> AnyDecoder for DecoderWrapper<A, L> {
68 fn decode_any(&self, bytes: &[u8], metrics: &DecoderMetrics) -> Result<Box<dyn Any + Send>> {
69 let _timer = ScopedMetricTimer::new(&metrics.decode_time_ms);
70 let asset: A = self.0.load(bytes).map_err(|e| anyhow!(e.to_string()))?;
71 metrics.assets_decoded_total.increment()?;
72 Ok(Box::new(asset))
73 }
74}
75
76struct DecoderMetrics {
77 decode_time_ms: HistogramHandle,
78 assets_decoded_total: CounterHandle,
79}
80
81impl DecoderMetrics {
82 fn new(registry: &MetricsRegistry) -> Self {
83 Self {
84 decode_time_ms: registry
85 .register_histogram(
86 "assets",
87 "decode_time",
88 "Asset decoding time",
89 "ms",
90 vec![1.0, 5.0, 16.0, 33.0, 100.0, 500.0],
91 )
92 .expect("Failed to register asset decode time metric"),
93 assets_decoded_total: registry
94 .register_counter("assets", "decoded_total", "Total number of assets decoded")
95 .expect("Failed to register asset count metric"),
96 }
97 }
98}
99
100pub struct DecoderRegistry {
102 metrics: DecoderMetrics,
103 decoders: HashMap<String, Box<dyn AnyDecoder>>,
104}
105
106impl DecoderRegistry {
107 pub fn new(metrics_registry: Arc<MetricsRegistry>) -> Self {
109 Self {
110 decoders: HashMap::new(),
111 metrics: DecoderMetrics::new(&metrics_registry),
112 }
113 }
114
115 pub fn register<A: Asset>(
117 &mut self,
118 type_name: &str,
119 decoder: impl AssetDecoder<A> + Send + Sync + 'static,
120 ) {
121 let wrapped = DecoderWrapper(decoder, std::marker::PhantomData);
122 self.decoders
123 .insert(type_name.to_string(), Box::new(wrapped));
124 }
125
126 pub fn decode<A: Asset>(&self, type_name: &str, bytes: &[u8]) -> Result<A> {
128 let decoder = self
129 .decoders
130 .get(type_name)
131 .ok_or_else(|| anyhow!("No decoder registered for asset type '{}'", type_name))?;
132
133 let asset_any = decoder.decode_any(bytes, &self.metrics)?;
134 let asset_boxed = asset_any.downcast::<A>().map_err(|_| {
135 anyhow!(
136 "Decoder for type '{}' returned a different asset type than requested.",
137 type_name
138 )
139 })?;
140 Ok(*asset_boxed)
141 }
142}