Skip to main content

batch_run/
entry.rs

1use termcolor::WriteColor;
2
3use std::fs::File;
4use std::path::{Path, PathBuf};
5
6use crate::binary::BinaryBuilder;
7use crate::cargo_rustc;
8use crate::config::{Config, WriterBuilder};
9use crate::logging;
10use crate::normalize::diagnostics;
11use crate::result::{EntryError, EntryFailed, EntryOutput, EntryResult};
12use crate::snapshot::{check_compile_fail, check_run_match};
13
14#[derive(Copy, Clone, Debug)]
15pub enum Expected {
16    RunMatch,
17    CompileFail,
18}
19
20impl Expected {
21    pub fn is_run_pass(self) -> bool {
22        use Expected::*;
23        match self {
24            RunMatch => true,
25            CompileFail => false,
26        }
27    }
28}
29
30#[derive(Clone, Debug)]
31pub struct Entry {
32    path: PathBuf,
33    expected: Expected,
34}
35
36impl Entry {
37    pub fn new<P: AsRef<Path>>(path: P, expected: Expected) -> Self {
38        Self {
39            path: path.as_ref().to_owned(),
40            expected,
41        }
42    }
43
44    fn run<W: WriteColor>(
45        &self,
46        builder: &BinaryBuilder,
47        cfg: &Config<W>,
48        log: &mut impl WriteColor,
49    ) -> EntryResult<()> {
50        logging::log_entry_start(self, log)?;
51        self.try_open()?;
52
53        let mut output =
54            cargo_rustc::build_entry(builder, &self.path, self.expected.is_run_pass())?;
55
56        let check = match self.expected {
57            Expected::RunMatch => {
58                // early exit if the entry has not compiled
59                if !output.status.success() {
60                    let stderr = diagnostics(&output.stderr).preferred().to_owned();
61                    logging::unexpected_build_error(log, stderr.as_bytes())?;
62                    return Err(EntryFailed::ShouldCompile(stderr));
63                }
64                output = cargo_rustc::run_entry()?;
65                check_run_match
66            }
67            Expected::CompileFail => check_compile_fail,
68        };
69        check(&self.path, output, cfg.update_mode(), log)
70            .and_then(|_| logging::ok(log).map_err(Into::into))
71    }
72
73    fn try_open(&self) -> EntryResult<()> {
74        if self.path.exists() {
75            return Ok(());
76        }
77        match File::open(&self.path) {
78            Ok(_) => Ok(()),
79            Err(err) => Err(EntryError::Open(self.path.clone(), err).into()),
80        }
81    }
82    pub fn path(&self) -> &Path {
83        &self.path
84    }
85    pub fn expected(&self) -> Expected {
86        self.expected
87    }
88}
89
90pub struct ExpandedEntry<W: WriteColor> {
91    log: W,
92    raw_entry: Entry,
93    error: Option<EntryFailed>,
94}
95
96pub(crate) fn expand_globs<W: WriteColor>(
97    entries: &[Entry],
98    writer: &WriterBuilder<W>,
99) -> Vec<ExpandedEntry<W>> {
100    fn glob(pattern: &str) -> EntryResult<Vec<PathBuf>> {
101        let mut paths = glob::glob(pattern)?
102            .map(|entry| entry.map_err(EntryFailed::from))
103            .collect::<EntryResult<Vec<PathBuf>>>()?;
104        paths.sort();
105        Ok(paths)
106    }
107
108    let mut vec = Vec::new();
109
110    for entry in entries {
111        let mut expanded = ExpandedEntry {
112            raw_entry: entry.clone(),
113            error: None,
114            log: writer.build(),
115        };
116        if let Some(utf8) = entry.path.to_str() {
117            if utf8.contains('*') {
118                match glob(utf8) {
119                    Ok(paths) => {
120                        for path in paths {
121                            vec.push(ExpandedEntry {
122                                raw_entry: Entry {
123                                    path,
124                                    expected: expanded.raw_entry.expected,
125                                },
126                                error: None,
127                                log: writer.build(),
128                            });
129                        }
130                        continue;
131                    }
132                    Err(error) => expanded.error = Some(error),
133                }
134            }
135        }
136        vec.push(expanded);
137    }
138
139    vec
140}
141
142impl<W: WriteColor> ExpandedEntry<W> {
143    pub fn run(self, builder: &BinaryBuilder, cfg: &Config<W>) -> EntryOutput<W> {
144        let Self {
145            error,
146            raw_entry,
147            mut log,
148        } = self;
149        let res = match error {
150            None => raw_entry.run(builder, cfg, &mut log),
151            Some(error) => {
152                // explicitly silence the io::Error - we have another error to show up
153                let _ = logging::log_entry_fail_to_start(&raw_entry, &mut log);
154                Err(error)
155            }
156        };
157        EntryOutput::new(res, log)
158    }
159
160    pub fn path(&self) -> &Path {
161        &self.raw_entry.path
162    }
163}