use derive_more::{Display, From};
use serde::{Deserialize, Deserializer, Serialize};
use zarrs_metadata::ConfigurationSerialize;
#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug, Display, From)]
#[non_exhaustive]
#[serde(untagged)]
pub enum GDeflateCodecConfiguration {
V0(GDeflateCodecConfigurationV0),
}
impl ConfigurationSerialize for GDeflateCodecConfiguration {}
#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug, Display)]
#[serde(deny_unknown_fields)]
#[display("{}", serde_json::to_string(self).unwrap_or_default())]
pub struct GDeflateCodecConfigurationV0 {
pub level: GDeflateCompressionLevel,
}
#[derive(Serialize, Copy, Clone, Debug, Eq, PartialEq)]
pub struct GDeflateCompressionLevel(u32);
#[derive(Debug, thiserror::Error)]
#[error("Invalid gdeflate compression level {0}, must be 0-12")]
pub struct GDeflateCompressionLevelError(u32);
macro_rules! gdeflate_compression_level_try_from {
( $t:ty ) => {
impl TryFrom<$t> for GDeflateCompressionLevel {
type Error = GDeflateCompressionLevelError;
fn try_from(level: $t) -> Result<Self, Self::Error> {
let level = u32::from(level);
if level <= 12 {
Ok(Self(level))
} else {
Err(GDeflateCompressionLevelError(level))
}
}
}
};
}
gdeflate_compression_level_try_from!(u8);
gdeflate_compression_level_try_from!(u16);
gdeflate_compression_level_try_from!(u32);
impl<'de> Deserialize<'de> for GDeflateCompressionLevel {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let level = u32::deserialize(d)?;
if level <= 12 {
Ok(Self(level))
} else {
Err(serde::de::Error::custom(
"gdeflate compression level must be between 0 and 9",
))
}
}
}
impl GDeflateCompressionLevel {
pub fn new<N: num::Unsigned + std::cmp::PartialOrd<u32>>(
compression_level: N,
) -> Result<Self, GDeflateCompressionLevelError>
where
u32: From<N>,
{
let compression_level = u32::from(compression_level);
if compression_level <= 12 {
Ok(Self(compression_level))
} else {
Err(GDeflateCompressionLevelError(compression_level))
}
}
#[must_use]
#[allow(clippy::cast_possible_wrap)] pub fn as_i32(&self) -> i32 {
self.0 as i32
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn codec_gdeflate_valid1() {
let json = r#"
{
"level": 12
}"#;
assert!(serde_json::from_str::<GDeflateCodecConfiguration>(json).is_ok());
}
#[test]
fn codec_gdeflate_invalid_level() {
let json = r#"
{
"level": 13
}"#;
let codec_configuration = serde_json::from_str::<GDeflateCodecConfiguration>(json);
assert!(codec_configuration.is_err());
}
}