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 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 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}