aboutsummaryrefslogtreecommitdiff
path: root/src/core/utils/search.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/utils/search.rs')
-rw-r--r--src/core/utils/search.rs135
1 files changed, 135 insertions, 0 deletions
diff --git a/src/core/utils/search.rs b/src/core/utils/search.rs
new file mode 100644
index 0000000..81607cd
--- /dev/null
+++ b/src/core/utils/search.rs
@@ -0,0 +1,135 @@
+use serde_json::Value;
+
+/// Search and filtering utilities for entity data
+#[allow(dead_code)]
+pub struct SearchFilter;
+
+#[allow(dead_code)]
+impl SearchFilter {
+ /// Filter a collection of JSON values based on a search query across specified fields
+ pub fn filter_data(data: &[Value], search_query: &str, search_fields: &[&str]) -> Vec<Value> {
+ if search_query.is_empty() {
+ return data.to_vec();
+ }
+
+ let search_lower = search_query.to_lowercase();
+ data.iter()
+ .filter(|item| {
+ search_fields.iter().any(|field| {
+ item.get(field)
+ .and_then(|v| v.as_str())
+ .map(|s| s.to_lowercase().contains(&search_lower))
+ .unwrap_or(false)
+ })
+ })
+ .cloned()
+ .collect()
+ }
+
+ /// Filter any generic collection with a custom predicate
+ pub fn filter_generic<T>(data: &[T], predicate: impl Fn(&T) -> bool) -> Vec<T>
+ where
+ T: Clone,
+ {
+ data.iter()
+ .filter(|item| predicate(item))
+ .cloned()
+ .collect()
+ }
+
+ /// Search assets specifically (common fields: name, asset_tag, manufacturer, model)
+ pub fn filter_assets(assets: &[Value], search_query: &str) -> Vec<Value> {
+ Self::filter_data(
+ assets,
+ search_query,
+ &[
+ "name",
+ "asset_tag",
+ "manufacturer",
+ "model",
+ "serial_number",
+ ],
+ )
+ }
+
+ /// Search borrowers (common fields: first_name, last_name, email, username)
+ pub fn filter_borrowers(borrowers: &[Value], search_query: &str) -> Vec<Value> {
+ Self::filter_data(
+ borrowers,
+ search_query,
+ &["first_name", "last_name", "email", "username"],
+ )
+ }
+
+ /// Search categories (common fields: category_name, category_code)
+ pub fn filter_categories(categories: &[Value], search_query: &str) -> Vec<Value> {
+ Self::filter_data(
+ categories,
+ search_query,
+ &["category_name", "category_code"],
+ )
+ }
+
+ /// Search zones (common fields: zone_name, zone_code)
+ pub fn filter_zones(zones: &[Value], search_query: &str) -> Vec<Value> {
+ Self::filter_data(zones, search_query, &["zone_name", "zone_code"])
+ }
+
+ /// Search suppliers (common fields: name)
+ pub fn filter_suppliers(suppliers: &[Value], search_query: &str) -> Vec<Value> {
+ Self::filter_data(suppliers, search_query, &["name"])
+ }
+}
+
+/// Sorting utilities
+#[allow(dead_code)]
+pub struct SortUtils;
+
+#[allow(dead_code)]
+impl SortUtils {
+ /// Sort JSON values by a specific field
+ pub fn sort_json_by_field(data: &mut [Value], field: &str, ascending: bool) {
+ data.sort_by(|a, b| {
+ let val_a = a.get(field);
+ let val_b = b.get(field);
+
+ let cmp = match (val_a, val_b) {
+ (Some(a), Some(b)) => {
+ // Try to compare as strings first
+ match (a.as_str(), b.as_str()) {
+ (Some(s_a), Some(s_b)) => s_a.cmp(s_b),
+ _ => {
+ // Try to compare as numbers
+ match (a.as_i64(), b.as_i64()) {
+ (Some(n_a), Some(n_b)) => n_a.cmp(&n_b),
+ _ => {
+ // Try to compare as floats
+ match (a.as_f64(), b.as_f64()) {
+ (Some(f_a), Some(f_b)) => f_a
+ .partial_cmp(&f_b)
+ .unwrap_or(std::cmp::Ordering::Equal),
+ _ => std::cmp::Ordering::Equal,
+ }
+ }
+ }
+ }
+ }
+ }
+ (Some(_), None) => std::cmp::Ordering::Less,
+ (None, Some(_)) => std::cmp::Ordering::Greater,
+ (None, None) => std::cmp::Ordering::Equal,
+ };
+
+ if ascending {
+ cmp
+ } else {
+ cmp.reverse()
+ }
+ });
+ }
+
+ /// Generic sort function for any collection
+ pub fn sort_generic<T>(data: &mut [T], compare_fn: impl Fn(&T, &T) -> std::cmp::Ordering) {
+ data.sort_by(compare_fn);
+ }
+}