summaryrefslogtreecommitdiffstats
path: root/src/input.rs
blob: a558b422defead9ef482e25e09aa0f01810fb412 (plain)
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use std::{fs::File, io::prelude::*, path::PathBuf};

use crate::{Error, Replacer, Result};

use is_terminal::IsTerminal;

#[derive(Debug)]
pub(crate) enum Source {
    Stdin,
    Files(Vec<PathBuf>),
}

impl Source {
    pub(crate) fn recursive() -> Result<Self> {
        Ok(Self::Files(
            ignore::WalkBuilder::new(".")
                .hidden(false)
                .filter_entry(|e| e.file_name() != ".git")
                .build()
                .filter_map(|d| d.ok())
                .filter_map(|d| match d.file_type() {
                    Some(t) if t.is_file() => Some(d.into_path()),
                    _ => None,
                })
                .collect(),
        ))
    }
}

pub(crate) struct App {
    replacer: Replacer,
    source: Source,
}

impl App {
    fn stdin_replace(&self, is_tty: bool) -> Result<()> {
        let mut buffer = Vec::with_capacity(256);
        let stdin = std::io::stdin();
        let mut handle = stdin.lock();
        handle.read_to_end(&mut buffer)?;

        let stdout = std::io::stdout();
        let mut handle = stdout.lock();

        handle.write_all(&if is_tty {
            self.replacer.replace_preview(&buffer)
        } else {
            self.replacer.replace(&buffer)
        })?;

        Ok(())
    }

    pub(crate) fn new(source: Source, replacer: Replacer) -> Self {
        Self { source, replacer }
    }
    pub(crate) fn run(&self, preview: bool) -> Result<()> {
        let is_tty = std::io::stdout().is_terminal();

        match (&self.source, preview) {
            (Source::Stdin, true) => self.stdin_replace(is_tty),
            (Source::Stdin, false) => self.stdin_replace(is_tty),
            (Source::Files(paths), false) => {
                use rayon::prelude::*;

                let failed_jobs: Vec<_> = paths
                    .par_iter()
                    .filter_map(|p| {
                        if let Err(e) = self.replacer.replace_file(p) {
                            Some((p.to_owned(), e))
                        } else {
                            None
                        }
                    })
                    .collect();

                if failed_jobs.is_empty() {
                    Ok(())
                } else {
                    let failed_jobs =
                        crate::error::FailedJobs::from(failed_jobs);
                    Err(Error::FailedProcessing(failed_jobs))
                }
            }
            (Source::Files(paths), true) => {
                let stdout = std::io::stdout();
                let mut handle = stdout.lock();
                let print_path = paths.len() > 1;

                paths.iter().try_for_each(|path| {
                    if Replacer::check_not_empty(File::open(path)?).is_err() {
                        return Ok(());
                    }
                    let file =
                        unsafe { memmap2::Mmap::map(&File::open(path)?)? };
                    if self.replacer.has_matches(&file) {
                        if print_path {
                            writeln!(
                                handle,
                                "----- FILE {} -----",
                                path.display()
                            )?;
                        }

                        handle
                            .write_all(&self.replacer.replace_preview(&file))?;
                        writeln!(handle)?;
                    }

                    Ok(())
                })
            }
        }
    }
}