use anyhow::{anyhow, Result};
#[derive(Debug, Clone)]
pub struct ConversionRule {
pub type_field: String,
pub type_value: String,
pub id_field: String,
pub target_table: String,
}
impl ConversionRule {
pub fn parse(rule_str: &str) -> Result<Self> {
let parts: Vec<&str> = rule_str.splitn(3, ',').collect();
if parts.len() < 2 {
return Err(anyhow!(
"Invalid rule format. Expected: 'type=\"value\",id_field table:id_field'"
));
}
let type_part = parts[0].trim();
let remainder = parts[1].trim();
let remainder_parts: Vec<&str> = remainder.splitn(2, ' ').collect();
if remainder_parts.len() != 2 {
return Err(anyhow!(
"Invalid rule format. Expected: 'type=\"value\",id_field table:id_field'"
));
}
let id_field = remainder_parts[0].trim();
let target_part = remainder_parts[1].trim();
if !type_part.starts_with("type=\"") || !type_part.ends_with('"') {
return Err(anyhow!(
"Invalid type specification. Expected: type=\"value\""
));
}
let type_value = type_part[6..type_part.len() - 1].to_string();
let target_parts: Vec<&str> = target_part.split(':').collect();
if target_parts.len() != 2 {
return Err(anyhow!(
"Invalid target specification. Expected: table:id_field"
));
}
let target_table = target_parts[0].to_string();
let target_id_field = target_parts[1].to_string();
if id_field != target_id_field {
return Err(anyhow!(
"ID field mismatch: {id_field} != {target_id_field}"
));
}
Ok(ConversionRule {
type_field: "type".to_string(),
type_value,
id_field: id_field.to_string(),
target_table,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_valid_rule() {
let rule_str = r#"type="user",user_id users:user_id"#;
let rule = ConversionRule::parse(rule_str).unwrap();
assert_eq!(rule.type_field, "type");
assert_eq!(rule.type_value, "user");
assert_eq!(rule.id_field, "user_id");
assert_eq!(rule.target_table, "users");
}
#[test]
fn test_parse_with_extra_whitespace() {
let rule_str = r#" type="user" , user_id users:user_id "#;
let rule = ConversionRule::parse(rule_str).unwrap();
assert_eq!(rule.type_field, "type");
assert_eq!(rule.type_value, "user");
assert_eq!(rule.id_field, "user_id");
assert_eq!(rule.target_table, "users");
}
#[test]
fn test_parse_different_type_values() {
let test_cases = vec![
(
r#"type="organization",org_id organizations:org_id"#,
"organization",
"org_id",
"organizations",
),
(
r#"type="product",product_id products:product_id"#,
"product",
"product_id",
"products",
),
(
r#"type="admin_user",id admins:id"#,
"admin_user",
"id",
"admins",
),
];
for (rule_str, expected_type, expected_id, expected_table) in test_cases {
let rule = ConversionRule::parse(rule_str).unwrap();
assert_eq!(rule.type_value, expected_type);
assert_eq!(rule.id_field, expected_id);
assert_eq!(rule.target_table, expected_table);
}
}
#[test]
fn test_parse_missing_comma() {
let rule_str = r#"type="user" user_id users:user_id"#;
let result = ConversionRule::parse(rule_str);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Invalid rule format"));
}
#[test]
fn test_parse_missing_space() {
let rule_str = r#"type="user",user_idusers:user_id"#;
let result = ConversionRule::parse(rule_str);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Invalid rule format"));
}
#[test]
fn test_parse_invalid_type_format_no_quotes() {
let rule_str = r#"type=user,user_id users:user_id"#;
let result = ConversionRule::parse(rule_str);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Invalid type specification"));
}
#[test]
fn test_parse_invalid_type_format_missing_opening_quote() {
let rule_str = r#"type=user",user_id users:user_id"#;
let result = ConversionRule::parse(rule_str);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Invalid type specification"));
}
#[test]
fn test_parse_invalid_type_format_missing_closing_quote() {
let rule_str = r#"type="user,user_id users:user_id"#;
let result = ConversionRule::parse(rule_str);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Invalid type specification"));
}
#[test]
fn test_parse_missing_colon_in_target() {
let rule_str = r#"type="user",user_id users_user_id"#;
let result = ConversionRule::parse(rule_str);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Invalid target specification"));
}
#[test]
fn test_parse_too_many_colons_in_target() {
let rule_str = r#"type="user",user_id users:extra:user_id"#;
let result = ConversionRule::parse(rule_str);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Invalid target specification"));
}
#[test]
fn test_parse_id_field_mismatch() {
let rule_str = r#"type="user",user_id users:different_id"#;
let result = ConversionRule::parse(rule_str);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("ID field mismatch"));
assert!(err_msg.contains("user_id"));
assert!(err_msg.contains("different_id"));
}
#[test]
fn test_parse_empty_string() {
let rule_str = "";
let result = ConversionRule::parse(rule_str);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Invalid rule format"));
}
#[test]
fn test_parse_only_type() {
let rule_str = r#"type="user""#;
let result = ConversionRule::parse(rule_str);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Invalid rule format"));
}
#[test]
fn test_parse_empty_type_value() {
let rule_str = r#"type="",user_id users:user_id"#;
let rule = ConversionRule::parse(rule_str).unwrap();
assert_eq!(rule.type_value, "");
assert_eq!(rule.id_field, "user_id");
assert_eq!(rule.target_table, "users");
}
#[test]
fn test_parse_type_value_with_spaces() {
let rule_str = r#"type="admin user",admin_id admins:admin_id"#;
let rule = ConversionRule::parse(rule_str).unwrap();
assert_eq!(rule.type_value, "admin user");
assert_eq!(rule.id_field, "admin_id");
assert_eq!(rule.target_table, "admins");
}
#[test]
fn test_parse_type_value_with_special_chars() {
let rule_str = r#"type="user-profile_v2",id profiles:id"#;
let rule = ConversionRule::parse(rule_str).unwrap();
assert_eq!(rule.type_value, "user-profile_v2");
assert_eq!(rule.id_field, "id");
assert_eq!(rule.target_table, "profiles");
}
}