use super::types::RuntimeConfig;
use alloc::sync::Arc;
#[cfg(feature = "std")]
use arc_swap::ArcSwap;
#[cfg(not(feature = "std"))]
use spin::RwLock;
pub struct RuntimeConfigHandle {
#[cfg(feature = "std")]
inner: ArcSwap<RuntimeConfig>,
#[cfg(not(feature = "std"))]
inner: RwLock<Arc<RuntimeConfig>>,
}
impl RuntimeConfigHandle {
#[must_use]
pub fn new(initial: RuntimeConfig) -> Self {
Self {
#[cfg(feature = "std")]
inner: ArcSwap::from_pointee(initial),
#[cfg(not(feature = "std"))]
inner: RwLock::new(Arc::new(initial)),
}
}
#[cfg(feature = "std")]
pub fn load(&self) -> arc_swap::Guard<Arc<RuntimeConfig>> {
self.inner.load()
}
#[cfg(not(feature = "std"))]
pub fn load(&self) -> spin::RwLockReadGuard<'_, Arc<RuntimeConfig>> {
self.inner.read()
}
#[must_use]
pub fn load_full(&self) -> Arc<RuntimeConfig> {
#[cfg(feature = "std")]
{
self.inner.load_full()
}
#[cfg(not(feature = "std"))]
{
self.inner.read().clone()
}
}
pub fn try_update<F>(&self, mutator: F) -> crate::Result<()>
where
F: FnOnce(&mut RuntimeConfig),
{
let current = self.load_full();
let mut next = (*current).clone();
mutator(&mut next);
let ecc_enabled = next.data_block_ecc() || next.kv_checksums_ecc() || next.manifest_ecc();
if ecc_enabled && !cfg!(feature = "page_ecc") {
return Err(crate::Error::PageEccUnsupported);
}
if ecc_enabled {
use crate::runtime_config::{EccGranularity, EccScheme};
if next.ecc_granularity != EccGranularity::Block {
return Err(crate::Error::FeatureUnsupported(
"ecc_granularity=Page (not yet wired; use Block)",
));
}
match next.ecc_scheme {
EccScheme::Xor { data_shards: 0 } => {
return Err(crate::Error::FeatureUnsupported(
"ecc_scheme=Xor data_shards=0",
));
}
EccScheme::ReedSolomon { data_shards: 0, .. } => {
return Err(crate::Error::FeatureUnsupported(
"ecc_scheme=ReedSolomon data_shards=0",
));
}
EccScheme::ReedSolomon { parity_shards, .. } if parity_shards < 2 => {
return Err(crate::Error::FeatureUnsupported(
"ecc_scheme=ReedSolomon needs >= 2 parity shards (use Xor for single parity)",
));
}
_ => {}
}
}
if matches!(
next.kv_checksum_compute_point,
crate::runtime_config::KvChecksumComputePoint::AtInsert
) {
if next.kv_checksum_algo.digest_size() != 4 {
return Err(crate::Error::FeatureUnsupported(
"kv_checksum_compute_point=AtInsert requires a 4-byte algorithm",
));
}
if !next.kv_checksum_algo.is_available() {
return Err(crate::Error::FeatureUnsupported(
"kv_checksum_compute_point=AtInsert requires a compiled-in algorithm",
));
}
}
#[cfg(feature = "std")]
{
self.inner.store(Arc::new(next));
}
#[cfg(not(feature = "std"))]
{
*self.inner.write() = Arc::new(next);
}
Ok(())
}
}
impl core::fmt::Debug for RuntimeConfigHandle {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RuntimeConfigHandle")
.field("inner", &*self.load())
.finish()
}
}
#[cfg(test)]
#[expect(
clippy::unwrap_used,
reason = "tests panic on the unhappy paths to surface failures loudly"
)]
mod tests;