feat: prepare for alternative stores

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
2026-01-17 00:19:18 +01:00
parent f19cea610b
commit 41b6479137
10 changed files with 316 additions and 179 deletions

View File

@@ -11,7 +11,9 @@ use crossterm::{
};
use fuzzy_matcher::FuzzyMatcher;
use fuzzy_matcher::skim::SkimMatcherV2;
use nocontrol::{ControlPlane, Operator, Specification, manifests::ManifestState};
use nocontrol::{
ControlPlane, Operator, Specification, manifests::ManifestState, stores::BackingStoreEdge,
};
use ratatui::{
Frame, Terminal,
backend::{Backend, CrosstermBackend},
@@ -28,8 +30,8 @@ enum InputMode {
Search,
}
struct App<TOperator: Operator> {
control_plane: ControlPlane<TOperator>,
struct App<TOperator: Operator, TStore: BackingStoreEdge<TOperator::Specifications>> {
control_plane: ControlPlane<TOperator, TStore>,
manifests: Vec<ManifestState<TOperator::Specifications>>,
filtered_indices: Vec<usize>,
list_state: ListState,
@@ -47,8 +49,10 @@ struct App<TOperator: Operator> {
fuzzy_matcher: SkimMatcherV2,
}
impl<TOperator: Operator> App<TOperator> {
fn new(control_plane: ControlPlane<TOperator>) -> Self {
impl<TOperator: Operator, TStore: BackingStoreEdge<TOperator::Specifications>>
App<TOperator, TStore>
{
fn new(control_plane: ControlPlane<TOperator, TStore>) -> Self {
let mut list_state = ListState::default();
list_state.select(Some(0));
@@ -309,9 +313,13 @@ impl<TOperator: Operator> App<TOperator> {
}
}
async fn run_app<B: Backend, TOperator: Operator + Send + Sync + 'static>(
async fn run_app<
B: Backend,
TOperator: Operator + Send + Sync + 'static,
TStore: BackingStoreEdge<TOperator::Specifications> + Send + Sync + 'static,
>(
terminal: &mut Terminal<B>,
app: Arc<Mutex<App<TOperator>>>,
app: Arc<Mutex<App<TOperator, TStore>>>,
) -> anyhow::Result<()>
where
TOperator::Specifications: Send + Sync,
@@ -414,7 +422,10 @@ where
Ok(())
}
fn ui<TOperator: Operator>(f: &mut Frame, app: &mut App<TOperator>) {
fn ui<TOperator: Operator, TStore: BackingStoreEdge<TOperator::Specifications>>(
f: &mut Frame,
app: &mut App<TOperator, TStore>,
) {
// Create layout
let chunks = Layout::default()
.direction(Direction::Vertical)
@@ -472,7 +483,14 @@ fn ui<TOperator: Operator>(f: &mut Frame, app: &mut App<TOperator>) {
render_command_input(f, app, chunks[3]);
}
fn render_manifest_list<TOperator: Operator>(f: &mut Frame, app: &mut App<TOperator>, area: Rect) {
fn render_manifest_list<
TOperator: Operator,
TStore: BackingStoreEdge<TOperator::Specifications>,
>(
f: &mut Frame,
app: &mut App<TOperator, TStore>,
area: Rect,
) {
// Collect filtered manifests data before borrowing list_state
let filtered_data: Vec<_> = app
.filtered_indices
@@ -530,7 +548,14 @@ fn render_manifest_list<TOperator: Operator>(f: &mut Frame, app: &mut App<TOpera
f.render_stateful_widget(list, area, &mut app.list_state);
}
fn render_manifest_details<TOperator: Operator>(f: &mut Frame, app: &App<TOperator>, area: Rect) {
fn render_manifest_details<
TOperator: Operator,
TStore: BackingStoreEdge<TOperator::Specifications>,
>(
f: &mut Frame,
app: &App<TOperator, TStore>,
area: Rect,
) {
let content = if let Some(manifest) = app.get_selected_manifest() {
let mut lines = vec![
Line::from(vec![
@@ -599,7 +624,11 @@ fn render_manifest_details<TOperator: Operator>(f: &mut Frame, app: &App<TOperat
f.render_widget(paragraph, area);
}
fn render_messages<TOperator: Operator>(f: &mut Frame, app: &App<TOperator>, area: Rect) {
fn render_messages<TOperator: Operator, TStore: BackingStoreEdge<TOperator::Specifications>>(
f: &mut Frame,
app: &App<TOperator, TStore>,
area: Rect,
) {
let messages: Vec<Line> = app
.messages
.iter()
@@ -616,7 +645,14 @@ fn render_messages<TOperator: Operator>(f: &mut Frame, app: &App<TOperator>, are
f.render_widget(paragraph, area);
}
fn render_command_input<TOperator: Operator>(f: &mut Frame, app: &App<TOperator>, area: Rect) {
fn render_command_input<
TOperator: Operator,
TStore: BackingStoreEdge<TOperator::Specifications>,
>(
f: &mut Frame,
app: &App<TOperator, TStore>,
area: Rect,
) {
let (title, input_text, style) = match app.input_mode {
InputMode::Normal => {
let hist_info = if let Some(idx) = app.history_index {
@@ -650,10 +686,13 @@ fn render_command_input<TOperator: Operator>(f: &mut Frame, app: &App<TOperator>
}
/// Run the TUI with the given control plane
pub async fn run<TOperator>(control_plane: ControlPlane<TOperator>) -> anyhow::Result<()>
pub async fn run<TOperator, TStore>(
control_plane: ControlPlane<TOperator, TStore>,
) -> anyhow::Result<()>
where
TOperator: Operator + Send + Sync + 'static,
TOperator::Specifications: Send + Sync,
TStore: BackingStoreEdge<TOperator::Specifications> + Send + Sync + 'static,
{
// Setup terminal
enable_raw_mode()?;