Files
pikl/crates/pikl-core/src/model/traits.rs

87 lines
3.4 KiB
Rust

//! 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>;
/// 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<usize>;
/// Get the JSON value of an item by its original (pre-filter)
/// index. Used by multi-select to retrieve selected items
/// that may not be in the current filtered set.
fn serialize_original(&self, original_index: usize) -> Option<&serde_json::Value>;
/// 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<String> {
None
}
/// Collect all filtered items as (value, original_index) pairs.
/// Default implementation iterates filtered results using
/// existing trait methods.
fn collect_filtered(&self) -> Vec<(&serde_json::Value, usize)> {
(0..self.filtered_count())
.filter_map(|i| {
let value = self.serialize_filtered(i)?;
let orig = self.original_index(i)?;
Some((value, orig))
})
.collect()
}
}
/// Extension of [`Menu`] with mutation methods. Required by
/// [`MenuRunner`] which needs to add, replace, and remove
/// items. Embedders that only need read-only access can
/// depend on `Menu` alone.
pub trait MutableMenu: Menu {
/// Add raw values from streaming input or AddItems actions.
fn add_raw(&mut self, values: Vec<serde_json::Value>);
/// Replace all items with a new set of values. Used by
/// handler hook `replace_items` responses.
fn replace_all(&mut self, values: Vec<serde_json::Value>);
/// Remove items at the given original indices. Used by
/// handler hook `remove_items` responses.
fn remove_by_indices(&mut self, indices: Vec<usize>);
}