//! Trait abstractions for the menu data layer. [`Menu`] //! is what the event loop drives. [`MenuItem`] is the display //! contract for a single entry. Different backends (JSON, //! CSV, Postgres, etc.) implement these. /// Display contract for a single entry in a menu. /// Provides a label for display and serialization for output. pub trait MenuItem: Send + Sync + 'static { /// Human-readable display text for this entry. fn label(&self) -> &str; /// Serialize this entry for structured output (stdout, hooks). fn serialize(&self) -> serde_json::Value; } /// A filterable collection that the menu event loop drives. /// Each implementation owns its data and handles filtering /// internally. The event loop only needs labels (strings) /// and serialized output (JSON values). pub trait Menu: Send + 'static { /// Total number of items before filtering. fn total(&self) -> usize; /// Apply a filter query. Implementations decide how to /// match (fuzzy, regex, exact, SQL WHERE, etc.). fn apply_filter(&mut self, query: &str); /// Number of items that passed the current filter. fn filtered_count(&self) -> usize; /// Get the display label for a filtered item by its /// position in the filtered results. fn filtered_label(&self, filtered_index: usize) -> Option<&str>; /// Add raw values from streaming input or AddItems actions. fn add_raw(&mut self, values: Vec); /// Get the JSON value of a filtered item for output. /// Returns a reference to the stored value. fn serialize_filtered(&self, filtered_index: usize) -> Option<&serde_json::Value>; /// Get the original index of a filtered item. Used to /// provide the item's position in the unfiltered list /// for output and hook events. fn original_index(&self, filtered_index: usize) -> Option; /// Replace all items with a new set of values. Used by /// handler hook `replace_items` responses. fn replace_all(&mut self, values: Vec); /// Remove items at the given original indices. Used by /// handler hook `remove_items` responses. fn remove_by_indices(&mut self, indices: Vec); /// Get the formatted display text for a filtered item, /// if a format template is configured. Returns None if /// no template is set, in which case the raw label is /// used. fn formatted_label(&self, _filtered_index: usize) -> Option { None } }