"""
Shared search service for UI components.
This module provides a framework-agnostic search service that can be used
by both PyQt and Textual implementations to ensure consistent search behavior
across the application.
"""
from typing import Dict, Callable, TypeVar, Generic
import logging
logger = logging.getLogger(__name__)
T = TypeVar('T')
[docs]
class SearchService(Generic[T]):
"""
Framework-agnostic search service with minimum character trigger.
This service provides consistent search behavior across PyQt and Textual
implementations, ensuring the same code path is used for all search operations.
Key features:
- Minimum character threshold (default: 2)
- Customizable searchable text extraction
- Performance optimization for short searches
"""
# Class constant for minimum search characters
MIN_SEARCH_CHARS = 2
[docs]
def __init__(self,
all_items: Dict[str, T],
searchable_text_extractor: Callable[[T], str],
min_chars: int = MIN_SEARCH_CHARS):
"""
Initialize search service.
Args:
all_items: Dictionary of all searchable items (key -> item)
searchable_text_extractor: Function to extract searchable text from an item
min_chars: Minimum characters required to trigger search (default: 2)
"""
self.all_items = all_items
self.searchable_text_extractor = searchable_text_extractor
self.min_chars = min_chars
self.filtered_items = all_items.copy()
[docs]
def filter(self, search_term: str) -> Dict[str, T]:
"""
Filter items based on search term.
This is the canonical search implementation used across all UI frameworks.
Args:
search_term: Search string to filter by
Returns:
Dictionary of filtered items
"""
search_term = search_term.strip()
# Performance optimization: skip expensive filtering for short searches
if not search_term or len(search_term) < self.min_chars:
if len(search_term) == 0:
# Only update if completely empty (to reset from previous filter)
if self.filtered_items != self.all_items:
self.filtered_items = self.all_items.copy()
return self.filtered_items
# For searches below min_chars, keep current state
return self.filtered_items
# Perform search
search_lower = search_term.lower()
filtered = {
key: item for key, item in self.all_items.items()
if search_lower in self.searchable_text_extractor(item).lower()
}
self.filtered_items = filtered
return filtered
[docs]
def reset(self):
"""Reset filter to show all items."""
self.filtered_items = self.all_items.copy()
return self.filtered_items
[docs]
def update_items(self, new_items: Dict[str, T]):
"""Update the items being searched."""
self.all_items = new_items
self.filtered_items = new_items.copy()