feat(config): add id to config and decouple variable params from serde_yaml
This commit is contained in:
		
							parent
							
								
									e643609d57
								
							
						
					
					
						commit
						f847d0cd81
					
				| 
						 | 
				
			
			@ -15,6 +15,8 @@ regex = "1.4.3"
 | 
			
		|||
lazy_static = "1.4.0"
 | 
			
		||||
dunce = "1.0.1"
 | 
			
		||||
walkdir = "2.3.1"
 | 
			
		||||
enum-as-inner = "0.3.3"
 | 
			
		||||
ordered-float = "2.0"
 | 
			
		||||
 | 
			
		||||
[dev-dependencies]
 | 
			
		||||
tempdir = "0.3.7"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,6 +28,7 @@ mod util;
 | 
			
		|||
pub(crate) mod store;
 | 
			
		||||
 | 
			
		||||
pub trait Config {
 | 
			
		||||
  fn id(&self) -> i32;
 | 
			
		||||
  fn label(&self) -> &str;
 | 
			
		||||
  fn match_paths(&self) -> &[String];
 | 
			
		||||
  fn backend(&self) -> Backend;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,7 +18,7 @@
 | 
			
		|||
 */
 | 
			
		||||
 | 
			
		||||
use super::{AppProperties, Backend, Config, parse::ParsedConfig, path::calculate_paths, util::os_matches};
 | 
			
		||||
use crate::merge;
 | 
			
		||||
use crate::{counter::next_id, merge};
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use log::error;
 | 
			
		||||
use regex::Regex;
 | 
			
