use std::sync::{LazyLock, RwLock, RwLockReadGuard, RwLockWriteGuard};
use serde::{Deserialize, Serialize};
use crate::array::ArrayMetadataOptions;
use crate::group::GroupMetadataOptions;
use zarrs_codec::{CodecMetadataOptions, CodecOptions};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(clippy::struct_excessive_bools)]
pub struct Config {
validate_checksums: bool,
store_empty_chunks: bool,
codec_concurrent_target: usize,
chunk_concurrent_minimum: usize,
codec_store_metadata_if_encode_only: bool,
metadata_convert_version: MetadataConvertVersion,
metadata_erase_version: MetadataEraseVersion,
include_zarrs_metadata: bool,
experimental_partial_encoding: bool,
convert_aliased_extension_names: bool,
}
#[allow(clippy::derivable_impls)]
impl Default for Config {
fn default() -> Self {
Self {
validate_checksums: true,
store_empty_chunks: false,
codec_concurrent_target: rayon::current_num_threads(),
chunk_concurrent_minimum: 4,
codec_store_metadata_if_encode_only: true,
metadata_convert_version: MetadataConvertVersion::default(),
metadata_erase_version: MetadataEraseVersion::default(),
include_zarrs_metadata: true,
experimental_partial_encoding: false,
convert_aliased_extension_names: false,
}
}
}
impl Config {
#[must_use]
pub fn codec_options(&self) -> CodecOptions {
CodecOptions::default()
.with_validate_checksums(self.validate_checksums)
.with_store_empty_chunks(self.store_empty_chunks)
.with_concurrent_target(self.codec_concurrent_target)
.with_chunk_concurrent_minimum(self.chunk_concurrent_minimum)
.with_experimental_partial_encoding(self.experimental_partial_encoding)
}
#[must_use]
pub fn codec_metadata_options(&self) -> CodecMetadataOptions {
CodecMetadataOptions::default()
.with_codec_store_metadata_if_encode_only(self.codec_store_metadata_if_encode_only)
}
#[must_use]
pub fn group_metadata_options(&self) -> crate::group::GroupMetadataOptions {
GroupMetadataOptions::default().with_metadata_convert_version(self.metadata_convert_version)
}
#[must_use]
pub fn array_metadata_options(&self) -> ArrayMetadataOptions {
ArrayMetadataOptions::default()
.with_codec_metadata_options(self.codec_metadata_options())
.with_metadata_convert_version(self.metadata_convert_version)
.with_include_zarrs_metadata(self.include_zarrs_metadata)
.with_convert_aliased_extension_names(self.convert_aliased_extension_names)
}
#[must_use]
pub fn validate_checksums(&self) -> bool {
self.validate_checksums
}
pub fn set_validate_checksums(&mut self, validate_checksums: bool) -> &mut Self {
self.validate_checksums = validate_checksums;
self
}
#[must_use]
pub fn store_empty_chunks(&self) -> bool {
self.store_empty_chunks
}
pub fn set_store_empty_chunks(&mut self, store_empty_chunks: bool) -> &mut Self {
self.store_empty_chunks = store_empty_chunks;
self
}
#[must_use]
pub fn codec_concurrent_target(&self) -> usize {
self.codec_concurrent_target
}
pub fn set_codec_concurrent_target(&mut self, concurrent_target: usize) -> &mut Self {
self.codec_concurrent_target = concurrent_target;
self
}
#[must_use]
pub fn chunk_concurrent_minimum(&self) -> usize {
self.chunk_concurrent_minimum
}
pub fn set_chunk_concurrent_minimum(&mut self, concurrent_minimum: usize) -> &mut Self {
self.chunk_concurrent_minimum = concurrent_minimum;
self
}
#[must_use]
pub fn codec_store_metadata_if_encode_only(&self) -> bool {
self.codec_store_metadata_if_encode_only
}
pub fn set_codec_store_metadata_if_encode_only(&mut self, enabled: bool) -> &mut Self {
self.codec_store_metadata_if_encode_only = enabled;
self
}
#[must_use]
pub fn metadata_convert_version(&self) -> MetadataConvertVersion {
self.metadata_convert_version
}
pub fn set_metadata_convert_version(&mut self, version: MetadataConvertVersion) -> &mut Self {
self.metadata_convert_version = version;
self
}
#[must_use]
pub fn metadata_erase_version(&self) -> MetadataEraseVersion {
self.metadata_erase_version
}
pub fn set_metadata_erase_version(&mut self, version: MetadataEraseVersion) -> &mut Self {
self.metadata_erase_version = version;
self
}
#[must_use]
pub fn include_zarrs_metadata(&self) -> bool {
self.include_zarrs_metadata
}
pub fn set_include_zarrs_metadata(&mut self, include_zarrs_metadata: bool) -> &mut Self {
self.include_zarrs_metadata = include_zarrs_metadata;
self
}
#[must_use]
pub fn experimental_partial_encoding(&self) -> bool {
self.experimental_partial_encoding
}
pub fn set_experimental_partial_encoding(
&mut self,
experimental_partial_encoding: bool,
) -> &mut Self {
self.experimental_partial_encoding = experimental_partial_encoding;
self
}
#[must_use]
pub fn convert_aliased_extension_names(&self) -> bool {
self.convert_aliased_extension_names
}
pub fn set_convert_aliased_extension_names(
&mut self,
convert_aliased_extension_names: bool,
) -> &mut Self {
self.convert_aliased_extension_names = convert_aliased_extension_names;
self
}
}
static CONFIG: LazyLock<RwLock<Config>> = LazyLock::new(|| RwLock::new(Config::default()));
pub fn global_config() -> RwLockReadGuard<'static, Config> {
CONFIG.read().unwrap()
}
pub fn global_config_mut() -> RwLockWriteGuard<'static, Config> {
CONFIG.write().unwrap()
}
pub enum MetadataRetrieveVersion {
Default,
V3,
V2,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
pub enum MetadataConvertVersion {
#[default]
Default,
V3,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
pub enum MetadataEraseVersion {
#[default]
Default,
All,
V3,
V2,
}
#[cfg(test)]
mod tests {
use serial_test::serial;
use super::*;
#[ignore]
#[test]
#[serial]
fn config_validate_checksums() {
*global_config_mut() = Config::default();
assert!(global_config().validate_checksums());
global_config_mut().set_validate_checksums(false);
assert!(!global_config().validate_checksums());
global_config_mut().set_validate_checksums(true);
*global_config_mut() = Config::default();
}
#[ignore]
#[test]
#[serial]
fn config_serialize_deserialize_update() {
*global_config_mut() = Config::default();
global_config_mut().set_validate_checksums(false);
let serialized = serde_json::to_string(&*global_config()).unwrap();
global_config_mut().set_validate_checksums(true);
assert!(global_config().validate_checksums());
let restored_config: Config = serde_json::from_str(&serialized).unwrap();
assert!(!restored_config.validate_checksums());
*global_config_mut() = restored_config;
assert!(!global_config().validate_checksums());
*global_config_mut() = Config::default();
}
}