Add config store base implementation and tests
This commit is contained in:
		
							parent
							
								
									0ca740914f
								
							
						
					
					
						commit
						7262727823
					
				| 
						 | 
				
			
			@ -4,8 +4,22 @@ mod path;
 | 
			
		|||
mod parse;
 | 
			
		||||
mod util;
 | 
			
		||||
mod resolve;
 | 
			
		||||
mod store;
 | 
			
		||||
 | 
			
		||||
pub trait Config {
 | 
			
		||||
  fn label(&self) -> &str;
 | 
			
		||||
  fn match_paths(&self) -> &HashSet<String>;
 | 
			
		||||
 | 
			
		||||
  fn is_match(&self, app: &AppProperties) -> bool;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait ConfigStore<'a> {
 | 
			
		||||
  fn default(&'a self) -> &'a dyn Config;
 | 
			
		||||
  fn active(&'a self, app: &AppProperties) -> &'a dyn Config;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct AppProperties<'a> {
 | 
			
		||||
  pub title: Option<&'a str>,
 | 
			
		||||
  pub class: Option<&'a str>,
 | 
			
		||||
  pub exec: Option<&'a str>,
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
use super::{parse::ParsedConfig, path::calculate_paths, Config};
 | 
			
		||||
use super::{parse::ParsedConfig, path::calculate_paths, util::os_matches, AppProperties, Config};
 | 
			
		||||
use crate::merge;
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use regex::Regex;
 | 
			
		||||
use std::iter::FromIterator;
 | 
			
		||||
use std::{collections::HashSet, path::Path};
 | 
			
		||||
use thiserror::Error;
 | 
			
		||||
| 
						 | 
				
			
			@ -8,12 +9,17 @@ use thiserror::Error;
 | 
			
		|||
const STANDARD_INCLUDES: &[&str] = &["../match/**/*.yml"];
 | 
			
		||||
const STANDARD_EXCLUDES: &[&str] = &["../match/**/_*.yml"];
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub(crate) struct ResolvedConfig {
 | 
			
		||||
  parsed: ParsedConfig,
 | 
			
		||||
 | 
			
		||||
  // Generated properties
 | 
			
		||||
  
 | 
			
		||||
  match_paths: HashSet<String>,
 | 
			
		||||
 | 
			
		||||
  filter_title: Option<Regex>,
 | 
			
		||||
  filter_class: Option<Regex>,
 | 
			
		||||
  filter_exec: Option<Regex>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for ResolvedConfig {
 | 
			
		||||
| 
						 | 
				
			
			@ -21,6 +27,9 @@ impl Default for ResolvedConfig {
 | 
			
		|||
    Self {
 | 
			
		||||
      parsed: Default::default(),
 | 
			
		||||
      match_paths: HashSet::new(),
 | 
			
		||||
      filter_title: None,
 | 
			
		||||
      filter_class: None,
 | 
			
		||||
      filter_exec: None,
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -33,6 +42,58 @@ impl Config for ResolvedConfig {
 | 
			
		|||
  fn match_paths(&self) -> &HashSet<String> {
 | 
			
		||||
    &self.match_paths
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fn is_match(&self, app: &AppProperties) -> bool {
 | 
			
		||||
    if self.parsed.filter_os.is_none()
 | 
			
		||||
      && self.parsed.filter_title.is_none()
 | 
			
		||||
      && self.parsed.filter_exec.is_none()
 | 
			
		||||
      && self.parsed.filter_class.is_none()
 | 
			
		||||
    {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let is_os_match = if let Some(filter_os) = self.parsed.filter_os.as_deref() {
 | 
			
		||||
      os_matches(filter_os)
 | 
			
		||||
    } else {
 | 
			
		||||
      true
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let is_title_match =
 | 
			
		||||
      if let Some(title_regex) = self.filter_title.as_ref() {
 | 
			
		||||
        if let Some(title) = app.title {
 | 
			
		||||
          title_regex.is_match(title)
 | 
			
		||||
        } else {
 | 
			
		||||
          false
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        true
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    let is_exec_match =
 | 
			
		||||
      if let Some(exec_regex) = self.filter_exec.as_ref() {
 | 
			
		||||
        if let Some(exec) = app.exec {
 | 
			
		||||
          exec_regex.is_match(exec)
 | 
			
		||||
        } else {
 | 
			
		||||
          false
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        true
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    let is_class_match =
 | 
			
		||||
      if let Some(class_regex) = self.filter_class.as_ref() {
 | 
			
		||||
        if let Some(class) = app.class {
 | 
			
		||||
          class_regex.is_match(class)
 | 
			
		||||
        } else {
 | 
			
		||||
          false
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        true
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    // All the filters that have been specified must be true to define a match
 | 
			
		||||
    is_os_match && is_exec_match && is_title_match && is_class_match
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ResolvedConfig {
 | 
			
		||||
| 
						 | 
				
			
			@ -47,13 +108,34 @@ impl ResolvedConfig {
 | 
			
		|||
    // Extract the base directory
 | 
			
		||||
    let base_dir = path
 | 
			
		||||
      .parent()
 | 
			
		||||
      .ok_or_else(|| ResolveError::ParentResolveFailed())?;
 | 
			
		||||
      .ok_or_else(ResolveError::ParentResolveFailed)?;
 | 
			
		||||
 | 
			
		||||
    let match_paths = Self::generate_match_paths(&config, base_dir);
 | 
			
		||||
 | 
			
		||||
    let filter_title = if let Some(filter_title) = config.filter_title.as_deref() {
 | 
			
		||||
      Some(Regex::new(filter_title)?)
 | 
			
		||||
    } else {
 | 
			
		||||
      None
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let filter_class = if let Some(filter_class) = config.filter_class.as_deref() {
 | 
			
		||||
      Some(Regex::new(filter_class)?)
 | 
			
		||||
    } else {
 | 
			
		||||
      None
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let filter_exec = if let Some(filter_exec) = config.filter_exec.as_deref() {
 | 
			
		||||
      Some(Regex::new(filter_exec)?)
 | 
			
		||||
    } else {
 | 
			
		||||
      None
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Ok(Self {
 | 
			
		||||
      parsed: config,
 | 
			
		||||
      match_paths,
 | 
			
		||||
      filter_title,
 | 
			
		||||
      filter_class,
 | 
			
		||||
      filter_exec,
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -210,9 +292,13 @@ mod tests {
 | 
			
		|||
        ..Default::default()
 | 
			
		||||
      }),
 | 
			
		||||
      HashSet::from_iter(
 | 
			
		||||
        vec!["../match/**/*.yml".to_string(), "custom/*.yml".to_string(), "sub/*.yml".to_string()]
 | 
			
		||||
          .iter()
 | 
			
		||||
          .cloned()
 | 
			
		||||
        vec![
 | 
			
		||||
          "../match/**/*.yml".to_string(),
 | 
			
		||||
          "custom/*.yml".to_string(),
 | 
			
		||||
          "sub/*.yml".to_string()
 | 
			
		||||
        ]
 | 
			
		||||
        .iter()
 | 
			
		||||
        .cloned()
 | 
			
		||||
      )
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -277,9 +363,13 @@ mod tests {
 | 
			
		|||
        ..Default::default()
 | 
			
		||||
      }),
 | 
			
		||||
      HashSet::from_iter(
 | 
			
		||||
        vec!["../match/**/_*.yml".to_string(), "custom/*.yml".to_string(), "sub/*.yml".to_string()]
 | 
			
		||||
          .iter()
 | 
			
		||||
          .cloned()
 | 
			
		||||
        vec![
 | 
			
		||||
          "../match/**/_*.yml".to_string(),
 | 
			
		||||
          "custom/*.yml".to_string(),
 | 
			
		||||
          "sub/*.yml".to_string()
 | 
			
		||||
        ]
 | 
			
		||||
        .iter()
 | 
			
		||||
        .cloned()
 | 
			
		||||
      )
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -364,16 +454,24 @@ mod tests {
 | 
			
		|||
      // Configs
 | 
			
		||||
 | 
			
		||||
      let parent_file = config_dir.join("parent.yml");
 | 
			
		||||
      std::fs::write(&parent_file, r#"
 | 
			
		||||
      std::fs::write(
 | 
			
		||||
        &parent_file,
 | 
			
		||||
        r#"
 | 
			
		||||
      excludes: ['../**/another.yml']
 | 
			
		||||
      "#).unwrap();
 | 
			
		||||
      "#,
 | 
			
		||||
      )
 | 
			
		||||
      .unwrap();
 | 
			
		||||
 | 
			
		||||
      let config_file = config_dir.join("default.yml");
 | 
			
		||||
      std::fs::write(&config_file, r#"
 | 
			
		||||
      std::fs::write(
 | 
			
		||||
        &config_file,
 | 
			
		||||
        r#"
 | 
			
		||||
      use_standard_includes: false
 | 
			
		||||
      excludes: []
 | 
			
		||||
      includes: ["../match/sub/*.yml"]
 | 
			
		||||
      "#).unwrap();
 | 
			
		||||
      "#,
 | 
			
		||||
      )
 | 
			
		||||
      .unwrap();
 | 
			
		||||
 | 
			
		||||
      let parent = ResolvedConfig::load(&parent_file, None).unwrap();
 | 
			
		||||
      let child = ResolvedConfig::load(&config_file, Some(&parent)).unwrap();
 | 
			
		||||
| 
						 | 
				
			
			@ -390,4 +488,178 @@ mod tests {
 | 
			
		|||
      assert_eq!(parent.match_paths(), &expected);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fn test_filter_is_match(config: &str, app: &AppProperties) -> bool {
 | 
			
		||||
    let mut result = false;
 | 
			
		||||
    let result_ref = &mut result;
 | 
			
		||||
    use_test_directory(move |_, _, config_dir| {
 | 
			
		||||
      let config_file = config_dir.join("default.yml");
 | 
			
		||||
      std::fs::write(&config_file, config).unwrap();
 | 
			
		||||
 | 
			
		||||
      let config = ResolvedConfig::load(&config_file, None).unwrap();
 | 
			
		||||
 | 
			
		||||
      *result_ref = config.is_match(app)
 | 
			
		||||
    });
 | 
			
		||||
    result
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn is_match_no_filters() {
 | 
			
		||||
    assert!(!test_filter_is_match(
 | 
			
		||||
      "",
 | 
			
		||||
      &AppProperties {
 | 
			
		||||
        title: Some("Google"),
 | 
			
		||||
        class: Some("Chrome"),
 | 
			
		||||
        exec: Some("chrome.exe"),
 | 
			
		||||
      },
 | 
			
		||||
    ));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn is_match_filter_title() {
 | 
			
		||||
    assert!(test_filter_is_match(
 | 
			
		||||
      "filter_title: Google",
 | 
			
		||||
      &AppProperties {
 | 
			
		||||
        title: Some("Google Mail"),
 | 
			
		||||
        class: Some("Chrome"),
 | 
			
		||||
        exec: Some("chrome.exe"),
 | 
			
		||||
      },
 | 
			
		||||
    ));
 | 
			
		||||
 | 
			
		||||
    assert!(!test_filter_is_match(
 | 
			
		||||
      "filter_title: Google",
 | 
			
		||||
      &AppProperties {
 | 
			
		||||
        title: Some("Yahoo"),
 | 
			
		||||
        class: Some("Chrome"),
 | 
			
		||||
        exec: Some("chrome.exe"),
 | 
			
		||||
      },
 | 
			
		||||
    ));
 | 
			
		||||
 | 
			
		||||
    assert!(!test_filter_is_match(
 | 
			
		||||
      "filter_title: Google",
 | 
			
		||||
      &AppProperties {
 | 
			
		||||
        title: None,
 | 
			
		||||
        class: Some("Chrome"),
 | 
			
		||||
        exec: Some("chrome.exe"),
 | 
			
		||||
      },
 | 
			
		||||
    ));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn is_match_filter_class() {
 | 
			
		||||
    assert!(test_filter_is_match(
 | 
			
		||||
      "filter_class: Chrome",
 | 
			
		||||
      &AppProperties {
 | 
			
		||||
        title: Some("Google Mail"),
 | 
			
		||||
        class: Some("Chrome"),
 | 
			
		||||
        exec: Some("chrome.exe"),
 | 
			
		||||
      },
 | 
			
		||||
    ));
 | 
			
		||||
 | 
			
		||||
    assert!(!test_filter_is_match(
 | 
			
		||||
      "filter_class: Chrome",
 | 
			
		||||
      &AppProperties {
 | 
			
		||||
        title: Some("Yahoo"),
 | 
			
		||||
        class: Some("Another"),
 | 
			
		||||
        exec: Some("chrome.exe"),
 | 
			
		||||
      },
 | 
			
		||||
    ));
 | 
			
		||||
 | 
			
		||||
    assert!(!test_filter_is_match(
 | 
			
		||||
      "filter_class: Chrome",
 | 
			
		||||
      &AppProperties {
 | 
			
		||||
        title: Some("google"),
 | 
			
		||||
        class: None,
 | 
			
		||||
        exec: Some("chrome.exe"),
 | 
			
		||||
      },
 | 
			
		||||
    ));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn is_match_filter_exec() {
 | 
			
		||||
    assert!(test_filter_is_match(
 | 
			
		||||
      "filter_exec: chrome.exe",
 | 
			
		||||
      &AppProperties {
 | 
			
		||||
        title: Some("Google Mail"),
 | 
			
		||||
        class: Some("Chrome"),
 | 
			
		||||
        exec: Some("chrome.exe"),
 | 
			
		||||
      },
 | 
			
		||||
    ));
 | 
			
		||||
 | 
			
		||||
    assert!(!test_filter_is_match(
 | 
			
		||||
      "filter_exec: chrome.exe",
 | 
			
		||||
      &AppProperties {
 | 
			
		||||
        title: Some("Yahoo"),
 | 
			
		||||
        class: Some("Another"),
 | 
			
		||||
        exec: Some("zoom.exe"),
 | 
			
		||||
      },
 | 
			
		||||
    ));
 | 
			
		||||
 | 
			
		||||
    assert!(!test_filter_is_match(
 | 
			
		||||
      "filter_exec: chrome.exe",
 | 
			
		||||
      &AppProperties {
 | 
			
		||||
        title: Some("google"),
 | 
			
		||||
        class: Some("Chrome"),
 | 
			
		||||
        exec: None,
 | 
			
		||||
      },
 | 
			
		||||
    ));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn is_match_filter_os() {
 | 
			
		||||
    let (current, another) = if cfg!(target_os = "windows") {
 | 
			
		||||
      ("windows", "macos")
 | 
			
		||||
    } else if cfg!(target_os = "macos") {
 | 
			
		||||
      ("macos", "windows")
 | 
			
		||||
    } else if cfg!(target_os = "linux") {
 | 
			
		||||
      ("linux", "macos")
 | 
			
		||||
    } else {
 | 
			
		||||
      ("invalid", "invalid")
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    assert!(test_filter_is_match(
 | 
			
		||||
      &format!("filter_os: {}", current),
 | 
			
		||||
      &AppProperties {
 | 
			
		||||
        title: Some("Google Mail"),
 | 
			
		||||
        class: Some("Chrome"),
 | 
			
		||||
        exec: Some("chrome.exe"),
 | 
			
		||||
      },
 | 
			
		||||
    ));
 | 
			
		||||
 | 
			
		||||
    assert!(!test_filter_is_match(
 | 
			
		||||
      &format!("filter_os: {}", another),
 | 
			
		||||
      &AppProperties {
 | 
			
		||||
        title: Some("Google Mail"),
 | 
			
		||||
        class: Some("Chrome"),
 | 
			
		||||
        exec: Some("chrome.exe"),
 | 
			
		||||
      },
 | 
			
		||||
    ));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn is_match_multiple_filters() {
 | 
			
		||||
    assert!(test_filter_is_match(
 | 
			
		||||
      r#"
 | 
			
		||||
      filter_exec: chrome.exe
 | 
			
		||||
      filter_title: "Youtube"
 | 
			
		||||
      "#,
 | 
			
		||||
      &AppProperties {
 | 
			
		||||
        title: Some("Youtube - Broadcast Yourself"),
 | 
			
		||||
        class: Some("Chrome"),
 | 
			
		||||
        exec: Some("chrome.exe"),
 | 
			
		||||
      },
 | 
			
		||||
    ));
 | 
			
		||||
 | 
			
		||||
    assert!(!test_filter_is_match(
 | 
			
		||||
      r#"
 | 
			
		||||
      filter_exec: chrome.exe
 | 
			
		||||
      filter_title: "Youtube"
 | 
			
		||||
      "#,
 | 
			
		||||
      &AppProperties {
 | 
			
		||||
        title: Some("Gmail"),
 | 
			
		||||
        class: Some("Chrome"),
 | 
			
		||||
        exec: Some("chrome.exe"),
 | 
			
		||||
      },
 | 
			
		||||
    ));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										103
									
								
								espanso-config/src/config/store.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								espanso-config/src/config/store.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,103 @@
 | 
			
		|||
use super::{Config, ConfigStore};
 | 
			
		||||
 | 
			
		||||
pub(crate) struct DefaultConfigStore {
 | 
			
		||||
  default: Box<dyn Config>,
 | 
			
		||||
  customs: Vec<Box<dyn Config>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> ConfigStore<'a> for DefaultConfigStore {
 | 
			
		||||
  fn default(&'a self) -> &'a dyn super::Config {
 | 
			
		||||
    self.default.as_ref()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fn active(&'a self, app: &super::AppProperties) -> &'a dyn super::Config {
 | 
			
		||||
    // Find a custom config that matches or fallback to the default one
 | 
			
		||||
    for custom in self.customs.iter() {
 | 
			
		||||
      if custom.is_match(app) {
 | 
			
		||||
        return custom.as_ref();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    self.default.as_ref()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
  use super::*;
 | 
			
		||||
 | 
			
		||||
  struct MockConfig {
 | 
			
		||||
    label: String,
 | 
			
		||||
    is_match: bool,
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  impl MockConfig {
 | 
			
		||||
    pub fn new(label: &str, is_match: bool) -> Self {
 | 
			
		||||
      Self {
 | 
			
		||||
        label: label.to_string(),
 | 
			
		||||
        is_match,
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  impl Config for MockConfig {
 | 
			
		||||
    fn label(&self) -> &str {
 | 
			
		||||
      &self.label
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn match_paths(&self) -> &std::collections::HashSet<String> {
 | 
			
		||||
      unimplemented!()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn is_match(&self, _: &crate::config::AppProperties) -> bool {
 | 
			
		||||
      self.is_match
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn config_store_selects_correctly() {
 | 
			
		||||
    let default = MockConfig::new("default", false);
 | 
			
		||||
    let custom1 = MockConfig::new("custom1", false);
 | 
			
		||||
    let custom2 = MockConfig::new("custom2", true);
 | 
			
		||||
 | 
			
		||||
    let store = DefaultConfigStore {
 | 
			
		||||
      default: Box::new(default),
 | 
			
		||||
      customs: vec![Box::new(custom1), Box::new(custom2)],
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    assert_eq!(store.default().label(), "default");
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
      store
 | 
			
		||||
        .active(&crate::config::AppProperties {
 | 
			
		||||
          title: None,
 | 
			
		||||
          class: None,
 | 
			
		||||
          exec: None,
 | 
			
		||||
        })
 | 
			
		||||
        .label(),
 | 
			
		||||
      "custom2"
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn config_store_active_fallback_to_default_if_no_match() {
 | 
			
		||||
    let default = MockConfig::new("default", false);
 | 
			
		||||
    let custom1 = MockConfig::new("custom1", false);
 | 
			
		||||
    let custom2 = MockConfig::new("custom2", false);
 | 
			
		||||
 | 
			
		||||
    let store = DefaultConfigStore {
 | 
			
		||||
      default: Box::new(default),
 | 
			
		||||
      customs: vec![Box::new(custom1), Box::new(custom2)],
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    assert_eq!(store.default().label(), "default");
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
      store
 | 
			
		||||
        .active(&crate::config::AppProperties {
 | 
			
		||||
          title: None,
 | 
			
		||||
          class: None,
 | 
			
		||||
          exec: None,
 | 
			
		||||
        })
 | 
			
		||||
        .label(),
 | 
			
		||||
      "default"
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -18,3 +18,45 @@ macro_rules! merge {
 | 
			
		|||
    }
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn os_matches(os: &str) -> bool {
 | 
			
		||||
  match os {
 | 
			
		||||
    "macos" => cfg!(target_os = "macos"),
 | 
			
		||||
    "windows" => cfg!(target_os = "windows"),
 | 
			
		||||
    "linux" => cfg!(target_os = "linux"),
 | 
			
		||||
    _ => false,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
  use super::*;
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  #[cfg(target_os = "linux")]
 | 
			
		||||
  fn os_matches_linux() {
 | 
			
		||||
    assert!(os_matches("linux"));
 | 
			
		||||
    assert!(!os_matches("windows"));
 | 
			
		||||
    assert!(!os_matches("macos"));
 | 
			
		||||
    assert!(!os_matches("invalid"));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  #[cfg(target_os = "macos")]
 | 
			
		||||
  fn os_matches_macos() {
 | 
			
		||||
    assert!(os_matches("macos"));
 | 
			
		||||
    assert!(!os_matches("windows"));
 | 
			
		||||
    assert!(!os_matches("linux"));
 | 
			
		||||
    assert!(!os_matches("invalid"));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  #[cfg(target_os = "windows")]
 | 
			
		||||
  fn os_matches_windows() {
 | 
			
		||||
    assert!(os_matches("windows"));
 | 
			
		||||
    assert!(!os_matches("macos"));
 | 
			
		||||
    assert!(!os_matches("linux"));
 | 
			
		||||
    assert!(!os_matches("invalid"));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -23,40 +23,3 @@ mod util;
 | 
			
		|||
mod config;
 | 
			
		||||
mod matches;
 | 
			
		||||
mod counter;
 | 
			
		||||
 | 
			
		||||
use std::path::Path;
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use serde::{Serialize, de::DeserializeOwned};
 | 
			
		||||
use thiserror::Error;
 | 
			
		||||
use config::Config;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
pub struct ConfigSet {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ConfigSet {
 | 
			
		||||
  //fn active(&self, app: AppProperties) -> &'a Config {
 | 
			
		||||
    // TODO: using the app properties, check if any of the sub configs match or not. If not, return the default
 | 
			
		||||
    // Here a RegexSet might be very useful to efficiently match them.
 | 
			
		||||
  //}
 | 
			
		||||
 | 
			
		||||
  //fn default(&self) -> &'a Config {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct AppProperties<'a> {
 | 
			
		||||
  pub title: Option<&'a str>,
 | 
			
		||||
  pub class: Option<&'a str>,
 | 
			
		||||
  pub exec: Option<&'a str>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
  use super::*;
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn todo() {
 | 
			
		||||
    
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user