		||||
| 
						 | 
				
			
			@ -34,6 +34,7 @@ pub(crate) struct ResolvedConfig {
 | 
			
		|||
  parsed: ParsedConfig,
 | 
			
		||||
 | 
			
		||||
  // Generated properties
 | 
			
		||||
  id: i32,
 | 
			
		||||
  match_paths: Vec<String>,
 | 
			
		||||
 | 
			
		||||
  filter_title: Option<Regex>,
 | 
			
		||||
| 
						 | 
				
			
			@ -45,6 +46,7 @@ impl Default for ResolvedConfig {
 | 
			
		|||
  fn default() -> Self {
 | 
			
		||||
    Self {
 | 
			
		||||
      parsed: Default::default(),
 | 
			
		||||
      id: 0,
 | 
			
		||||
      match_paths: Vec::new(),
 | 
			
		||||
      filter_title: None,
 | 
			
		||||
      filter_class: None,
 | 
			
		||||
| 
						 | 
				
			
			@ -54,6 +56,10 @@ impl Default for ResolvedConfig {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
impl Config for ResolvedConfig {
 | 
			
		||||
  fn id(&self) -> i32 {
 | 
			
		||||
    self.id
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fn label(&self) -> &str {
 | 
			
		||||
    self.parsed.label.as_deref().unwrap_or("none")
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -162,6 +168,7 @@ impl ResolvedConfig {
 | 
			
		|||
 | 
			
		||||
    Ok(Self {
 | 
			
		||||
      parsed: config,
 | 
			
		||||
      id: next_id(),
 | 
			
		||||
      match_paths,
 | 
			
		||||
      filter_title,
 | 
			
		||||
      filter_class,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -134,6 +134,10 @@ mod tests {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  impl Config for MockConfig {
 | 
			
		||||
    fn id(&self) -> i32 {
 | 
			
		||||
      0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn label(&self) -> &str {
 | 
			
		||||
      &self.label
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,7 @@
 | 
			
		|||
use std::sync::atomic::{AtomicI32, Ordering};
 | 
			
		||||
 | 
			
		||||
thread_local! {
 | 
			
		||||
  // TODO: if thread local, we probably don't need an atomic
 | 
			
		||||
  static STRUCT_COUNTER: AtomicI32 = AtomicI32::new(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,7 @@ use crate::{
 | 
			
		|||
    store::{MatchSet, MatchStore},
 | 
			
		||||
    Match, Variable,
 | 
			
		||||
  },
 | 
			
		||||
  counter::next_id,
 | 
			
		||||
};
 | 
			
		||||
use std::convert::TryInto;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -137,6 +138,8 @@ struct LegacyInteropConfig {
 | 
			
		|||
  pub name: String,
 | 
			
		||||
  match_paths: Vec<String>,
 | 
			
		||||
 | 
			
		||||
  id: i32,
 | 
			
		||||
 | 
			
		||||
  config: LegacyConfig,
 | 
			
		||||
 | 
			
		||||
  filter_title: Option<Regex>,
 | 
			
		||||
| 
						 | 
				
			
			@ -147,6 +150,7 @@ struct LegacyInteropConfig {
 | 
			
		|||
impl From<config::LegacyConfig> for LegacyInteropConfig {
 | 
			
		||||
  fn from(config: config::LegacyConfig) -> Self {
 | 
			
		||||
    Self {
 | 
			
		||||
      id: next_id(),
 | 
			
		||||
      config: config.clone(),
 | 
			
		||||
      name: config.name.clone(),
 | 
			
		||||
      match_paths: vec![config.name],
 | 
			
		||||
| 
						 | 
				
			
			@ -170,6 +174,10 @@ impl From<config::LegacyConfig> for LegacyInteropConfig {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
impl Config for LegacyInteropConfig {
 | 
			
		||||
  fn id(&self) -> i32 {
 | 
			
		||||
    self.id
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fn label(&self) -> &str {
 | 
			
		||||
    &self.config.name
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,21 +17,25 @@
 | 
			
		|||
 * along with espanso.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use crate::{counter::next_id, matches::{
 | 
			
		||||
  group::{path::resolve_imports, MatchGroup},
 | 
			
		||||
  Match, Variable,
 | 
			
		||||
}};
 | 
			
		||||
use crate::{
 | 
			
		||||
  counter::next_id,
 | 
			
		||||
  matches::{
 | 
			
		||||
    group::{path::resolve_imports, MatchGroup},
 | 
			
		||||
    Match, Variable,
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use log::warn;
 | 
			
		||||
use parse::YAMLMatchGroup;
 | 
			
		||||
use std::convert::{TryFrom, TryInto};
 | 
			
		||||
 | 
			
		||||
use self::parse::{YAMLMatch, YAMLVariable};
 | 
			
		||||
use self::{parse::{YAMLMatch, YAMLVariable}, util::convert_params};
 | 
			
		||||
use crate::matches::{MatchCause, MatchEffect, TextEffect, TriggerCause};
 | 
			
		||||
 | 
			
		||||
use super::Importer;
 | 
			
		||||
 | 
			
		||||
pub(crate) mod parse;
 | 
			
		||||
mod util;
 | 
			
		||||
 | 
			
		||||
pub(crate) struct YAMLImporter {}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -150,7 +154,7 @@ impl TryFrom<YAMLVariable> for Variable {
 | 
			
		|||
    Ok(Self {
 | 
			
		||||
      name: yaml_var.name,
 | 
			
		||||
      var_type: yaml_var.var_type,
 | 
			
		||||
      params: yaml_var.params,
 | 
			
		||||
      params: convert_params(yaml_var.params)?,
 | 
			
		||||
      id: next_id(),
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -159,8 +163,7 @@ impl TryFrom<YAMLVariable> for Variable {
 | 
			
		|||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
  use super::*;
 | 
			
		||||
  use crate::{matches::Match, util::tests::use_test_directory};
 | 
			
		||||
  use serde_yaml::{Mapping, Value};
 | 
			
		||||
  use crate::{matches::{Match, Params, Value}, util::tests::use_test_directory};
 | 
			
		||||
  use std::fs::create_dir_all;
 | 
			
		||||
 | 
			
		||||
  fn create_match(yaml: &str) -> Result<Match> {
 | 
			
		||||
| 
						 | 
				
			
			@ -172,7 +175,7 @@ mod tests {
 | 
			
		|||
    if let MatchEffect::Text(e) = &mut m.effect {
 | 
			
		||||
      e.vars.iter_mut().for_each(|v| v.id = 0);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    Ok(m)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -331,8 +334,8 @@ mod tests {
 | 
			
		|||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn vars_maps_correctly() {
 | 
			
		||||
    let mut params = Mapping::new();
 | 
			
		||||
    params.insert(Value::String("param1".to_string()), Value::Bool(true));
 | 
			
		||||
    let mut params = Params::new();
 | 
			
		||||
    params.insert("param1".to_string(), Value::Bool(true));
 | 
			
		||||
    let vars = vec![Variable {
 | 
			
		||||
      name: "var1".to_string(),
 | 
			
		||||
      var_type: "test".to_string(),
 | 
			
		||||
| 
						 | 
				
			
			@ -371,7 +374,7 @@ mod tests {
 | 
			
		|||
    let vars = vec![Variable {
 | 
			
		||||
      name: "var1".to_string(),
 | 
			
		||||
      var_type: "test".to_string(),
 | 
			
		||||
      params: Mapping::new(),
 | 
			
		||||
      params: Params::new(),
 | 
			
		||||
      ..Default::default()
 | 
			
		||||
    }];
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
| 
						 | 
				
			
			@ -444,7 +447,7 @@ mod tests {
 | 
			
		|||
      let vars = vec![Variable {
 | 
			
		||||
        name: "var1".to_string(),
 | 
			
		||||
        var_type: "test".to_string(),
 | 
			
		||||
        params: Mapping::new(),
 | 
			
		||||
        params: Params::new(),
 | 
			
		||||
        ..Default::default()
 | 
			
		||||
      }];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										167
									
								
								espanso-config/src/matches/group/loader/yaml/util.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								espanso-config/src/matches/group/loader/yaml/util.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,167 @@
 | 
			
		|||
/*
 | 
			
		||||
 * This file is part of espanso.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2019-2021 Federico Terzi
 | 
			
		||||
 *
 | 
			
		||||
 * espanso is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * espanso is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with espanso.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use std::convert::TryInto;
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use serde_yaml::Mapping;
 | 
			
		||||
use thiserror::Error;
 | 
			
		||||
 | 
			
		||||
use crate::matches::{Number, Params, Value};
 | 
			
		||||
 | 
			
		||||
pub(crate) fn convert_params(m: Mapping) -> Result<Params> {
 | 
			
		||||
  let mut params = Params::new();
 | 
			
		||||
 | 
			
		||||
  for (key, value) in m {
 | 
			
		||||
    let key = key.as_str().ok_or(ConversionError::InvalidKeyFormat)?;
 | 
			
		||||
    let value = convert_value(value)?;
 | 
			
		||||
    params.insert(key.to_owned(), value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Ok(params)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn convert_value(value: serde_yaml::Value) -> Result<Value> {
 | 
			
		||||
  Ok(match value {
 | 
			
		||||
    serde_yaml::Value::Null => Value::Null,
 | 
			
		||||
    serde_yaml::Value::Bool(val) => Value::Bool(val),
 | 
			
		||||
    serde_yaml::Value::Number(n) => {
 | 
			
		||||
      if n.is_i64() {
 | 
			
		||||
        Value::Number(Number::Integer(
 | 
			
		||||
          n.as_i64().ok_or(ConversionError::InvalidNumberFormat)?,
 | 
			
		||||
        ))
 | 
			
		||||
      } else if n.is_u64() {
 | 
			
		||||
        Value::Number(Number::Integer(
 | 
			
		||||
          n.as_u64()
 | 
			
		||||
            .ok_or(ConversionError::InvalidNumberFormat)?
 | 
			
		||||
            .try_into()?,
 | 
			
		||||
        ))
 | 
			
		||||
      } else if n.is_f64() {
 | 
			
		||||
        Value::Number(Number::Float(
 | 
			
		||||
          n.as_f64()
 | 
			
		||||
            .ok_or(ConversionError::InvalidNumberFormat)?
 | 
			
		||||
            .into(),
 | 
			
		||||
        ))
 | 
			
		||||
      } else {
 | 
			
		||||
        return Err(ConversionError::InvalidNumberFormat.into());
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    serde_yaml::Value::String(s) => Value::String(s),
 | 
			
		||||
    serde_yaml::Value::Sequence(arr) => Value::Array(
 | 
			
		||||
      arr
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .map(convert_value)
 | 
			
		||||
        .collect::<Result<Vec<Value>>>()?,
 | 
			
		||||
    ),
 | 
			
		||||
    serde_yaml::Value::Mapping(m) => Value::Object(convert_params(m)?),
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Error, Debug)]
 | 
			
		||||
pub enum ConversionError {
 | 
			
		||||
  #[error("invalid key format")]
 | 
			
		||||
  InvalidKeyFormat,
 | 
			
		||||
 | 
			
		||||
  #[error("invalid number format")]
 | 
			
		||||
  InvalidNumberFormat,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
  use super::*;
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn convert_value_null() {
 | 
			
		||||
    assert_eq!(convert_value(serde_yaml::Value::Null).unwrap(), Value::Null);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn convert_value_bool() {
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
      convert_value(serde_yaml::Value::Bool(true)).unwrap(),
 | 
			
		||||
      Value::Bool(true)
 | 
			
		||||
    );
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
      convert_value(serde_yaml::Value::Bool(false)).unwrap(),
 | 
			
		||||
      Value::Bool(false)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn convert_value_number() {
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
      convert_value(serde_yaml::Value::Number(0.into())).unwrap(),
 | 
			
		||||
      Value::Number(Number::Integer(0))
 | 
			
		||||
    );
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
      convert_value(serde_yaml::Value::Number((-100).into())).unwrap(),
 | 
			
		||||
      Value::Number(Number::Integer(-100))
 | 
			
		||||
    );
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
      convert_value(serde_yaml::Value::Number(1.5.into())).unwrap(),
 | 
			
		||||
      Value::Number(Number::Float(1.5.into()))
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn convert_value_string() {
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
      convert_value(serde_yaml::Value::String("hello".to_string())).unwrap(),
 | 
			
		||||
      Value::String("hello".to_string())
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn convert_value_array() {
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
      convert_value(serde_yaml::Value::Sequence(vec![
 | 
			
		||||
        serde_yaml::Value::Bool(true),
 | 
			
		||||
        serde_yaml::Value::Null,
 | 
			
		||||
      ]))
 | 
			
		||||
      .unwrap(),
 | 
			
		||||
      Value::Array(vec![Value::Bool(true), Value::Null,])
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn convert_value_params() {
 | 
			
		||||
    let mut mapping = serde_yaml::Mapping::new();
 | 
			
		||||
    mapping.insert(serde_yaml::Value::String("test".to_string()), serde_yaml::Value::Null);
 | 
			
		||||
 | 
			
		||||
    let mut expected = Params::new();
 | 
			
		||||
    expected.insert("test".to_string(), Value::Null);
 | 
			
		||||
    assert_eq!(convert_value(serde_yaml::Value::Mapping(mapping)).unwrap(), Value::Object(expected));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn convert_params_works_correctly() {
 | 
			
		||||
    let mut mapping = serde_yaml::Mapping::new();
 | 
			
		||||
    mapping.insert(serde_yaml::Value::String("test".to_string()), serde_yaml::Value::Null);
 | 
			
		||||
 | 
			
		||||
    let mut expected = Params::new();
 | 
			
		||||
    expected.insert("test".to_string(), Value::Null);
 | 
			
		||||
    assert_eq!(convert_params(mapping).unwrap(), expected);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #[test]
 | 
			
		||||
  fn convert_params_invalid_key_type() {
 | 
			
		||||
    let mut mapping = serde_yaml::Mapping::new();
 | 
			
		||||
    mapping.insert(serde_yaml::Value::Null, serde_yaml::Value::Null);
 | 
			
		||||
 | 
			
		||||
    assert!(convert_params(mapping).is_err());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -17,7 +17,9 @@
 | 
			
		|||
 * along with espanso.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use serde_yaml::Mapping;
 | 
			
		||||
use std::collections::{BTreeMap};
 | 
			
		||||
use enum_as_inner::EnumAsInner;
 | 
			
		||||
use ordered_float::OrderedFloat;
 | 
			
		||||
 | 
			
		||||
use crate::counter::{StructId};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -107,7 +109,7 @@ pub struct Variable {
 | 
			
		|||
  pub id: StructId,
 | 
			
		||||
  pub name: String,
 | 
			
		||||
  pub var_type: String,
 | 
			
		||||
  pub params: Mapping,
 | 
			
		||||
  pub params: Params,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for Variable {
 | 
			
		||||
| 
						 | 
				
			
			@ -116,7 +118,25 @@ impl Default for Variable {
 | 
			
		|||
      id: 0,
 | 
			
		||||
      name: String::new(),
 | 
			
		||||
      var_type: String::new(),
 | 
			
		||||
      params: Mapping::new(),
 | 
			
		||||
      params: Params::new(),
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub type Params = BTreeMap<String, Value>;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, EnumAsInner)]
 | 
			
		||||
pub enum Value {
 | 
			
		||||
  Null,
 | 
			
		||||
  Bool(bool),
 | 
			
		||||
  Number(Number),
 | 
			
		||||
  String(String),
 | 
			
		||||
  Array(Vec<Value>),
 | 
			
		||||
  Object(Params),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
 | 
			
		||||
pub enum Number {
 | 
			
		||||
  Integer(i64),
 | 
			
		||||
  Float(OrderedFloat<f64>),
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user