schemaui 0.7.2

A Rust library for generating TUI and Web UIs from JSON Schemas for configuration management.
Documentation
use crossterm::event::KeyEvent;
use serde_json::Value;

use super::helpers::OverlayContext;
use crate::tui::model::FieldSchema;
use crate::tui::state::array::{ArrayEditorContext, ArrayEditorSession};
use crate::tui::state::composite::{
    CompositeEditorSession, CompositeListEditorContext, CompositeVariantSummary,
};
use crate::tui::state::error::FieldCoercionError;
use crate::tui::state::key_value::{KeyValueEditorContext, KeyValueEditorSession};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ComponentKind {
    TextInput,
    ArrayBuffer,
    Bool,
    Enum,
    MultiSelect,
    Composite,
    CompositeList,
    ScalarArray,
    KeyValue,
}

pub(crate) trait FieldComponent: FieldComponentClone + std::fmt::Debug {
    fn kind(&self) -> ComponentKind;
    fn display_value(&self, schema: &FieldSchema) -> String;
    fn handle_key(&mut self, schema: &FieldSchema, key: &KeyEvent) -> bool {
        let _ = (schema, key);
        false
    }
    fn seed_value(&mut self, schema: &FieldSchema, value: &Value);
    fn current_value(&self, schema: &FieldSchema) -> Result<Option<Value>, FieldCoercionError>;
    fn cursor_offset(&self, _schema: &FieldSchema) -> Option<usize> {
        None
    }

    fn bool_value(&self) -> Option<bool> {
        None
    }

    fn set_bool(&mut self, _value: bool) -> bool {
        false
    }

    fn enum_state(&self) -> Option<EnumStateRef<'_>> {
        None
    }

    fn set_enum_index(&mut self, _index: usize) -> bool {
        false
    }

    fn multi_state(&self) -> Option<MultiSelectStateRef<'_>> {
        None
    }

    fn set_multi_state(&mut self, _flags: &[bool]) -> bool {
        false
    }

    fn composite_popup(&self) -> Option<CompositePopupData> {
        None
    }

    fn composite_selector(&self) -> Option<CompositeSelectorView> {
        None
    }

    fn composite_entry_selector(&self) -> Option<CompositeSelectorView> {
        None
    }

    fn composite_summaries(&self) -> Option<Vec<CompositeVariantSummary>> {
        None
    }

    fn active_composite_variants(&self) -> Vec<usize> {
        Vec::new()
    }

    fn apply_composite_selection(&mut self, _selection: usize, _flags: Option<Vec<bool>>) -> bool {
        false
    }

    fn open_composite_editor(
        &mut self,
        pointer: &str,
        variant_index: usize,
    ) -> Result<CompositeEditorSession, FieldCoercionError> {
        let _ = variant_index;
        Err(FieldCoercionError::unsupported(
            pointer,
            "composite editing",
        ))
    }

    fn restore_composite_editor(&mut self, _session: CompositeEditorSession) {}

    fn collection_panel(&self) -> Option<(Vec<String>, usize)> {
        None
    }

    fn collection_selected_label(&self) -> Option<String> {
        None
    }

    fn collection_selected_index(&self) -> Option<usize> {
        None
    }

    fn collection_select(&mut self, _delta: i32) -> bool {
        false
    }

    fn collection_set_selected(&mut self, _index: usize) -> bool {
        false
    }

    fn collection_add(&mut self) -> bool {
        false
    }

    fn collection_add_with_variant(&mut self, _variant_index: usize) -> bool {
        false
    }

    fn composite_list_variant_selector(&self) -> Option<CompositePopupData> {
        None
    }

    fn composite_list_variant_count(&self) -> usize {
        0
    }

    fn collection_remove(&mut self) -> bool {
        false
    }

    fn collection_move(&mut self, _delta: i32) -> bool {
        false
    }

    fn open_composite_list_editor(
        &mut self,
        pointer: &str,
    ) -> Result<CompositeListEditorContext, FieldCoercionError> {
        Err(FieldCoercionError::unsupported(
            pointer,
            "composite list editing",
        ))
    }

    fn restore_composite_list_editor(
        &mut self,
        _entry_index: usize,
        _session: CompositeEditorSession,
    ) {
    }

    fn open_key_value_editor(
        &mut self,
        pointer: &str,
    ) -> Result<KeyValueEditorContext, FieldCoercionError> {
        Err(FieldCoercionError::unsupported(pointer, "map editing"))
    }

    fn apply_key_value_editor(
        &mut self,
        _entry_index: usize,
        _session: &KeyValueEditorSession,
    ) -> Result<bool, FieldCoercionError> {
        Err(FieldCoercionError::unsupported("", "map editing"))
    }

    fn open_scalar_array_editor(
        &mut self,
        pointer: &str,
    ) -> Result<ArrayEditorContext, FieldCoercionError> {
        Err(FieldCoercionError::unsupported(pointer, "array editing"))
    }

    fn apply_scalar_array_editor(
        &mut self,
        _entry_index: usize,
        _session: &ArrayEditorSession,
    ) -> Result<bool, FieldCoercionError> {
        Err(FieldCoercionError::unsupported("", "array editing"))
    }

    fn overlay_context(&self, _schema: &FieldSchema) -> Option<OverlayContext> {
        None
    }
}

pub(crate) trait FieldComponentClone {
    fn clone_box(&self) -> Box<dyn FieldComponent>;
}

impl<T> FieldComponentClone for T
where
    T: 'static + FieldComponent + Clone,
{
    fn clone_box(&self) -> Box<dyn FieldComponent> {
        Box::new(self.clone())
    }
}

impl Clone for Box<dyn FieldComponent> {
    fn clone(&self) -> Self {
        self.clone_box()
    }
}

#[derive(Debug, Clone)]
pub struct EnumStateRef<'a> {
    pub options: &'a [String],
    pub selected: usize,
}

#[derive(Debug, Clone)]
pub struct MultiSelectStateRef<'a> {
    pub options: &'a [String],
    pub selected: &'a [bool],
}

#[derive(Debug, Clone)]
pub struct CompositePopupData {
    pub options: Vec<String>,
    pub selected: usize,
    pub multi: bool,
    pub active: Vec<bool>,
}

#[derive(Debug, Clone)]
pub struct CompositeSelectorView {
    pub multi: bool,
    pub options: Vec<String>,
    pub active: Vec<bool>,
    pub descriptions: Vec<Option<String>>,
}