#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ShapePrefix {
NormalTable,
Heatmap,
Transposed,
Delta,
ColorCoded,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum TableType {
Normal, Heatmap, Transposed, }
pub fn classify_shape_name(name: &str) -> Option<ShapePrefix> {
if name.contains("ntbl_") {
Some(ShapePrefix::NormalTable)
} else if name.contains("htmp_") {
Some(ShapePrefix::Heatmap)
} else if name.contains("trns_") {
Some(ShapePrefix::Transposed)
} else if name.contains("delt_") {
Some(ShapePrefix::Delta)
} else if name.contains("_ccst") {
Some(ShapePrefix::ColorCoded)
} else {
None
}
}
pub fn prefix_to_table_type(prefix: ShapePrefix) -> Option<TableType> {
match prefix {
ShapePrefix::NormalTable => Some(TableType::Normal),
ShapePrefix::Heatmap => Some(TableType::Heatmap),
ShapePrefix::Transposed => Some(TableType::Transposed),
_ => None,
}
}
pub fn is_exact_token_match(shape_name: &str, linked_name: &str) -> bool {
if linked_name.is_empty() {
return false;
}
let linked_len = linked_name.len();
let name_len = shape_name.len();
let mut pos = 0;
while let Some(found) = shape_name[pos..].find(linked_name) {
let abs_pos = pos + found;
let before_ok = abs_pos == 0 || !shape_name.as_bytes()[abs_pos - 1].is_ascii_alphanumeric();
let end_pos = abs_pos + linked_len;
let after_ok = end_pos >= name_len || !shape_name.as_bytes()[end_pos].is_ascii_alphanumeric();
if before_ok && after_ok {
return true;
}
pos = abs_pos + 1;
if pos >= name_len {
break;
}
}
false
}
pub fn strip_sign_suffix(name: &str) -> &str {
for suffix in &["_pos", "_neg", "_none"] {
if let Some(stripped) = name.strip_suffix(suffix) {
return stripped;
}
}
name
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_classify_ntbl() {
assert_eq!(classify_shape_name("ntbl_Revenue"), Some(ShapePrefix::NormalTable));
}
#[test]
fn test_classify_htmp() {
assert_eq!(classify_shape_name("htmp_Scores"), Some(ShapePrefix::Heatmap));
}
#[test]
fn test_classify_trns() {
assert_eq!(classify_shape_name("trns_Matrix"), Some(ShapePrefix::Transposed));
}
#[test]
fn test_classify_delt() {
assert_eq!(classify_shape_name("delt_Growth_pos"), Some(ShapePrefix::Delta));
}
#[test]
fn test_classify_ccst() {
assert_eq!(classify_shape_name("table_ccst"), Some(ShapePrefix::ColorCoded));
}
#[test]
fn test_classify_unknown() {
assert_eq!(classify_shape_name("regular_shape"), None);
}
#[test]
fn test_classify_priority_ntbl_over_htmp() {
assert_eq!(classify_shape_name("ntbl_htmp_test"), Some(ShapePrefix::NormalTable));
}
#[test]
fn test_token_match_exact() {
assert!(is_exact_token_match("ntbl_Revenue", "Revenue"));
}
#[test]
fn test_token_match_middle() {
assert!(is_exact_token_match("ntbl_Revenue_Q4", "Revenue"));
}
#[test]
fn test_token_no_match_partial() {
assert!(!is_exact_token_match("ntbl_RevenueTotal", "Revenue"));
}
#[test]
fn test_token_no_match_partial_before() {
assert!(!is_exact_token_match("ntbl_TotalRevenue", "Revenue"));
}
#[test]
fn test_token_match_at_start() {
assert!(is_exact_token_match("Revenue_table", "Revenue"));
}
#[test]
fn test_token_match_at_end() {
assert!(is_exact_token_match("ntbl_Revenue", "Revenue"));
}
#[test]
fn test_token_match_whole_string() {
assert!(is_exact_token_match("Revenue", "Revenue"));
}
#[test]
fn test_token_match_underscore_boundary() {
assert!(is_exact_token_match("data_Revenue_2024", "Revenue"));
}
#[test]
fn test_token_match_hyphen_boundary() {
assert!(is_exact_token_match("data-Revenue-2024", "Revenue"));
}
#[test]
fn test_token_match_space_boundary() {
assert!(is_exact_token_match("data Revenue 2024", "Revenue"));
}
#[test]
fn test_token_no_match_empty_linked() {
assert!(!is_exact_token_match("ntbl_Revenue", ""));
}
#[test]
fn test_token_no_match_not_found() {
assert!(!is_exact_token_match("ntbl_Revenue", "Costs"));
}
#[test]
fn test_strip_pos() {
assert_eq!(strip_sign_suffix("delt_Growth_pos"), "delt_Growth");
}
#[test]
fn test_strip_neg() {
assert_eq!(strip_sign_suffix("delt_Growth_neg"), "delt_Growth");
}
#[test]
fn test_strip_none() {
assert_eq!(strip_sign_suffix("delt_Growth_none"), "delt_Growth");
}
#[test]
fn test_strip_no_suffix() {
assert_eq!(strip_sign_suffix("delt_Growth"), "delt_Growth");
}
#[test]
fn test_table_type_priority() {
assert!(TableType::Normal < TableType::Heatmap);
assert!(TableType::Heatmap < TableType::Transposed);
}
}