lance_encoding/
version.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright The Lance Authors
3
4use std::str::FromStr;
5
6use lance_core::{Error, Result};
7use snafu::location;
8
9pub const LEGACY_FORMAT_VERSION: &str = "0.1";
10pub const V2_FORMAT_2_0: &str = "2.0";
11pub const V2_FORMAT_2_1: &str = "2.1";
12pub const V2_FORMAT_2_2: &str = "2.2";
13
14/// Lance file version
15#[derive(Debug, Default, PartialEq, Eq, Clone, Copy, Ord, PartialOrd, strum::EnumIter)]
16pub enum LanceFileVersion {
17    // This is a little confusing but we rely on the following facts:
18    //
19    // Any version <= Next is stable
20    // The latest version before Stable is the default version for new datasets
21    // Any version >= Next is unstable
22    //
23    // As a result, 'Stable' is not the divider between stable and unstable (Next does this)
24    // but only serves to mark the default version for new datasets.
25    //
26    /// The legacy (0.1) format
27    Legacy,
28    #[default]
29    V2_0,
30    /// The latest stable release (also the default version for new datasets)
31    Stable,
32    V2_1,
33    /// The latest unstable release
34    Next,
35    V2_2,
36}
37
38impl LanceFileVersion {
39    /// Convert Stable or Next to the actual version
40    pub fn resolve(&self) -> Self {
41        match self {
42            Self::Stable => Self::V2_0,
43            Self::Next => Self::V2_1,
44            _ => *self,
45        }
46    }
47
48    pub fn is_unstable(&self) -> bool {
49        self >= &Self::Next
50    }
51
52    pub fn try_from_major_minor(major: u32, minor: u32) -> Result<Self> {
53        match (major, minor) {
54            (0, 0) => Ok(Self::Legacy),
55            (0, 1) => Ok(Self::Legacy),
56            (0, 2) => Ok(Self::Legacy),
57            (0, 3) => Ok(Self::V2_0),
58            (2, 0) => Ok(Self::V2_0),
59            (2, 1) => Ok(Self::V2_1),
60            (2, 2) => Ok(Self::V2_2),
61            _ => Err(Error::InvalidInput {
62                source: format!("Unknown Lance storage version: {}.{}", major, minor).into(),
63                location: location!(),
64            }),
65        }
66    }
67
68    pub fn to_numbers(&self) -> (u32, u32) {
69        match self {
70            Self::Legacy => (0, 2),
71            Self::V2_0 => (2, 0),
72            Self::V2_1 => (2, 1),
73            Self::V2_2 => (2, 2),
74            Self::Stable => self.resolve().to_numbers(),
75            Self::Next => self.resolve().to_numbers(),
76        }
77    }
78
79    pub fn iter_non_legacy() -> impl Iterator<Item = Self> {
80        use strum::IntoEnumIterator;
81
82        Self::iter().filter(|&v| v != Self::Stable && v != Self::Next && v != Self::Legacy)
83    }
84}
85
86impl std::fmt::Display for LanceFileVersion {
87    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88        write!(
89            f,
90            "{}",
91            match self {
92                Self::Legacy => LEGACY_FORMAT_VERSION,
93                Self::V2_0 => V2_FORMAT_2_0,
94                Self::V2_1 => V2_FORMAT_2_1,
95                Self::V2_2 => V2_FORMAT_2_2,
96                Self::Stable => "stable",
97                Self::Next => "next",
98            }
99        )
100    }
101}
102
103impl FromStr for LanceFileVersion {
104    type Err = Error;
105
106    fn from_str(value: &str) -> Result<Self> {
107        match value.to_lowercase().as_str() {
108            LEGACY_FORMAT_VERSION => Ok(Self::Legacy),
109            V2_FORMAT_2_0 => Ok(Self::V2_0),
110            V2_FORMAT_2_1 => Ok(Self::V2_1),
111            V2_FORMAT_2_2 => Ok(Self::V2_2),
112            "stable" => Ok(Self::Stable),
113            "legacy" => Ok(Self::Legacy),
114            "next" => Ok(Self::Next),
115            // Version 0.3 is an alias of 2.0
116            "0.3" => Ok(Self::V2_0),
117            _ => Err(Error::InvalidInput {
118                source: format!("Unknown Lance storage version: {}", value).into(),
119                location: location!(),
120            }),
121        }
122    }
123}