greentic_bundle/bundle_fs/
mod.rs1mod 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}