use super::canonical;
pub const ERR_NAME_CONFLICT: &str = "ERR_NAME_CONFLICT:";
fn norm_key(s: &str) -> String {
canonical(s).join("")
}
fn is_system_reserved(name: &str) -> bool {
name == "_id"
}
pub fn detect_conflicts<'a, I>(names: I) -> Result<(), String>
where
I: IntoIterator<Item = &'a str>,
{
let mut seen: Vec<(String, &'a str)> = Vec::new();
for name in names {
if is_system_reserved(name) {
continue;
}
let key = norm_key(name);
if key.is_empty() {
continue;
}
if let Some((_, prev)) = seen.iter().find(|(k, _)| k == &key) {
return Err(format!(
"{ERR_NAME_CONFLICT}名称 \"{name}\" 与 \"{prev}\" 归一后相同({key})"
));
}
seen.push((key, name));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn duplicate_after_normalize_errors() {
assert!(detect_conflicts(["orderTotal", "order_total"]).is_err());
assert!(detect_conflicts(["orderTotal", "orderItem"]).is_ok());
assert!(detect_conflicts(["orderTotal"]).is_ok());
}
#[test]
fn contract_keys_are_valid_field_names() {
assert!(detect_conflicts(["type"]).is_ok());
assert!(detect_conflicts([
"type", "required", "default", "read", "write", "fields", "strategy", "fnRef"
])
.is_ok());
assert!(detect_conflicts(["fn_ref", "localField", "foreignField"]).is_ok());
}
#[test]
fn system_reserved_exempt() {
assert!(detect_conflicts(["_id", "id"]).is_ok());
}
#[test]
fn error_text_names_both() {
let e = detect_conflicts(["orderTotal", "order_total"]).unwrap_err();
assert!(e.contains("orderTotal") && e.contains("order_total"));
}
}