Making progress in the config parsing
This commit is contained in:
parent
e26a04de67
commit
2283cedbd3
75
Cargo.lock
generated
75
Cargo.lock
generated
|
@ -210,7 +210,7 @@ checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.8",
|
"quote 1.0.9",
|
||||||
"syn 1.0.60",
|
"syn 1.0.60",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -235,6 +235,7 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
|
"tempdir",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -300,6 +301,12 @@ dependencies = [
|
||||||
"widestring",
|
"widestring",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuchsia-cprng"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.1.16"
|
version = "0.1.16"
|
||||||
|
@ -522,13 +529,50 @@ checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.8"
|
version = "1.0.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
|
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
||||||
|
dependencies = [
|
||||||
|
"fuchsia-cprng",
|
||||||
|
"libc",
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
"rdrand",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.4.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rdrand"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.1.57"
|
version = "0.1.57"
|
||||||
|
@ -564,6 +608,15 @@ version = "0.6.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
|
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "remove_dir_all"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-argon2"
|
name = "rust-argon2"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
|
@ -604,7 +657,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31"
|
checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.8",
|
"quote 1.0.9",
|
||||||
"syn 1.0.60",
|
"syn 1.0.60",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -676,7 +729,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
|
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.8",
|
"quote 1.0.9",
|
||||||
"unicode-xid 0.2.1",
|
"unicode-xid 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -689,6 +742,16 @@ dependencies = [
|
||||||
"unicode-xid 0.0.4",
|
"unicode-xid 0.0.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempdir"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
|
||||||
|
dependencies = [
|
||||||
|
"rand",
|
||||||
|
"remove_dir_all",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termcolor"
|
name = "termcolor"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
|
@ -714,7 +777,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1"
|
checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote 1.0.8",
|
"quote 1.0.9",
|
||||||
"syn 1.0.60",
|
"syn 1.0.60",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -11,3 +11,6 @@ thiserror = "1.0.23"
|
||||||
serde = { version = "1.0.123", features = ["derive"] }
|
serde = { version = "1.0.123", features = ["derive"] }
|
||||||
serde_yaml = "0.8.17"
|
serde_yaml = "0.8.17"
|
||||||
glob = "0.3.0"
|
glob = "0.3.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tempdir = "0.3.7"
|
20
espanso-config/src/config/macro_util.rs
Normal file
20
espanso-config/src/config/macro_util.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! merge {
|
||||||
|
( $t:ident, $child:expr, $parent:expr, $( $x:ident ),* ) => {
|
||||||
|
{
|
||||||
|
$(
|
||||||
|
if $child.$x.is_none() {
|
||||||
|
$child.$x = $parent.$x.clone();
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
|
||||||
|
// Build a temporary object to verify that all fields
|
||||||
|
// are being used at compile time
|
||||||
|
$t {
|
||||||
|
$(
|
||||||
|
$x: None,
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ use anyhow::Result;
|
||||||
|
|
||||||
mod yaml;
|
mod yaml;
|
||||||
mod path;
|
mod path;
|
||||||
|
mod macro_util;
|
||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub label: Option<String>,
|
pub label: Option<String>,
|
||||||
|
|
|
@ -3,7 +3,6 @@ use std::collections::HashSet;
|
||||||
use glob::glob;
|
use glob::glob;
|
||||||
use log::error;
|
use log::error;
|
||||||
|
|
||||||
// TODO: test
|
|
||||||
pub fn calculate_paths<'a>(glob_patterns: impl Iterator<Item = &'a String>) -> HashSet<String> {
|
pub fn calculate_paths<'a>(glob_patterns: impl Iterator<Item = &'a String>) -> HashSet<String> {
|
||||||
let mut path_set = HashSet::new();
|
let mut path_set = HashSet::new();
|
||||||
for glob_pattern in glob_patterns {
|
for glob_pattern in glob_patterns {
|
||||||
|
@ -15,9 +14,10 @@ pub fn calculate_paths<'a>(glob_patterns: impl Iterator<Item = &'a String>) -> H
|
||||||
Ok(path) => {
|
Ok(path) => {
|
||||||
path_set.insert(path.to_string_lossy().to_string());
|
path_set.insert(path.to_string_lossy().to_string());
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => error!(
|
||||||
error!("glob error when processing pattern: {}, with error: {}", glob_pattern, err)
|
"glob error when processing pattern: {}, with error: {}",
|
||||||
}
|
glob_pattern, err
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,3 +32,41 @@ pub fn calculate_paths<'a>(glob_patterns: impl Iterator<Item = &'a String>) -> H
|
||||||
|
|
||||||
path_set
|
path_set
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::fs::create_dir_all;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use tempdir::TempDir;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn calculate_paths_works_correctly() {
|
||||||
|
let dir = TempDir::new("tempconfig").unwrap();
|
||||||
|
let match_dir = dir.path().join("match");
|
||||||
|
create_dir_all(&match_dir).unwrap();
|
||||||
|
|
||||||
|
let sub_dir = match_dir.join("sub");
|
||||||
|
create_dir_all(&sub_dir).unwrap();
|
||||||
|
|
||||||
|
std::fs::write(match_dir.join("base.yml"), "test").unwrap();
|
||||||
|
std::fs::write(match_dir.join("another.yml"), "test").unwrap();
|
||||||
|
std::fs::write(match_dir.join("_sub.yml"), "test").unwrap();
|
||||||
|
std::fs::write(sub_dir.join("sub.yml"), "test").unwrap();
|
||||||
|
|
||||||
|
let result = calculate_paths(vec![
|
||||||
|
format!("{}/**/*.yml", dir.path().to_string_lossy()),
|
||||||
|
format!("{}/match/sub/*.yml", dir.path().to_string_lossy()),
|
||||||
|
// Invalid path
|
||||||
|
"invalid".to_string(),
|
||||||
|
].iter());
|
||||||
|
|
||||||
|
let mut expected = HashSet::new();
|
||||||
|
expected.insert(format!("{}/match/base.yml", dir.path().to_string_lossy()));
|
||||||
|
expected.insert(format!("{}/match/another.yml", dir.path().to_string_lossy()));
|
||||||
|
expected.insert(format!("{}/match/_sub.yml", dir.path().to_string_lossy()));
|
||||||
|
expected.insert(format!("{}/match/sub/sub.yml", dir.path().to_string_lossy()));
|
||||||
|
|
||||||
|
assert_eq!(result, expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,67 +1,125 @@
|
||||||
use std::collections::HashSet;
|
use anyhow::{private::kind::TraitKind, Result};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
|
use std::{collections::HashSet, convert::TryFrom};
|
||||||
|
|
||||||
|
use crate::{merge, util::is_yaml_empty};
|
||||||
|
|
||||||
use super::path::calculate_paths;
|
use super::path::calculate_paths;
|
||||||
|
|
||||||
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)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct YAMLConfig {
|
pub struct YAMLConfig {
|
||||||
|
#[serde(default)]
|
||||||
pub label: Option<String>,
|
pub label: Option<String>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
pub includes: Option<Vec<String>>,
|
pub includes: Option<Vec<String>>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
pub excludes: Option<Vec<String>>,
|
pub excludes: Option<Vec<String>>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
pub extra_includes: Option<Vec<String>>,
|
pub extra_includes: Option<Vec<String>>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
pub extra_excludes: Option<Vec<String>>,
|
pub extra_excludes: Option<Vec<String>>,
|
||||||
|
|
||||||
pub use_standard_includes: bool,
|
#[serde(default)]
|
||||||
|
pub use_standard_includes: Option<bool>,
|
||||||
|
|
||||||
// Filters
|
// Filters
|
||||||
|
#[serde(default)]
|
||||||
pub filter_title: Option<String>,
|
pub filter_title: Option<String>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
pub filter_class: Option<String>,
|
pub filter_class: Option<String>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
pub filter_exec: Option<String>,
|
pub filter_exec: Option<String>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
pub filter_os: Option<String>,
|
pub filter_os: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl YAMLConfig {
|
impl YAMLConfig {
|
||||||
// TODO test
|
pub fn parse_from_str(yaml: &str) -> Result<Self> {
|
||||||
|
// Because an empty string is not valid YAML but we want to support it anyway
|
||||||
|
if is_yaml_empty(yaml) {
|
||||||
|
return Ok(serde_yaml::from_str(
|
||||||
|
"arbitrary_field_that_will_not_block_the_parser: true",
|
||||||
|
)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(serde_yaml::from_str(yaml)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn merge_parent(&mut self, parent: &YAMLConfig) {
|
||||||
|
// Override the None fields with the parent's value
|
||||||
|
merge!(
|
||||||
|
YAMLConfig,
|
||||||
|
self,
|
||||||
|
parent,
|
||||||
|
|
||||||
|
// Fields
|
||||||
|
label,
|
||||||
|
includes,
|
||||||
|
excludes,
|
||||||
|
extra_includes,
|
||||||
|
extra_excludes,
|
||||||
|
use_standard_includes,
|
||||||
|
filter_title,
|
||||||
|
filter_class,
|
||||||
|
filter_exec,
|
||||||
|
filter_os
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn aggregate_includes(&self) -> HashSet<String> {
|
pub fn aggregate_includes(&self) -> HashSet<String> {
|
||||||
let mut includes = HashSet::new();
|
let mut includes = HashSet::new();
|
||||||
|
|
||||||
if self.use_standard_includes {
|
if self.use_standard_includes.is_none() || self.use_standard_includes.unwrap() {
|
||||||
STANDARD_INCLUDES.iter().for_each(|include| { includes.insert(include.to_string()); })
|
STANDARD_INCLUDES.iter().for_each(|include| {
|
||||||
|
includes.insert(include.to_string());
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(yaml_includes) = self.includes.as_ref() {
|
if let Some(yaml_includes) = self.includes.as_ref() {
|
||||||
yaml_includes.iter().for_each(|include| { includes.insert(include.to_string()); })
|
yaml_includes.iter().for_each(|include| {
|
||||||
|
includes.insert(include.to_string());
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(extra_includes) = self.extra_includes.as_ref() {
|
if let Some(extra_includes) = self.extra_includes.as_ref() {
|
||||||
extra_includes.iter().for_each(|include| { includes.insert(include.to_string()); })
|
extra_includes.iter().for_each(|include| {
|
||||||
|
includes.insert(include.to_string());
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
includes
|
includes
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO test
|
|
||||||
pub fn aggregate_excludes(&self) -> HashSet<String> {
|
pub fn aggregate_excludes(&self) -> HashSet<String> {
|
||||||
let mut excludes = HashSet::new();
|
let mut excludes = HashSet::new();
|
||||||
|
|
||||||
if self.use_standard_includes {
|
if self.use_standard_includes.is_none() || self.use_standard_includes.unwrap() {
|
||||||
STANDARD_EXCLUDES.iter().for_each(|exclude| { excludes.insert(exclude.to_string()); })
|
STANDARD_EXCLUDES.iter().for_each(|exclude| {
|
||||||
|
excludes.insert(exclude.to_string());
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(yaml_excludes) = self.excludes.as_ref() {
|
if let Some(yaml_excludes) = self.excludes.as_ref() {
|
||||||
yaml_excludes.iter().for_each(|exclude| { excludes.insert(exclude.to_string()); })
|
yaml_excludes.iter().for_each(|exclude| {
|
||||||
|
excludes.insert(exclude.to_string());
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(extra_excludes) = self.extra_excludes.as_ref() {
|
if let Some(extra_excludes) = self.extra_excludes.as_ref() {
|
||||||
extra_excludes.iter().for_each(|exclude| { excludes.insert(exclude.to_string()); })
|
extra_excludes.iter().for_each(|exclude| {
|
||||||
|
excludes.insert(exclude.to_string());
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
excludes
|
excludes
|
||||||
|
@ -69,9 +127,11 @@ impl YAMLConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: convert to TryFrom (check the matches section for an example)
|
// TODO: convert to TryFrom (check the matches section for an example)
|
||||||
impl From<YAMLConfig> for super::Config {
|
impl TryFrom<YAMLConfig> for super::Config {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
// TODO: test
|
// TODO: test
|
||||||
fn from(yaml_config: YAMLConfig) -> Self {
|
fn try_from(yaml_config: YAMLConfig) -> Result<Self, Self::Error> {
|
||||||
let includes = yaml_config.aggregate_includes();
|
let includes = yaml_config.aggregate_includes();
|
||||||
let excludes = yaml_config.aggregate_excludes();
|
let excludes = yaml_config.aggregate_excludes();
|
||||||
|
|
||||||
|
@ -79,11 +139,169 @@ impl From<YAMLConfig> for super::Config {
|
||||||
let exclude_paths = calculate_paths(excludes.iter());
|
let exclude_paths = calculate_paths(excludes.iter());
|
||||||
let include_paths = calculate_paths(includes.iter());
|
let include_paths = calculate_paths(includes.iter());
|
||||||
|
|
||||||
let match_files: Vec<String> = Vec::from_iter(include_paths.difference(&exclude_paths).cloned());
|
let match_files: Vec<String> =
|
||||||
|
Vec::from_iter(include_paths.difference(&exclude_paths).cloned());
|
||||||
|
|
||||||
Self {
|
Ok(Self {
|
||||||
label: yaml_config.label,
|
label: yaml_config.label,
|
||||||
match_files,
|
match_files,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::config::Config;
|
||||||
|
use std::iter::FromIterator;
|
||||||
|
use std::{convert::TryInto, fs::create_dir_all};
|
||||||
|
use tempdir::TempDir;
|
||||||
|
|
||||||
|
// fn create_config(yaml: &str) -> Result<Config> {
|
||||||
|
// let yaml_config: YAMLConfig = serde_yaml::from_str(yaml)?;
|
||||||
|
// let m: Config = yaml_config.try_into()?;
|
||||||
|
// Ok(m)
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn aggregate_includes_empty_config() {
|
||||||
|
assert_eq!(
|
||||||
|
YAMLConfig::parse_from_str("").unwrap().aggregate_includes(),
|
||||||
|
HashSet::from_iter(vec!["match/**/*.yml".to_string(),].iter().cloned())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn aggregate_includes_no_standard() {
|
||||||
|
assert_eq!(
|
||||||
|
YAMLConfig::parse_from_str("use_standard_includes: false").unwrap().aggregate_includes(),
|
||||||
|
HashSet::new()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn aggregate_includes_custom_includes() {
|
||||||
|
assert_eq!(
|
||||||
|
YAMLConfig::parse_from_str("includes: ['custom/*.yml']")
|
||||||
|
.unwrap()
|
||||||
|
.aggregate_includes(),
|
||||||
|
HashSet::from_iter(
|
||||||
|
vec!["match/**/*.yml".to_string(), "custom/*.yml".to_string()]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn aggregate_includes_extra_includes() {
|
||||||
|
assert_eq!(
|
||||||
|
YAMLConfig::parse_from_str("extra_includes: ['custom/*.yml']")
|
||||||
|
.unwrap()
|
||||||
|
.aggregate_includes(),
|
||||||
|
HashSet::from_iter(
|
||||||
|
vec!["match/**/*.yml".to_string(), "custom/*.yml".to_string()]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn aggregate_includes_includes_and_extra_includes() {
|
||||||
|
assert_eq!(
|
||||||
|
YAMLConfig::parse_from_str("includes: ['sub/*.yml']\nextra_includes: ['custom/*.yml']")
|
||||||
|
.unwrap()
|
||||||
|
.aggregate_includes(),
|
||||||
|
HashSet::from_iter(
|
||||||
|
vec!["match/**/*.yml".to_string(), "custom/*.yml".to_string(), "sub/*.yml".to_string()]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn aggregate_excludes_empty_config() {
|
||||||
|
assert_eq!(
|
||||||
|
YAMLConfig::parse_from_str("").unwrap().aggregate_excludes(),
|
||||||
|
HashSet::from_iter(vec!["match/**/_*.yml".to_string(),].iter().cloned())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn aggregate_excludes_no_standard() {
|
||||||
|
assert_eq!(
|
||||||
|
YAMLConfig::parse_from_str("use_standard_includes: false").unwrap().aggregate_excludes(),
|
||||||
|
HashSet::new()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn aggregate_excludes_custom_excludes() {
|
||||||
|
assert_eq!(
|
||||||
|
YAMLConfig::parse_from_str("excludes: ['custom/*.yml']")
|
||||||
|
.unwrap()
|
||||||
|
.aggregate_excludes(),
|
||||||
|
HashSet::from_iter(
|
||||||
|
vec!["match/**/_*.yml".to_string(), "custom/*.yml".to_string()]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn aggregate_excludes_extra_excludes() {
|
||||||
|
assert_eq!(
|
||||||
|
YAMLConfig::parse_from_str("extra_excludes: ['custom/*.yml']")
|
||||||
|
.unwrap()
|
||||||
|
.aggregate_excludes(),
|
||||||
|
HashSet::from_iter(
|
||||||
|
vec!["match/**/_*.yml".to_string(), "custom/*.yml".to_string()]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn aggregate_excludes_excludes_and_extra_excludes() {
|
||||||
|
assert_eq!(
|
||||||
|
YAMLConfig::parse_from_str("excludes: ['sub/*.yml']\nextra_excludes: ['custom/*.yml']")
|
||||||
|
.unwrap()
|
||||||
|
.aggregate_excludes(),
|
||||||
|
HashSet::from_iter(
|
||||||
|
vec!["match/**/_*.yml".to_string(), "custom/*.yml".to_string(), "sub/*.yml".to_string()]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn merge_parent_field_parent_fallback() {
|
||||||
|
let parent =
|
||||||
|
YAMLConfig::parse_from_str("use_standard_includes: false").unwrap();
|
||||||
|
let mut child =
|
||||||
|
YAMLConfig::parse_from_str("").unwrap();
|
||||||
|
assert_eq!(child.use_standard_includes, None);
|
||||||
|
|
||||||
|
child.merge_parent(&parent);
|
||||||
|
assert_eq!(child.use_standard_includes, Some(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn merge_parent_field_child_overwrite_parent() {
|
||||||
|
let parent =
|
||||||
|
YAMLConfig::parse_from_str("use_standard_includes: true").unwrap();
|
||||||
|
let mut child =
|
||||||
|
YAMLConfig::parse_from_str("use_standard_includes: false").unwrap();
|
||||||
|
assert_eq!(child.use_standard_includes, Some(false));
|
||||||
|
|
||||||
|
child.merge_parent(&parent);
|
||||||
|
assert_eq!(child.use_standard_includes, Some(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test conversion to Config (we need to test that the file match resolution works)
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
mod util;
|
||||||
mod config;
|
mod config;
|
||||||
mod matches;
|
mod matches;
|
||||||
|
|
||||||
|
|
38
espanso-config/src/util.rs
Normal file
38
espanso-config/src/util.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/// Check if the given string represents an empty YAML.
|
||||||
|
/// In other words, it checks if the document is only composed
|
||||||
|
/// of spaces and/or comments
|
||||||
|
pub fn is_yaml_empty(yaml: &str) -> bool {
|
||||||
|
for line in yaml.lines() {
|
||||||
|
let trimmed_line = line.trim();
|
||||||
|
if !trimmed_line.starts_with("#") && !trimmed_line.is_empty() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_yaml_empty_document_empty() {
|
||||||
|
assert_eq!(is_yaml_empty(""), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_yaml_empty_document_with_comments() {
|
||||||
|
assert_eq!(is_yaml_empty("\n#comment \n \n"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_yaml_empty_document_with_comments_and_content() {
|
||||||
|
assert_eq!(is_yaml_empty("\n#comment \n field: true\n"), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_yaml_empty_document_with_content() {
|
||||||
|
assert_eq!(is_yaml_empty("\nfield: true\n"), false);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user