use strfmt::{strfmt_map, FmtError, Formatter};
use regex::{Captures, Regex};
use crate::{Row, Headers, error};
#[derive(Debug)]
pub enum ColSpec {
Regex {
source: String,
colname: String,
coldef: String,
regex: Regex,
},
Mix { colname: String, coldef: String },
}
fn interpolate(template: &str, captures: &Captures) -> String {
let mut res = String::new();
captures.expand(template, &mut res);
res
}
impl ColSpec {
pub fn compute(&self, data: &Row, headers: &Headers) -> error::Result<String> {
match *self {
ColSpec::Mix { ref coldef, .. } => match strfmt_map(coldef, &|mut fmt: Formatter| {
let v = match headers.get_field(data, fmt.key) {
Some(v) => v,
None => {
return Err(FmtError::KeyError(fmt.key.to_string()));
}
};
fmt.str(v)
}) {
Ok(s) => Ok(s),
Err(FmtError::Invalid(f)) => Err(error::Error::InvalidFormat(f)),
Err(FmtError::KeyError(s)) => Err(error::Error::ColumnNotFound(s)),
Err(FmtError::TypeError(s)) => Err(error::Error::InvalidFormat(s)),
},
ColSpec::Regex {
ref source,
ref coldef,
ref regex,
..
} => match headers.get_field(data, source) {
Some(field) => match regex.captures(field) {
Some(captures) => Ok(interpolate(coldef, &captures)),
None => Err(error::Error::ReNoMatch(regex.to_string(), field.to_string())),
},
None => Err(error::Error::ColumnNotFound(source.to_string())),
},
}
}
}
#[cfg(test)]
mod tests {
use regex::Regex;
use crate::Row;
use super::{interpolate, ColSpec};
#[test]
fn test_colspec_simplest() {
let c: ColSpec = ColSpec::Mix {
colname: "new".to_string(),
coldef: "value".to_string(),
};
let data = Row::new();
assert_eq!(
c.compute(&data, &Row::from(vec!["a"]).into())
.unwrap(),
"value",
);
}
#[test]
fn test_colspec_regex_source() {
let c = ColSpec::Regex {
source: "_source".to_string(),
colname: "new".to_string(),
coldef: "${number}".to_string(),
regex: Regex::new("a(?P<number>[0-9]+)m").unwrap(),
};
let data = Row::from(vec!["a20m"]);
assert_eq!(
c.compute(&data, &Row::from(vec!["_source"]).into())
.unwrap(),
"20",
);
}
#[test]
fn test_colspec_mix() {
let c = ColSpec::Mix {
colname: "new".to_string(),
coldef: "{a}-{b}".to_string(),
};
let data = Row::from(vec!["2", "4"]);
assert_eq!(
c.compute(&data, &Row::from(vec!["a", "b"]).into())
.unwrap(),
"2-4",
);
}
#[test]
fn test_interpolate() {
let regex =
Regex::new(r"(?P<year>[0-9]{4})-(?P<month>[0-9]{2})-(?P<day>[0-9]{2})").unwrap();
let captures = regex.captures("2019-02-09").unwrap();
let template = String::from("Día: ${day} mes: ${month} año: ${year}");
assert_eq!(
interpolate(&template, &captures),
"Día: 09 mes: 02 año: 2019"
);
}
}