1use dyn_fmt::AsStrFormatExt;
16use regex::Regex;
17use snafu::{ensure, OptionExt};
18use yaml_rust::Yaml;
19
20use crate::error::{
21 Error, InvalidTableSuffixTemplateSnafu, RequiredTableSuffixTemplateSnafu, Result,
22};
23use crate::{PipelineMap, Value};
24
25const REPLACE_KEY: &str = "{}";
26
27lazy_static::lazy_static! {
28 static ref NAME_TPL: Regex = Regex::new(r"\$\{([^}]+)\}").unwrap();
29}
30
31#[derive(Debug, PartialEq)]
44pub(crate) struct TableSuffixTemplate {
45 pub template: String,
46 pub keys: Vec<String>,
47}
48
49impl TableSuffixTemplate {
50 pub fn apply(&self, val: &PipelineMap) -> Option<String> {
51 let values = self
52 .keys
53 .iter()
54 .filter_map(|key| {
55 let v = val.get(key)?;
56 match v {
57 Value::Int8(v) => Some(v.to_string()),
58 Value::Int16(v) => Some(v.to_string()),
59 Value::Int32(v) => Some(v.to_string()),
60 Value::Int64(v) => Some(v.to_string()),
61 Value::Uint8(v) => Some(v.to_string()),
62 Value::Uint16(v) => Some(v.to_string()),
63 Value::Uint32(v) => Some(v.to_string()),
64 Value::Uint64(v) => Some(v.to_string()),
65 Value::String(v) => Some(v.clone()),
66 _ => None,
67 }
68 })
69 .collect::<Vec<_>>();
70 if values.len() != self.keys.len() {
71 return None;
72 }
73 Some(self.template.format(&values))
74 }
75}
76
77impl TryFrom<&Yaml> for TableSuffixTemplate {
78 type Error = Error;
79
80 fn try_from(value: &Yaml) -> Result<Self> {
81 let name_template = value
82 .as_str()
83 .context(RequiredTableSuffixTemplateSnafu)?
84 .to_string();
85
86 let mut keys = Vec::new();
87 for cap in NAME_TPL.captures_iter(&name_template) {
88 ensure!(
89 cap.len() >= 2,
90 InvalidTableSuffixTemplateSnafu {
91 input: name_template.clone(),
92 }
93 );
94 let key = cap[1].trim().to_string();
95 keys.push(key);
96 }
97
98 let template = NAME_TPL
99 .replace_all(&name_template, REPLACE_KEY)
100 .to_string();
101
102 Ok(TableSuffixTemplate { template, keys })
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use yaml_rust::YamlLoader;
109
110 use crate::tablesuffix::TableSuffixTemplate;
111
112 #[test]
113 fn test_table_suffix_parsing() {
114 let yaml = r#"
115 table_suffix: _${xxx}_${b}
116 "#;
117 let config = YamlLoader::load_from_str(yaml);
118 assert!(config.is_ok());
119 let config = config.unwrap()[0]["table_suffix"].clone();
120 let name_template = TableSuffixTemplate::try_from(&config);
121 assert!(name_template.is_ok());
122 let name_template = name_template.unwrap();
123 assert_eq!(name_template.template, "_{}_{}");
124 assert_eq!(name_template.keys, vec!["xxx", "b"]);
125 }
126}