1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use super::*;
use crate::preproc::rga_preproc;
use ::tar::EntryType::Regular;
use failure::*;
use lazy_static::lazy_static;

use std::path::PathBuf;

static EXTENSIONS: &[&str] = &["tar", "tar.gz", "tar.bz2", "tar.xz", "tar.zst"];

lazy_static! {
    static ref METADATA: AdapterMeta = AdapterMeta {
        name: "tar".to_owned(),
        version: 1,
        description: "Reads a tar file as a stream and recurses down into its contents".to_owned(),
        fast_matchers: EXTENSIONS
            .iter()
            .map(|s| FastMatcher::FileExtension(s.to_string()))
            .collect(),
        slow_matchers: None
    };
}
#[derive(Default)]
pub struct TarAdapter;

impl TarAdapter {
    pub fn new() -> TarAdapter {
        TarAdapter
    }
}
impl GetMetadata for TarAdapter {
    fn metadata(&self) -> &AdapterMeta {
        &METADATA
    }
}

fn decompress_any<'a, R>(filename: &Path, inp: &'a mut R) -> Fallible<Box<dyn Read + 'a>>
where
    R: Read,
{
    let extension = filename.extension().map(|e| e.to_string_lossy().to_owned());
    match extension {
        Some(e) => Ok(match e.to_owned().as_ref() {
            "tgz" | "gz" => Box::new(flate2::read::MultiGzDecoder::new(inp)),
            "tbz" | "tbz2" | "bz2" => Box::new(bzip2::read::BzDecoder::new(inp)),
            "xz" => Box::new(xz2::read::XzDecoder::new_multi_decoder(inp)),
            "zst" => Box::new(zstd::stream::read::Decoder::new(inp)?),
            "tar" => Box::new(inp),
            ext => Err(format_err!("don't know how to decompress {}", ext))?,
        }),
        None => Err(format_err!("no extension")),
    }
}

impl FileAdapter for TarAdapter {
    fn adapt(&self, ai: AdaptInfo) -> Fallible<()> {
        let AdaptInfo {
            filepath_hint,
            mut inp,
            oup,
            line_prefix,
            archive_recursion_depth,
            config,
            ..
        } = ai;

        let decompress = decompress_any(filepath_hint, &mut inp)?;
        let mut archive = ::tar::Archive::new(decompress);
        for entry in archive.entries()? {
            let mut file = entry.unwrap();
            if Regular == file.header().entry_type() {
                let path = PathBuf::from(file.path()?.to_owned());
                eprintln!(
                    "{}|{}: {} bytes",
                    filepath_hint.display(),
                    path.display(),
                    file.header().size()?,
                );
                let line_prefix = &format!("{}{}: ", line_prefix, path.display());
                let ai2: AdaptInfo = AdaptInfo {
                    filepath_hint: &path,
                    is_real_file: false,
                    archive_recursion_depth: archive_recursion_depth + 1,
                    inp: &mut file,
                    oup,
                    line_prefix,
                    config: config.clone(),
                };
                rga_preproc(ai2)?;
            }
        }
        Ok(())
    }
}