solana_runtime/snapshot_utils/
archive_format.rs

1use {
2    std::{fmt, str::FromStr},
3    strum::Display,
4};
5
6pub const SUPPORTED_ARCHIVE_COMPRESSION: &[&str] = &["bz2", "gzip", "zstd", "lz4", "tar", "none"];
7pub const DEFAULT_ARCHIVE_COMPRESSION: &str = "zstd";
8
9pub const TAR_BZIP2_EXTENSION: &str = "tar.bz2";
10pub const TAR_GZIP_EXTENSION: &str = "tar.gz";
11pub const TAR_ZSTD_EXTENSION: &str = "tar.zst";
12pub const TAR_LZ4_EXTENSION: &str = "tar.lz4";
13pub const TAR_EXTENSION: &str = "tar";
14
15/// The different archive formats used for snapshots
16#[derive(Copy, Clone, Debug, Eq, PartialEq, Display)]
17pub enum ArchiveFormat {
18    TarBzip2,
19    TarGzip,
20    TarZstd,
21    TarLz4,
22    Tar,
23}
24
25impl ArchiveFormat {
26    /// Get the file extension for the ArchiveFormat
27    pub fn extension(&self) -> &str {
28        match self {
29            ArchiveFormat::TarBzip2 => TAR_BZIP2_EXTENSION,
30            ArchiveFormat::TarGzip => TAR_GZIP_EXTENSION,
31            ArchiveFormat::TarZstd => TAR_ZSTD_EXTENSION,
32            ArchiveFormat::TarLz4 => TAR_LZ4_EXTENSION,
33            ArchiveFormat::Tar => TAR_EXTENSION,
34        }
35    }
36
37    pub fn from_cli_arg(archive_format_str: &str) -> Option<ArchiveFormat> {
38        match archive_format_str {
39            "bz2" => Some(ArchiveFormat::TarBzip2),
40            "gzip" => Some(ArchiveFormat::TarGzip),
41            "zstd" => Some(ArchiveFormat::TarZstd),
42            "lz4" => Some(ArchiveFormat::TarLz4),
43            "tar" | "none" => Some(ArchiveFormat::Tar),
44            _ => None,
45        }
46    }
47}
48
49// Change this to `impl<S: AsRef<str>> TryFrom<S> for ArchiveFormat [...]`
50// once this Rust bug is fixed: https://github.com/rust-lang/rust/issues/50133
51impl TryFrom<&str> for ArchiveFormat {
52    type Error = ParseError;
53
54    fn try_from(extension: &str) -> Result<Self, Self::Error> {
55        match extension {
56            TAR_BZIP2_EXTENSION => Ok(ArchiveFormat::TarBzip2),
57            TAR_GZIP_EXTENSION => Ok(ArchiveFormat::TarGzip),
58            TAR_ZSTD_EXTENSION => Ok(ArchiveFormat::TarZstd),
59            TAR_LZ4_EXTENSION => Ok(ArchiveFormat::TarLz4),
60            TAR_EXTENSION => Ok(ArchiveFormat::Tar),
61            _ => Err(ParseError::InvalidExtension(extension.to_string())),
62        }
63    }
64}
65
66impl FromStr for ArchiveFormat {
67    type Err = ParseError;
68
69    fn from_str(extension: &str) -> Result<Self, Self::Err> {
70        Self::try_from(extension)
71    }
72}
73
74#[derive(Debug, Clone, Eq, PartialEq)]
75pub enum ParseError {
76    InvalidExtension(String),
77}
78
79impl fmt::Display for ParseError {
80    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81        match self {
82            ParseError::InvalidExtension(extension) => {
83                write!(f, "Invalid archive extension: {}", extension)
84            }
85        }
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use {super::*, std::iter::zip};
92    const INVALID_EXTENSION: &str = "zip";
93
94    #[test]
95    fn test_extension() {
96        assert_eq!(ArchiveFormat::TarBzip2.extension(), TAR_BZIP2_EXTENSION);
97        assert_eq!(ArchiveFormat::TarGzip.extension(), TAR_GZIP_EXTENSION);
98        assert_eq!(ArchiveFormat::TarZstd.extension(), TAR_ZSTD_EXTENSION);
99        assert_eq!(ArchiveFormat::TarLz4.extension(), TAR_LZ4_EXTENSION);
100        assert_eq!(ArchiveFormat::Tar.extension(), TAR_EXTENSION);
101    }
102
103    #[test]
104    fn test_try_from() {
105        assert_eq!(
106            ArchiveFormat::try_from(TAR_BZIP2_EXTENSION),
107            Ok(ArchiveFormat::TarBzip2)
108        );
109        assert_eq!(
110            ArchiveFormat::try_from(TAR_GZIP_EXTENSION),
111            Ok(ArchiveFormat::TarGzip)
112        );
113        assert_eq!(
114            ArchiveFormat::try_from(TAR_ZSTD_EXTENSION),
115            Ok(ArchiveFormat::TarZstd)
116        );
117        assert_eq!(
118            ArchiveFormat::try_from(TAR_LZ4_EXTENSION),
119            Ok(ArchiveFormat::TarLz4)
120        );
121        assert_eq!(
122            ArchiveFormat::try_from(TAR_EXTENSION),
123            Ok(ArchiveFormat::Tar)
124        );
125        assert_eq!(
126            ArchiveFormat::try_from(INVALID_EXTENSION),
127            Err(ParseError::InvalidExtension(INVALID_EXTENSION.to_string()))
128        );
129    }
130
131    #[test]
132    fn test_from_str() {
133        assert_eq!(
134            ArchiveFormat::from_str(TAR_BZIP2_EXTENSION),
135            Ok(ArchiveFormat::TarBzip2)
136        );
137        assert_eq!(
138            ArchiveFormat::from_str(TAR_GZIP_EXTENSION),
139            Ok(ArchiveFormat::TarGzip)
140        );
141        assert_eq!(
142            ArchiveFormat::from_str(TAR_ZSTD_EXTENSION),
143            Ok(ArchiveFormat::TarZstd)
144        );
145        assert_eq!(
146            ArchiveFormat::from_str(TAR_LZ4_EXTENSION),
147            Ok(ArchiveFormat::TarLz4)
148        );
149        assert_eq!(
150            ArchiveFormat::from_str(TAR_EXTENSION),
151            Ok(ArchiveFormat::Tar)
152        );
153        assert_eq!(
154            ArchiveFormat::from_str(INVALID_EXTENSION),
155            Err(ParseError::InvalidExtension(INVALID_EXTENSION.to_string()))
156        );
157    }
158
159    #[test]
160    fn test_from_cli_arg() {
161        let golden = [
162            Some(ArchiveFormat::TarBzip2),
163            Some(ArchiveFormat::TarGzip),
164            Some(ArchiveFormat::TarZstd),
165            Some(ArchiveFormat::TarLz4),
166            Some(ArchiveFormat::Tar),
167            Some(ArchiveFormat::Tar),
168        ];
169
170        for (arg, expected) in zip(SUPPORTED_ARCHIVE_COMPRESSION.iter(), golden.into_iter()) {
171            assert_eq!(ArchiveFormat::from_cli_arg(arg), expected);
172        }
173
174        assert_eq!(ArchiveFormat::from_cli_arg("bad"), None);
175    }
176}