mod entity_search;
mod index;
pub use entity_search::{EntitySearch, EntitySearchResult};
pub use index::SearchIndex;
use serde_json::Value;
pub struct SearchableRegistration {
pub entity_type: &'static str,
pub fields: &'static [&'static str],
}
inventory::collect!(SearchableRegistration);
pub fn iter_searchable() -> impl Iterator<Item = &'static SearchableRegistration> {
inventory::iter::<SearchableRegistration>()
}
pub fn extract_searchable_text(item: &Value, fields: &[&str]) -> String {
let Some(obj) = item.as_object() else {
return String::new();
};
fields
.iter()
.filter_map(|field| {
obj.get(*field).and_then(|v| match v {
Value::String(s) => Some(s.as_str()),
Value::Number(n) => Some(n.to_string()).map(|s| s.leak() as &str),
Value::Bool(b) => Some(if *b { "true" } else { "false" }),
_ => None,
})
})
.collect::<Vec<_>>()
.join(" ")
}
#[cfg(test)]
mod tests {
use serde_json::json;
use super::*;
#[test]
fn test_extract_searchable_text() {
let item = json!({
"name": "Test Target",
"category": "Audio",
"serviceId": "svc-123"
});
let text = extract_searchable_text(&item, &["name", "category"]);
assert_eq!(text, "Test Target Audio");
}
#[test]
fn test_extract_searchable_text_missing_fields() {
let item = json!({
"name": "Test Target"
});
let text = extract_searchable_text(&item, &["name", "category"]);
assert_eq!(text, "Test Target");
}
}