use crate::rules::Rules;
#[derive(Debug)]
pub struct Watches {
rules: Vec<Rules>,
}
impl Watches {
pub fn new(rules: Vec<Rules>) -> Self {
Watches { rules }
}
pub fn watch(&self, path: &str) -> Option<Vec<Rules>> {
let cmds = self
.rules
.iter()
.cloned()
.filter(|r| !r.ignore(path) && r.watch(path))
.collect::<Vec<Rules>>();
if !cmds.is_empty() {
Some(cmds)
} else {
None
}
}
pub fn run_on_init(&self) -> Option<Vec<Rules>> {
let cmds = self
.rules
.iter()
.cloned()
.filter(|r| r.run_on_init())
.collect::<Vec<Rules>>();
if !cmds.is_empty() {
Some(cmds)
} else {
None
}
}
pub fn paths_to_watch(&self) -> Option<Vec<String>> {
let mut paths = self
.rules
.iter()
.map(|r| r.watch_absolute_paths())
.flatten()
.collect::<Vec<String>>();
let current_dir = std::env::current_dir().expect("Unable to get current directory");
paths.push(current_dir.to_str().unwrap().to_string());
if !paths.is_empty() {
Some(paths)
} else {
None
}
}
}
#[cfg(test)]
mod tests {
extern crate glob;
extern crate notify;
extern crate yaml_rust;
use super::*;
use crate::rules;
use std::env;
fn get_absolute_path(path: &str) -> String {
let mut absolute_path = env::current_dir().unwrap();
absolute_path.push(path);
absolute_path.to_str().unwrap().to_string()
}
#[test]
fn it_loads_from_args() {
let args = String::from("cargo build");
let watches = Watches::new(
rules::from_string(vec![".".to_owned()], args)
.expect("Error while parsing rules from string"),
);
assert!(watches.watch(&get_absolute_path("src/main.rs")).is_some());
assert!(watches.watch(&get_absolute_path("test/main.rs")).is_some());
assert!(watches.watch(&get_absolute_path(".")).is_some());
let result = rules::commands(watches.watch(&get_absolute_path(".")).unwrap());
assert_eq!(vec!["cargo build"], result);
}
#[test]
fn it_watches_test_path() {
let file_content = "
- name: my tests
run: 'cargo tests'
change: 'tests/**'
";
let watches = Watches::new(rules::from_yaml(&file_content).expect("Error parsing yaml"));
assert!(watches
.watch("/Users/crosa/others/funzzy/tests/test.rs")
.is_some());
assert!(watches.watch("tests/tests.rs").is_some());
assert!(watches.watch("tests/ruby.rb").is_some());
assert!(watches.watch("tests/folder/other.rs").is_some())
}
#[test]
fn it_watches_specific_path() {
let file_content = "
- name: my tests
run: 'cargo tests'
change: './tests/foo/bar.rs'
";
let watches = Watches::new(rules::from_yaml(&file_content).expect("Error parsing yaml"));
assert!(watches.watch("./tests/foo/bar.rs").is_some())
}
#[test]
fn it_doesnot_watch_test_path() {
let file_content = "
- name: my source
run: 'cargo build'
change: 'src/**'
";
let watches = Watches::new(rules::from_yaml(&file_content).expect("Error parsing yaml"));
assert!(watches
.watch("/Users/crosa/others/funzzy/events.yaml")
.is_none());
assert!(watches.watch("tests/").is_none());
assert!(watches.watch("tests/test.rs").is_none());
assert!(watches.watch("tests/folder/other.rs").is_none());
}
#[test]
fn it_creates_a_list_of_shell_commands() {
let file_content = "
- name: my source
run: 'cargo build'
change: 'src/**'
";
let watches = Watches::new(rules::from_yaml(&file_content).expect("Error parsing yaml"));
let result = rules::commands(watches.watch("src/test.rs").unwrap());
assert_eq!("cargo build", result[0])
}
#[test]
fn it_works_with_multiples_items() {
let file_content = "
- name: my source
run: 'cargo build'
change: 'src/**'
- name: other
run: 'cargo test'
change: 'test/**'
";
let watches = Watches::new(rules::from_yaml(&file_content).expect("Error parsing yaml"));
let result = rules::commands(watches.watch("test/test.rs").unwrap());
assert_eq!("cargo test", result[0]);
let result_src = rules::commands(watches.watch("src/test.rs").unwrap());
assert_eq!("cargo build", result_src[0]);
}
#[test]
fn it_allows_many_rules_watching_same_path() {
let file_content = "
- name: same path
run: 'echo same'
change: '**'
- name: my source
run: 'cargo build'
change: 'src/**'
- name: other
run: 'cargo test'
change: 'test/**'
";
let watches = Watches::new(rules::from_yaml(&file_content).expect("Error parsing yaml"));
let result = rules::commands(watches.watch("src/test.rs").unwrap());
assert_eq!(vec!["echo same", "cargo build"], result);
let result_multiple = rules::commands(watches.watch("test/test.rs").unwrap());
assert_eq!(vec!["echo same", "cargo test"], result_multiple);
}
#[test]
fn it_ignores_pattern() {
let file_content = "
- name: my source
run: 'cargo build'
change: 'src/**'
ignore: 'src/test/**'
";
let watches = Watches::new(rules::from_yaml(&file_content).expect("Error parsing yaml"));
assert!(watches.watch("src/other.rb").is_some());
assert!(watches.watch("src/test.txt").is_some());
assert!(watches.watch("src/test/other.tmp").is_none())
}
#[test]
fn it_ignores_a_list_of_patterns() {
let file_content = "
- name: my source
run: 'cargo build'
change: 'src/**'
ignore: ['src/test/**', 'src/tmp/**']
";
let watches = Watches::new(rules::from_yaml(&file_content).expect("Error parsing yaml"));
assert!(watches.watch("src/other.rb").is_some());
assert!(watches.watch("src/test.txt").is_some());
assert!(watches.watch("src/tmp/test.txt").is_none());
assert!(watches.watch("src/test/other.tmp").is_none())
}
#[test]
fn it_returns_on_init_rules() {
let file_content = "
- name: my source
run: 'cargo build'
change: 'src/**'
run_on_init: true
- name: my source
run: ['cat foo', 'cat bar']
change: 'src/**'
run_on_init: true
- name: other
run: 'cargo test'
change: 'test/**'
";
let watches = Watches::new(rules::from_yaml(&file_content).expect("Error parsing yaml"));
let results = rules::commands(watches.run_on_init().unwrap());
assert_eq!(
results,
vec![
"cargo build".to_string(),
"cat foo".to_string(),
"cat bar".to_string(),
]
);
}
#[test]
fn it_returns_rules_with_absolute_path_and_current_dir() {
let file_content = "
- name: my source
run: ['cat foo', 'cat bar']
change: 'src/**'
- name: rule with absolute path
run: 'cargo build'
change:
- 'src/**'
- '/tmp/**'
- '/User/**'
- name: it does not consider the ignored rules
run: 'cargo test'
change: 'test/**'
ignored: '/test/**'
- name: another with absolute path
run: echo 'absolute paths'
change:
- '/dev/**'
- '/usr/**'
- '/etc/**'
";
let watches = Watches::new(rules::from_yaml(&file_content).expect("Error parsing yaml"));
let results = watches.paths_to_watch().expect("No rules found");
assert_eq!(results.len(), 6);
assert_eq!(results[0], "/tmp/**");
assert_eq!(results[4], "/etc/**");
}
#[test]
fn it_returns_an_error_when_fail_to_load_config_file() {
assert!(rules::from_yaml(
&r#"
- name: run tests
run: [
"yarn test {{filepath}}",
change: 'src/**'
"#
)
.is_err());
assert!(rules::from_yaml(
&r#"
- name: other
run: 'cargo test'
change: 'test/**'
"#
)
.is_ok());
}
}