• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tamada / totebag / 12855166384

19 Jan 2025 04:12PM UTC coverage: 80.663% (-0.7%) from 81.322%
12855166384

push

github

web-flow
Merge pull request #35 from tamada/release/v0.7.0

Release/v0.7.0

438 of 628 new or added lines in 18 files covered. (69.75%)

15 existing lines in 7 files now uncovered.

1339 of 1660 relevant lines covered (80.66%)

14.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

72.03
/src/extractor.rs
1
use std::path::PathBuf;
2

3
use super::{Result, ToteError};
4
use super::format::{find_format, Format};
5

6
mod cab;
7
mod lha;
8
mod rar;
9
mod sevenz;
10
mod tar;
11
mod zip;
12

13
pub struct ExtractorOpts {
14
    pub dest: PathBuf,
15
    pub use_archive_name_dir: bool,
16
    pub overwrite: bool,
17
}
18

19
impl ExtractorOpts {
NEW
20
    pub fn new(dest: Option<PathBuf>) -> ExtractorOpts {
×
NEW
21
        ExtractorOpts::new_with_opts(dest, false, false)
×
NEW
22
    }
×
23

24
    pub fn new_with_opts(dest: Option<PathBuf>, use_archive_name_dir: bool, overwrite: bool) -> ExtractorOpts {
8✔
25
        ExtractorOpts {
8✔
26
            dest: dest.unwrap_or_else(|| PathBuf::from(".")),
8✔
27
            use_archive_name_dir,
8✔
28
            overwrite,
8✔
29
        }
8✔
30
    }
8✔
31

32
    /// Returns the base of the destination directory for the archive file.
33
    /// The target is the archive file name of source.
34
    pub fn destination(&self, target: &PathBuf) -> Result<PathBuf> {
101✔
35
        let dest = self.destination_file(target);
101✔
36
        if dest.exists() && !self.overwrite {
101✔
37
            if dest == PathBuf::from(".") {
1✔
38
                Ok(dest)
1✔
39
            } else {
NEW
40
                Err(ToteError::FileExists(dest.clone()))
×
41
            }
42
        } else {
43
            Ok(dest)
100✔
44
        }
45
    }
101✔
46

47
    fn destination_file(&self, target: &PathBuf) -> PathBuf {
101✔
48
        if self.use_archive_name_dir {
101✔
49
            let file_name = target.file_name().unwrap().to_str().unwrap();
62✔
50
            let ext = target.extension().unwrap().to_str().unwrap();
62✔
51
            let dir_name = file_name
62✔
52
                .trim_end_matches(ext)
62✔
53
                .trim_end_matches(".")
62✔
54
                .to_string();
62✔
55
            self.dest.join(dir_name)
62✔
56
        } else {
57
            self.dest.clone()
39✔
58
        }
59
    }
101✔
60
}
61

62

63
pub struct Extractor<'a> {
64
    archive_file: PathBuf,
65
    opts: &'a ExtractorOpts,
66
    extractor: Box<dyn ToteExtractor>,
67
}
68

69
impl<'a> Extractor<'a> {
70
    pub fn new(archive_file: PathBuf, opts: &'a ExtractorOpts) -> Result<Self> {
8✔
71
        match create_extractor(&archive_file) {
8✔
72
            Ok(extractor) => Ok(Self {
8✔
73
                archive_file,
8✔
74
                opts,
8✔
75
                extractor,
8✔
76
            }),
8✔
NEW
77
            Err(e) => Err(e),
×
78
        }
79
    }
8✔
80

81
    pub fn format(&self) -> Format {
8✔
82
        self.extractor.format()
8✔
83
    }
8✔
84

NEW
85
    pub fn perform(&self) -> Result<()> {
×
NEW
86
        self.extractor.perform(&self.archive_file, &self.opts)
×
NEW
87
    }
×
88

NEW
89
    pub fn can_extract(&self) -> Result<()> {
×
NEW
90
        let dest = self.target_dir();
×
NEW
91
        if dest == PathBuf::from(".") {
×
NEW
92
            Ok(())
×
93
        } else {
NEW
94
            if dest.exists() && dest.is_dir() && self.opts.overwrite {
×
NEW
95
                Err(ToteError::DirExists(dest))
×
NEW
96
            } else if dest.is_file() {
×
NEW
97
                Err(ToteError::FileExists(dest))
×
98
            } else {
NEW
99
                Ok(())
×
100
            }
101
        }
NEW
102
    }
×
103

104
    pub fn list(&self) -> Result<Vec<String>> {
8✔
105
        self.extractor.list_archives(&self.archive_file)
8✔
106
    }
8✔
107

NEW
108
    pub fn info(&self) -> String {
×
NEW
109
        format!(
×
NEW
110
            "Format: {:?}\nFile: {:?}\nDestination: {:?}",
×
NEW
111
            self.extractor.format(),
×
NEW
112
            self.archive_file,
×
NEW
113
            self.opts.dest,
×
NEW
114
        )
×
NEW
115
    }
×
116

NEW
117
    pub fn target_dir(&self) -> PathBuf {
×
NEW
118
        if self.opts.use_archive_name_dir {
×
NEW
119
            let base = self.opts.dest.clone();
×
NEW
120
            if let Some(file_name) = self.archive_file.file_stem() {
×
NEW
121
                let dir_name = file_name.to_str().unwrap();
×
NEW
122
                self.opts.dest.join(dir_name)
×
123
            } else {
NEW
124
                base
×
125
            }
126
        } else {
NEW
127
            self.opts.dest.clone()
×
128
        }
NEW
129
    }
×
130
}
131

