Skip to main content

greentic_bundle/bundle_fs/
mod.rs

1mod backhand_writer;
2mod native_mksquashfs_writer;
3mod native_unsquashfs_reader;
4
5use std::path::Path;
6
7use anyhow::{Result, bail};
8
9pub use backhand_writer::{BackhandBundleFsReader, BackhandBundleFsWriter};
10pub use native_mksquashfs_writer::MksquashfsBundleFsWriter;
11pub use native_unsquashfs_reader::UnsquashfsBundleFsReader;
12
13pub const WRITER_ENV: &str = "GREENTIC_BUNDLE_SQUASHFS_WRITER";
14pub const READER_ENV: &str = "GREENTIC_BUNDLE_SQUASHFS_READER";
15
16pub trait BundleFsWriter {
17    fn write_bundle(&self, input_dir: &Path, output_file: &Path) -> Result<()>;
18}
19
20pub trait BundleFsReader {
21    fn list_bundle(&self, bundle_file: &Path) -> Result<Vec<BundleEntry>>;
22    fn extract_bundle(&self, bundle_file: &Path, output_dir: &Path) -> Result<()>;
23}
24
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct BundleEntry {
27    pub path: String,
28    pub kind: BundleEntryKind,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub enum BundleEntryKind {
33    File,
34    Directory,
35    Symlink,
36    Other,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum BundleFsWriterKind {
41    Backhand,
42    Mksquashfs,
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum BundleFsReaderKind {
47    Backhand,
48    Unsquashfs,
49}
50
51impl BundleFsWriterKind {
52    pub fn from_env_value(value: Option<&str>) -> Result<Self> {
53        match value.map(str::trim).filter(|value| !value.is_empty()) {
54            None => Ok(Self::Backhand),
55            Some("backhand") => Ok(Self::Backhand),
56            Some("mksquashfs") => Ok(Self::Mksquashfs),
57            Some(value) => bail!(
58                "{WRITER_ENV}={value} is not supported. Accepted values: backhand, mksquashfs"
59            ),
60        }
61    }
62}
63
64impl BundleFsReaderKind {
65    pub fn from_env_value(value: Option<&str>) -> Result<Self> {
66        match value.map(str::trim).filter(|value| !value.is_empty()) {
67            None => Ok(Self::Backhand),
68            Some("backhand") => Ok(Self::Backhand),
69            Some("unsquashfs") => Ok(Self::Unsquashfs),
70            Some(value) => bail!(
71                "{READER_ENV}={value} is not supported. Accepted values: backhand, unsquashfs"
72            ),
73        }
74    }
75}
76
77pub fn selected_writer_kind() -> Result<BundleFsWriterKind> {
78    BundleFsWriterKind::from_env_value(std::env::var(WRITER_ENV).ok().as_deref())
79}
80
81pub fn selected_reader_kind() -> Result<BundleFsReaderKind> {
82    BundleFsReaderKind::from_env_value(std::env::var(READER_ENV).ok().as_deref())
83}
84
85pub fn write_bundle(input_dir: &Path, output_file: &Path) -> Result<()> {
86    match selected_writer_kind()? {
87        BundleFsWriterKind::Backhand => BackhandBundleFsWriter.write_bundle(input_dir, output_file),
88        BundleFsWriterKind::Mksquashfs => {
89            MksquashfsBundleFsWriter.write_bundle(input_dir, output_file)
90        }
91    }
92}
93
94pub fn list_bundle(bundle_file: &Path) -> Result<Vec<BundleEntry>> {
95    match selected_reader_kind()? {
96        BundleFsReaderKind::Backhand => BackhandBundleFsReader.list_bundle(bundle_file),
97        BundleFsReaderKind::Unsquashfs => UnsquashfsBundleFsReader.list_bundle(bundle_file),
98    }
99}
100
101pub fn extract_bundle(bundle_file: &Path, output_dir: &Path) -> Result<()> {
102    match selected_reader_kind()? {
103        BundleFsReaderKind::Backhand => {
104            BackhandBundleFsReader.extract_bundle(bundle_file, output_dir)
105        }
106        BundleFsReaderKind::Unsquashfs => {
107            UnsquashfsBundleFsReader.extract_bundle(bundle_file, output_dir)
108        }
109    }
110}
111
112pub fn read_bundle_file(bundle_file: &Path, inner_path: &str) -> Result<Vec<u8>> {
113    match selected_reader_kind()? {
114        BundleFsReaderKind::Backhand => {
115            backhand_writer::read_bundle_file_with_backhand(bundle_file, inner_path)
116        }
117        BundleFsReaderKind::Unsquashfs => {
118            native_unsquashfs_reader::read_bundle_file_with_unsquashfs(bundle_file, inner_path)
119        }
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::{BundleFsReaderKind, BundleFsWriterKind, READER_ENV, WRITER_ENV};
126
127    #[test]
128    fn writer_selection_defaults_to_backhand() {
129        assert_eq!(
130            BundleFsWriterKind::from_env_value(None).expect("writer kind"),
131            BundleFsWriterKind::Backhand
132        );
133    }
134
135    #[test]
136    fn writer_selection_accepts_backhand() {
137        assert_eq!(
138            BundleFsWriterKind::from_env_value(Some("backhand")).expect("writer kind"),
139            BundleFsWriterKind::Backhand
140        );
141    }
142
143    #[test]
144    fn writer_selection_accepts_mksquashfs() {
145        assert_eq!(
146            BundleFsWriterKind::from_env_value(Some("mksquashfs")).expect("writer kind"),
147            BundleFsWriterKind::Mksquashfs
148        );
149    }
150
151    #[test]
152    fn writer_selection_rejects_unknown_values() {
153        let error = BundleFsWriterKind::from_env_value(Some("external")).expect_err("error");
154        let message = error.to_string();
155        assert!(message.contains(WRITER_ENV));
156        assert!(message.contains("backhand, mksquashfs"));
157    }
158
159    #[test]
160    fn reader_selection_defaults_to_backhand() {
161        assert_eq!(
162            BundleFsReaderKind::from_env_value(None).expect("reader kind"),
163            BundleFsReaderKind::Backhand
164        );
165    }
166
167    #[test]
168    fn reader_selection_accepts_backhand() {
169        assert_eq!(
170            BundleFsReaderKind::from_env_value(Some("backhand")).expect("reader kind"),
171            BundleFsReaderKind::Backhand
172        );
173    }
174
175    #[test]
176    fn reader_selection_accepts_unsquashfs() {
177        assert_eq!(
178            BundleFsReaderKind::from_env_value(Some("unsquashfs")).expect("reader kind"),
179            BundleFsReaderKind::Unsquashfs
180        );
181    }
182
183    #[test]
184    fn reader_selection_rejects_unknown_values() {
185        let error = BundleFsReaderKind::from_env_value(Some("external")).expect_err("error");
186        let message = error.to_string();
187        assert!(message.contains(READER_ENV));
188        assert!(message.contains("backhand, unsquashfs"));
189    }
190}