Repository: influxdata/influxdb
File: influxdb3_catalog/src/log/versions/v2.rs (Rust)

Code before:
    pub key_columns: Vec<ColumnId>,
    /// Columns that store values in the cache
    pub value_columns: LastCacheValueColumnsDef,
    /// The number of last values to hold in the cache
    pub count: LastCacheSize,
    /// The time-to-live (TTL) in seconds for entries in the cache
    pub ttl: LastCacheTtl,
}

/// A last cache will either store values for an explicit set of columns, or will accept all
/// non-key columns
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Default)]
#[serde(rename_all = "snake_case")]
pub enum LastCacheValueColumnsDef {
    /// Explicit list of column names
    Explicit { columns: Vec<ColumnId> },
    /// Stores all non-key columns
    #[default]
    AllNonKeyColumns,
}

/// The maximum allowed size for a last cache
pub const LAST_CACHE_MAX_SIZE: usize = 10;

/// The size of the last cache
///
/// Must be between 1 and [`LAST_CACHE_MAX_SIZE`]
#[derive(Debug, Serialize, Eq, PartialEq, Clone, Copy)]
pub struct LastCacheSize(pub(crate) usize);

impl LastCacheSize {
    pub fn new(size: usize) -> Result<Self> {
        if size == 0 || size > LAST_CACHE_MAX_SIZE {
            Err(CatalogError::InvalidLastCacheSize)
        } else {
            Ok(Self(size))
        }
    }
}

impl<'de> Deserialize<'de> for LastCacheSize {
    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let count = usize::deserialize(deserializer)?;
        Self::new(count).map_err(|e| serde::de::Error::custom(e.to_string()))
    }
}

impl Default for LastCacheSize {

Review comment (bug):
This is a textbook use case for [`NonZeroUsize`](https://doc.rust-lang.org/std/num/type.NonZeroUsize.html), i.e.,
```rust
/// Must be greater than 0
#[derive(Debug, Serialize, Eq, PartialEq, Clone, Copy)]
pub struct LastCacheSize(pub(crate) NonZeroUsize);

Code after:
    pub key_columns: Vec<ColumnId>,
    /// Columns that store values in the cache
    pub value_columns: LastCacheValueColumnsDef,
    /// The number of last values to hold in the cache
    pub count: LastCacheSize,
    /// The time-to-live (TTL) in seconds for entries in the cache
    pub ttl: LastCacheTtl,
}

/// A last cache will either store values for an explicit set of columns, or will accept all
/// non-key columns
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone, Default)]
#[serde(rename_all = "snake_case")]
pub enum LastCacheValueColumnsDef {
    /// Explicit list of column names
    Explicit { columns: Vec<ColumnId> },
    /// Stores all non-key columns
    #[default]
    AllNonKeyColumns,
}

/// The size of the last cache
///
/// Must be greater than 0
#[derive(Debug, Serialize, Eq, PartialEq, Clone, Copy)]
pub struct LastCacheSize(pub(crate) usize);

impl LastCacheSize {
    pub fn new(size: usize) -> Result<Self> {
        if size == 0 {
            Err(CatalogError::InvalidLastCacheSize)
        } else {
            Ok(Self(size))
        }
    }
}

impl<'de> Deserialize<'de> for LastCacheSize {
    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let count = usize::deserialize(deserializer)?;
        Self::new(count).map_err(|e| serde::de::Error::custom(e.to_string()))
    }
}

impl Default for LastCacheSize {
    fn default() -> Self {
        Self(1)
    }