132
pub(crate) trait ToteExtractor {
133
    fn list_archives(&self, archive_file: &PathBuf) -> Result<Vec<String>>;
134
    fn perform(&self, archive_file: &PathBuf, opts: &ExtractorOpts) -> Result<()>;
135
    fn format(&self) -> Format;
136
}
137

138
fn create_extractor(file: &PathBuf) -> Result<Box<dyn ToteExtractor>> {
16✔
139
    let format = find_format(file.file_name());
16✔
140
    match format {
16✔
141
        Ok(format) => {
16✔
142
            return match format {
16✔
143
                Format::Cab => Ok(Box::new(cab::CabExtractor {})),
1✔
144
                Format::LHA => Ok(Box::new(lha::LhaExtractor {})),
×
145
                Format::Rar => Ok(Box::new(rar::RarExtractor {})),
1✔
146
                Format::SevenZ => Ok(Box::new(sevenz::SevenZExtractor {})),
2✔
147
                Format::Tar => Ok(Box::new(tar::TarExtractor {})),
2✔
148
                Format::TarBz2 => Ok(Box::new(tar::TarBz2Extractor {})),
2✔
149
                Format::TarGz => Ok(Box::new(tar::TarGzExtractor {})),
2✔
150
                Format::TarXz => Ok(Box::new(tar::TarXzExtractor {})),
2✔
151
                Format::TarZstd => Ok(Box::new(tar::TarZstdExtractor {})),
1✔
152
                Format::Zip => Ok(Box::new(zip::ZipExtractor {})),
2✔
153
                Format::Unknown(s) => Err(ToteError::UnknownFormat(format!(
1✔
154
                    "{}: unsupported format",
1✔
155
                    s
1✔
156
                ))),
1✔
157
            }
158
        }
159
        Err(msg) => Err(msg),
×
160
    }
161
}
16✔
162

163
#[cfg(test)]
164
mod tests {
165
    use super::*;
166

167
    #[test]
168
    fn test_destination() {
1✔
169
        let opts1 = ExtractorOpts {
1✔
170
            dest: PathBuf::from("."),
1✔
171
            use_archive_name_dir: true,
1✔
172
            overwrite: false,
1✔
173
        };
1✔
174
        let target = PathBuf::from("/tmp/archive.zip");
1✔
175

176
        if let Ok(t) = opts1.destination(&target) {
1✔
177
            assert_eq!(t, PathBuf::from("./archive"));
1✔
178
        }
×
179

180
        let opts2 = ExtractorOpts {
1✔
181
            dest: PathBuf::from("."),
1✔
182
            use_archive_name_dir: false,
1✔
183
            overwrite: false,
1✔
184
        };
1✔
185
        let target = PathBuf::from("/tmp/archive.zip");
1✔
186
        if let Ok(t) = opts2.destination(&target) {
1✔
187
            assert_eq!(t, PathBuf::from("."));
1✔
UNCOV
188
        }
×
189
    }
1✔
190

191
    #[test]
192
    fn test_create_extractor() {
1✔
193
        let e1 = create_extractor(&PathBuf::from("results/test.zip"));
1✔
194
        assert!(e1.is_ok());
1✔
195
        assert_eq!(e1.unwrap().format(), Format::Zip);
1✔
196

197
        let e2 = create_extractor(&PathBuf::from("results/test.tar"));
1✔
198
        assert!(e2.is_ok());
1✔
199
        assert_eq!(e2.unwrap().format(), Format::Tar);
1✔
200

201
        let e3 = create_extractor(&PathBuf::from("results/test.tgz"));
1✔
202
        assert!(e3.is_ok());
1✔
203
        assert_eq!(e3.unwrap().format(), Format::TarGz);
1✔
204

205
        let e4 = create_extractor(&PathBuf::from("results/test.tbz2"));
1✔
206
        assert!(e4.is_ok());
1✔
207
        assert_eq!(e4.unwrap().format(), Format::TarBz2);
1✔
208

209
        let e5 = create_extractor(&PathBuf::from("results/test.rar"));
1✔
210
        assert!(e5.is_ok());
1✔
211
        assert_eq!(e5.unwrap().format(), Format::Rar);
1✔
212

213
        let e6 = create_extractor(&PathBuf::from("results/test.tar.xz"));
1✔
214
        assert!(e6.is_ok());
1✔
215
        assert_eq!(e6.unwrap().format(), Format::TarXz);
1✔
216

217
        let e7 = create_extractor(&PathBuf::from("results/test.7z"));
1✔
218
        assert!(e7.is_ok());
1✔
219
        assert_eq!(e7.unwrap().format(), Format::SevenZ);
1✔
220

221
        let e8 = create_extractor(&PathBuf::from("results/test.unknown"));
1✔
222
        assert!(e8.is_err());
1✔
223
        if let Err(ToteError::UnknownFormat(msg)) = e8 {
1✔
224
            assert_eq!(msg, "test.unknown: unsupported format".to_string());
1✔
225
        } else {
226
            assert!(false);
×
227
        }
228
    }
1✔
229
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc