Files
k-frame/crates/domain/src/entities/widget_config.rs
Gabriel Kaszewski a51d22649a internal data sources (clock, static text), connection indicator, rendering fixes
DataSourceConfig refactored to enum: External/Clock/StaticText. Clock
generates formatted time via chrono, static text emits configured string.

ESP32: connection status indicator (green/red dot bottom-right), per-widget
clear before redraw, RenderEvent enum for local + server messages.

Polling uses DataUpdate instead of ScreenUpdate to avoid wiping widget state.
Empty mappings passthrough raw source data for internal sources.
2026-06-19 11:26:49 +02:00

81 lines
2.3 KiB
Rust

use crate::value_objects::{DisplayHint, KeyMapping, Value, WidgetState};
use std::collections::BTreeMap;
pub type WidgetId = u16;
pub type DataSourceId = u16;
const DEFAULT_MAX_DATA_SIZE: u16 = 2048;
#[derive(Debug, Clone)]
pub struct WidgetConfig {
pub id: WidgetId,
pub name: String,
pub display_hint: DisplayHint,
pub data_source_id: DataSourceId,
pub mappings: Vec<KeyMapping>,
pub max_data_size: u16,
}
impl WidgetConfig {
pub fn new(
id: WidgetId,
name: String,
display_hint: DisplayHint,
data_source_id: DataSourceId,
mappings: Vec<KeyMapping>,
) -> Self {
Self {
id,
name,
display_hint,
data_source_id,
mappings,
max_data_size: DEFAULT_MAX_DATA_SIZE,
}
}
pub fn extract(&self, raw: &Value) -> WidgetState {
let has_mappings = self.mappings.iter().any(|m| !m.source_path.is_empty());
if !has_mappings {
let data = match raw {
Value::Object(map) => map.clone(),
_ => BTreeMap::new(),
};
return WidgetState { data, error: None };
}
let budget = self.max_data_size as usize;
let mut used = 0usize;
let mut data = BTreeMap::new();
for mapping in &self.mappings {
if let Some((key, value)) = mapping.extract(raw) {
let key_cost = key.len();
let remaining = budget.saturating_sub(used + key_cost);
let value = Self::truncate_value(value, remaining);
used += key_cost + value.estimated_size();
data.insert(key, value);
if used >= budget {
break;
}
}
}
WidgetState { data, error: None }
}
fn truncate_value(value: Value, max_bytes: usize) -> Value {
match value {
Value::String(s) if s.len() > max_bytes => {
let truncated: String = s
.char_indices()
.take_while(|(i, _)| *i < max_bytes)
.map(|(_, c)| c)
.collect();
Value::String(truncated)
}
other => other,
}
}
}