Skip to main content

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