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 parse;
|
||||||
mod util;
|
mod util;
|
||||||
mod resolve;
|
mod resolve;
|
||||||
|
mod store;
|
||||||
|
|
||||||
pub trait Config {
|
pub trait Config {
|
||||||
fn label(&self) -> &str;
|
fn label(&self) -> &str;
|
||||||
fn match_paths(&self) -> &HashSet<String>;
|
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 crate::merge;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use regex::Regex;
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use std::{collections::HashSet, path::Path};
|
use std::{collections::HashSet, path::Path};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
@ -8,12 +9,17 @@ use thiserror::Error;
|
||||||
const STANDARD_INCLUDES: &[&str] = &["../match/**/*.yml"];
|
const STANDARD_INCLUDES: &[&str] = &["../match/**/*.yml"];
|
||||||
const STANDARD_EXCLUDES: &[&str] = &["../match/**/_*.yml"];
|
const STANDARD_EXCLUDES: &[&str] = &["../match/**/_*.yml"];
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct ResolvedConfig {
|
pub(crate) struct ResolvedConfig {
|
||||||
parsed: ParsedConfig,
|
parsed: ParsedConfig,
|
||||||
|
|
||||||
// Generated properties
|
// Generated properties
|
||||||
|
|
||||||
match_paths: HashSet<String>,
|
match_paths: HashSet<String>,
|
||||||
|
|
||||||
|
filter_title: Option<Regex>,
|
||||||
|
filter_class: Option<Regex>,
|
||||||
|
filter_exec: Option<Regex>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ResolvedConfig {
|
impl Default for ResolvedConfig {
|
||||||
|
@ -21,6 +27,9 @@ impl Default for ResolvedConfig {
|
||||||
Self {
|
Self {
|
||||||
parsed: Default::default(),
|
parsed: Default::default(),
|
||||||
match_paths: HashSet::new(),
|
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> {
|
fn match_paths(&self) -> &HashSet<String> {
|
||||||
&self.match_paths
|
&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 {
|
impl ResolvedConfig {
|
||||||
|
@ -47,13 +108,34 @@ impl ResolvedConfig {
|
||||||
// Extract the base directory
|
// Extract the base directory
|
||||||
let base_dir = path
|
let base_dir = path
|
||||||
.parent()
|
.parent()
|
||||||
.ok_or_else(|| ResolveError::ParentResolveFailed())?;
|
.ok_or_else(ResolveError::ParentResolveFailed)?;
|
||||||
|
|
||||||
let match_paths = Self::generate_match_paths(&config, base_dir);
|
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 {
|
Ok(Self {
|
||||||
parsed: config,
|
parsed: config,
|
||||||
match_paths,
|
match_paths,
|
||||||
|
filter_title,
|
||||||
|
filter_class,
|
||||||
|
filter_exec,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +292,11 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
HashSet::from_iter(
|
HashSet::from_iter(
|
||||||
vec!["../match/**/*.yml".to_string(), "custom/*.yml".to_string(), "sub/*.yml".to_string()]
|
vec![
|
||||||
|
"../match/**/*.yml".to_string(),
|
||||||
|
"custom/*.yml".to_string(),
|
||||||
|
"sub/*.yml".to_string()
|
||||||
|
]
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
)
|
)
|
||||||
|
@ -277,7 +363,11 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
HashSet::from_iter(
|
HashSet::from_iter(
|
||||||
vec!["../match/**/_*.yml".to_string(), "custom/*.yml".to_string(), "sub/*.yml".to_string()]
|
vec![
|
||||||
|
"../match/**/_*.yml".to_string(),
|
||||||
|
"custom/*.yml".to_string(),
|
||||||
|
"sub/*.yml".to_string()
|
||||||
|
]
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
)
|
)
|
||||||
|
@ -364,16 +454,24 @@ mod tests {
|
||||||
// Configs
|
// Configs
|
||||||
|
|
||||||
let parent_file = config_dir.join("parent.yml");
|
let parent_file = config_dir.join("parent.yml");
|
||||||
std::fs::write(&parent_file, r#"
|
std::fs::write(
|
||||||
|
&parent_file,
|
||||||
|
r#"
|
||||||
excludes: ['../**/another.yml']
|
excludes: ['../**/another.yml']
|
||||||
"#).unwrap();
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let config_file = config_dir.join("default.yml");
|
let config_file = config_dir.join("default.yml");
|
||||||
std::fs::write(&config_file, r#"
|
std::fs::write(
|
||||||
|
&config_file,
|
||||||
|
r#"
|
||||||
use_standard_includes: false
|
use_standard_includes: false
|
||||||
excludes: []
|
excludes: []
|
||||||
includes: ["../match/sub/*.yml"]
|
includes: ["../match/sub/*.yml"]
|
||||||
"#).unwrap();
|
"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let parent = ResolvedConfig::load(&parent_file, None).unwrap();
|
let parent = ResolvedConfig::load(&parent_file, None).unwrap();
|
||||||
let child = ResolvedConfig::load(&config_file, Some(&parent)).unwrap();
|
let child = ResolvedConfig::load(&config_file, Some(&parent)).unwrap();
|
||||||
|
@ -390,4 +488,178 @@ mod tests {
|
||||||
assert_eq!(parent.match_paths(), &expected);
|
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 config;
|
||||||
mod matches;
|
mod matches;
|
||||||
mod counter;
|
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