use crate::tio::proto::DeviceRoute;
use glob::Pattern;
pub struct ColumnFilter {
pattern: Pattern,
}
impl ColumnFilter {
pub fn new(pattern_str: &str) -> Result<Self, String> {
let normalized = Self::normalize_pattern(pattern_str);
let pattern =
Pattern::new(&normalized).map_err(|e| format!("Invalid glob pattern: {}", e))?;
Ok(Self { pattern })
}
fn normalize_pattern(pattern_str: &str) -> String {
let trimmed = pattern_str.trim();
if trimmed.is_empty() {
return trimmed.to_string();
}
if trimmed.contains('*') {
if trimmed.starts_with("**/") && !trimmed.ends_with("/**") && !trimmed.ends_with("/*") {
let after_prefix = &trimmed[3..]; if !after_prefix.contains('/') && Self::is_alphabetic_name(after_prefix) {
return format!("**/*/{}", after_prefix);
}
}
return trimmed.to_string();
}
if !trimmed.contains('/') {
return format!("**/{}/**", trimmed);
}
trimmed.to_string()
}
fn is_alphabetic_name(s: &str) -> bool {
s.chars().any(|c| c.is_alphabetic())
}
pub fn matches(&self, route: &DeviceRoute, stream_name: &str, col_name: &str) -> bool {
let full_path = self.get_path_string(route, stream_name, col_name);
self.pattern.matches(&full_path)
}
pub fn get_path_string(
&self,
route: &DeviceRoute,
stream_name: &str,
col_name: &str,
) -> String {
let route_str = route.to_string();
let clean_route = route_str.trim_start_matches('/');
if clean_route.is_empty() {
format!("/{}/{}", stream_name, col_name)
} else {
format!("/{}/{}/{}", clean_route, stream_name, col_name)
}
}
}