schemaui 0.12.3

A Rust library for generating TUI and Web UIs from JSON Schemas for configuration management.
Documentation
use std::sync::Arc;

use serde_json::Value;

use crate::tui::model::FieldSchema;
use crate::tui::state::error::FieldCoercionError;

use super::{ComponentKind, FieldComponent, MultiSelectStateRef, palette::ComponentPalette};

#[derive(Debug, Clone)]
pub struct MultiSelectComponent {
    labels: Vec<String>,
    values: Vec<Value>,
    selected: Vec<bool>,
    palette: Arc<ComponentPalette>,
}

impl MultiSelectComponent {
    pub fn new(
        labels: &[String],
        values: &[Value],
        default: Option<&Value>,
        palette: Arc<ComponentPalette>,
    ) -> Self {
        let mut selected = vec![false; labels.len()];
        if let Some(Value::Array(items)) = default {
            for item in items {
                if let Some(idx) = values.iter().position(|candidate| candidate == item) {
                    selected[idx] = true;
                }
            }
        }
        Self {
            labels: labels.to_vec(),
            values: values.to_vec(),
            selected,
            palette,
        }
    }
}

impl FieldComponent for MultiSelectComponent {
    fn kind(&self) -> ComponentKind {
        ComponentKind::MultiSelect
    }

    fn display_value(&self, _schema: &FieldSchema) -> String {
        let values = self
            .labels
            .iter()
            .zip(self.selected.iter())
            .filter_map(|(option, flag)| if *flag { Some(option.clone()) } else { None })
            .collect::<Vec<_>>();
        if values.is_empty() {
            format!("[] {}", self.palette.collection.list_hint)
        } else {
            format!("[{}]", values.join(", "))
        }
    }

    fn seed_value(&mut self, _schema: &FieldSchema, value: &Value) {
        if let Value::Array(items) = value {
            let mut flags = vec![false; self.labels.len()];
            for item in items {
                if let Some(idx) = self.values.iter().position(|candidate| candidate == item) {
                    flags[idx] = true;
                }
            }
            if flags.len() == self.selected.len() {
                self.selected = flags;
            }
        }
    }

    fn current_value(&self, _schema: &FieldSchema) -> Result<Option<Value>, FieldCoercionError> {
        let values = self
            .values
            .iter()
            .zip(self.selected.iter())
            .filter_map(
                |(option, flag)| {
                    if *flag { Some(option.clone()) } else { None }
                },
            )
            .collect();
        Ok(Some(Value::Array(values)))
    }

    fn multi_state(&self) -> Option<MultiSelectStateRef<'_>> {
        Some(MultiSelectStateRef {
            options: &self.labels,
            selected: &self.selected,
        })
    }

    fn set_multi_state(&mut self, flags: &[bool]) -> bool {
        if flags.len() != self.selected.len() {
            return false;
        }
        if self.selected != flags {
            self.selected.clone_from_slice(flags);
            true
        } else {
            false
        }
    }
}