use {
crate::*,
anyhow::Result,
notify::{
RecommendedWatcher,
RecursiveMode,
Watcher,
},
std::{
collections::HashSet,
path::PathBuf,
process::Command,
},
};
static DEFAULT_WATCHES: &[&str] = &["src", "tests", "benches", "examples"];
#[derive(Debug)]
pub struct Mission<'s> {
pub location_name: String,
pub job_name: String,
pub cargo_execution_directory: PathBuf,
pub workspace_root: PathBuf,
pub job: Job,
files_to_watch: Vec<PathBuf>,
directories_to_watch: Vec<PathBuf>,
pub settings: &'s Settings,
}
impl<'s> Mission<'s> {
pub fn new(
location: &MissionLocation,
job_name: String,
job: Job,
settings: &'s Settings,
) -> Result<Self> {
let location_name = location.name();
let add_all_src = location.intended_is_package;
let mut files_to_watch: Vec<PathBuf> = Vec::new();
let mut directories_to_watch = Vec::new();
if !location.intended_is_package {
directories_to_watch.push(location.intended_dir.clone());
}
for item in &location.packages {
if item.source.is_none() {
let item_path = item
.manifest_path
.parent()
.expect("parent of a target folder is a root folder");
if add_all_src {
let mut watches: Vec<&str> = job.watch.iter().map(|s| s.as_str()).collect();
if job.default_watch {
for watch in DEFAULT_WATCHES {
if !watches.contains(watch) {
watches.push(watch);
}
}
}
debug!("watches: {watches:?}");
for dir in &watches {
let full_path = item_path.join(dir);
if full_path.exists() {
directories_to_watch.push(full_path.into());
} else {
info!("missing {} dir: {:?}", dir, full_path);
}
}
}
if item.manifest_path.exists() {
files_to_watch.push(item.manifest_path.clone().into());
} else {
warn!("missing manifest file: {:?}", item.manifest_path);
}
}
}
let cargo_execution_directory = location.package_directory.clone();
Ok(Mission {
location_name,
job_name,
cargo_execution_directory,
workspace_root: location.workspace_root.clone(),
job,
files_to_watch,
directories_to_watch,
settings,
})
}
pub fn ignorer(&self) -> Option<Ignorer> {
match self.job.apply_gitignore {
Some(false) => {
debug!("No gitignorer because of settings");
None
}
_ => {
match Ignorer::new(&self.workspace_root) {
Ok(ignorer) => Some(ignorer),
Err(e) => {
debug!("Failed to initialise git ignorer: {e}");
None
}
}
}
}
}
pub fn bacon_locations_path(&self) -> PathBuf {
let path = &self.settings.export.path;
if path.is_relative() {
self.workspace_root.join(path)
} else {
path.to_path_buf()
}
}
pub fn on_success(&self) -> &Option<Action> {
&self.job.on_success
}
pub fn is_success(
&self,
report: &Report,
) -> bool {
report.is_success(self.job.allow_warnings, self.job.allow_failures)
}
pub fn add_watchs(
&self,
watcher: &mut RecommendedWatcher,
) -> Result<()> {
for file in &self.files_to_watch {
debug!("add watch file {:?}", file);
watcher.watch(file, RecursiveMode::NonRecursive)?;
}
for dir in &self.directories_to_watch {
debug!("add watch dir {:?}", dir);
watcher.watch(dir, RecursiveMode::Recursive)?;
}
Ok(())
}
pub fn get_command(&self) -> Command {
let mut tokens = self.job.command.iter();
let mut command = Command::new(
tokens.next().unwrap(), );
let mut no_default_features_done = false;
let mut features_done = false;
let mut last_is_features = false;
let tokens = tokens.chain(&self.settings.additional_job_args);
for arg in tokens {
if last_is_features {
if self.settings.all_features {
debug!("ignoring features given along --all-features");
} else {
features_done = true;
match (&self.settings.features, self.settings.no_default_features) {
(Some(features), false) => {
command.arg("--features");
command.arg(merge_features(arg, features));
}
(Some(features), true) => {
command.arg("--features");
command.arg(features);
}
(None, true) => {
}
(None, false) => {
command.arg("--features");
command.arg(arg);
}
}
}
last_is_features = false;
} else if arg == "--no-default-features" {
no_default_features_done = true;
last_is_features = false;
command.arg(arg);
} else if arg == "--features" {
last_is_features = true;
} else {
command.arg(arg);
}
}
if self.settings.no_default_features && !no_default_features_done {
command.arg("--no-default-features");
}
if self.settings.all_features {
command.arg("--all-features");
}
if !features_done {
if let Some(features) = &self.settings.features {
if self.settings.all_features {
debug!("not using features because of --all-features");
} else {
command.arg("--features");
command.arg(features);
}
}
}
command.current_dir(&self.cargo_execution_directory);
command.envs(&self.job.env);
debug!("command: {:#?}", &command);
command
}
pub fn need_stdout(&self) -> bool {
self.job.need_stdout
}
}
fn merge_features(
a: &str,
b: &str,
) -> String {
let mut features = HashSet::new();
for feature in a.split(',') {
features.insert(feature);
}
for feature in b.split(',') {
features.insert(feature);
}
features.iter().copied().collect::<Vec<&str>>().join(",")
}