#![allow(clippy::too_many_arguments)]
#![allow(dead_code)]
use crate::error::{MetricsError, Result};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::sync::{Arc, Mutex, RwLock};
use std::time::Instant;
pub mod collaboration;
pub mod core;
pub mod data_sources;
pub mod events;
pub mod layout;
pub mod rendering;
pub mod widgets;
pub use core::*;
pub use widgets::{
BorderConfig, ChartType, DataBindingConfig, EventType, FontConfig, InputType,
InteractiveWidget, RenderContent, RenderContext, ShaderProgram, StyleConfig, WidgetConfig,
WidgetEvent, WidgetEventResponse, WidgetType,
};
pub use data_sources::{
ChangeType, ConnectionConfig, DataFormat, DataSource, DataSourceConfig, DataSourceManager,
DataSourceType, DataUpdate, ValidationConfig,
};
pub use events::{
DashboardEvent, EventAction, EventHandler, EventMetadata, EventPriority, EventResponse,
EventSystem, NotificationLevel,
};
pub use layout::{
ContainerConstraints, GridPosition, LayoutConstraints, LayoutManager, Margin, WidgetLayout,
};
pub use rendering::{
PerformanceMonitor, RenderStatistics, RenderingBackend, RenderingConfig, RenderingEngine,
UpdateManager, UpdateRequest, UpdateType,
};
pub use collaboration::{
CollaborationManager, Conflict, ConflictResolver, ConflictType, CursorPosition, Operation,
OperationType, Selection, SharedState, UserSession,
};
pub struct InteractiveDashboard {
config: DashboardConfig,
widgets: Arc<RwLock<HashMap<String, Box<dyn InteractiveWidget + Send + Sync>>>>,
data_sources: Arc<RwLock<HashMap<String, Box<dyn DataSource + Send + Sync>>>>,
event_system: Arc<Mutex<EventSystem>>,
layout_manager: Arc<Mutex<LayoutManager>>,
renderer: Arc<Mutex<Box<dyn RenderingEngine + Send + Sync>>>,
update_manager: Arc<Mutex<UpdateManager>>,
collaboration: Arc<Mutex<CollaborationManager>>,
state: Arc<RwLock<DashboardState>>,
}
impl std::fmt::Debug for InteractiveDashboard {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("InteractiveDashboard")
.field("config", &self.config)
.field(
"widgets",
&format!(
"{} widgets",
self.widgets.read().expect("Operation failed").len()
),
)
.field(
"data_sources",
&format!(
"{} data sources",
self.data_sources.read().expect("Operation failed").len()
),
)
.field("event_system", &"<event_system>")
.field("layout_manager", &"<layout_manager>")
.field("renderer", &"<renderer>")
.field("update_manager", &"<update_manager>")
.field("collaboration", &"<collaboration>")
.field("state", &"<state>")
.finish()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DashboardState {
pub view_state: ViewState,
pub filters: HashMap<String, Value>,
pub selections: Vec<String>,
pub viewport: ViewportState,
pub time_range: Option<TimeRange>,
pub custom_state: HashMap<String, Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ViewState {
pub mode: ViewMode,
pub visible_widgets: Vec<String>,
pub z_order: Vec<String>,
pub layout_mode: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ViewMode {
Normal,
FullScreen,
Presentation,
Edit,
Preview,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ViewportState {
pub zoom: f64,
pub pan_x: f64,
pub pan_y: f64,
pub bounds: ViewportBounds,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ViewportBounds {
pub min_x: f64,
pub max_x: f64,
pub min_y: f64,
pub max_y: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimeRange {
pub start: std::time::SystemTime,
pub end: std::time::SystemTime,
pub timezone: Option<String>,
}
impl InteractiveDashboard {
pub fn new(
config: DashboardConfig,
renderer: Box<dyn RenderingEngine + Send + Sync>,
) -> Result<Self> {
let container_constraints = ContainerConstraints {
width: config.width as f64,
height: config.height as f64,
..Default::default()
};
let layout_manager = LayoutManager::new(config.layout.clone(), container_constraints);
let event_system = EventSystem::new(events::EventSystemConfig::default());
let update_manager = UpdateManager::new(rendering::UpdateConfig::default());
let collaboration = if let Some(collab_config) = &config.collaboration_config {
CollaborationManager::new(collab_config.clone())
} else {
CollaborationManager::new(CollaborationConfig {
enabled: false,
auth: core::AuthConfig {
method: core::AuthMethod::None,
session_timeout: std::time::Duration::from_secs(3600),
guest_access: true,
required_permissions: Vec::new(),
},
sharing: core::ShareConfig {
public_sharing: false,
default_permission: core::PermissionLevel::ReadOnly,
link_expiration: None,
password_protection: false,
},
sync: core::SyncConfig {
sync_interval: std::time::Duration::from_secs(5),
conflict_resolution: core::ConflictResolution::LastWriterWins,
operational_transforms: false,
history_retention: std::time::Duration::from_secs(3600),
},
max_collaborators: 1,
})
};
Ok(Self {
config,
widgets: Arc::new(RwLock::new(HashMap::new())),
data_sources: Arc::new(RwLock::new(HashMap::new())),
event_system: Arc::new(Mutex::new(event_system)),
layout_manager: Arc::new(Mutex::new(layout_manager)),
renderer: Arc::new(Mutex::new(renderer)),
update_manager: Arc::new(Mutex::new(update_manager)),
collaboration: Arc::new(Mutex::new(collaboration)),
state: Arc::new(RwLock::new(DashboardState::default())),
})
}
pub fn add_widget(&self, widget: Box<dyn InteractiveWidget + Send + Sync>) -> Result<()> {
let widget_id = widget.id().to_string();
let widget_config = widget.config().clone();
self.widgets
.write()
.expect("Operation failed")
.insert(widget_id.clone(), widget);
self.layout_manager
.lock()
.expect("Operation failed")
.add_widget(&widget_config)?;
let mut state = self.state.write().expect("Operation failed");
state.view_state.visible_widgets.push(widget_id.clone());
state.view_state.z_order.push(widget_id);
Ok(())
}
pub fn remove_widget(&self, widget_id: &str) -> Result<()> {
self.widgets
.write()
.expect("Operation failed")
.remove(widget_id);
self.layout_manager
.lock()
.expect("Operation failed")
.remove_widget(widget_id)?;
let mut state = self.state.write().expect("Operation failed");
state
.view_state
.visible_widgets
.retain(|id| id != widget_id);
state.view_state.z_order.retain(|id| id != widget_id);
Ok(())
}
pub fn register_data_source(&self, source: Box<dyn DataSource + Send + Sync>) -> Result<()> {
let source_id = source.id().to_string();
self.data_sources
.write()
.expect("Operation failed")
.insert(source_id, source);
Ok(())
}
pub fn handle_interaction(&self, event: WidgetEvent) -> Result<()> {
let dashboard_event = DashboardEvent {
id: event.id.clone(),
event_type: format!("{:?}", event.event_type),
source: event.source_widget,
target: event.target,
timestamp: event.timestamp,
data: event.data,
metadata: EventMetadata::default(),
};
self.event_system
.lock()
.expect("Operation failed")
.queue_event(dashboard_event)?;
Ok(())
}
pub fn update_state(&self, updates: HashMap<String, Value>) -> Result<()> {
let mut state = self.state.write().expect("Operation failed");
for (key, value) in updates {
state.custom_state.insert(key, value);
}
Ok(())
}
pub fn render(&self, context: &RenderContext) -> Result<()> {
let widgets = self.widgets.read().expect("Operation failed");
let renderer = self.renderer.lock().expect("Operation failed");
renderer.clear([0.0, 0.0, 0.0, 1.0])?;
let state = self.state.read().expect("Operation failed");
let mut visible_widgets: Vec<_> = state
.view_state
.z_order
.iter()
.filter(|id| state.view_state.visible_widgets.contains(id))
.filter_map(|id| widgets.get(id))
.collect();
visible_widgets.sort_by_key(|widget| widget.config().z_index);
for widget in visible_widgets {
if let Ok(widget_render) = widget.render(context) {
renderer.render_widget(&widget_render, context)?;
}
}
renderer.present()?;
Ok(())
}
pub fn process_updates(&self) -> Result<()> {
let _responses = self
.event_system
.lock()
.expect("Operation failed")
.process_events()?;
let _update_requests = self
.update_manager
.lock()
.expect("Operation failed")
.process_updates()?;
if self
.config
.collaboration_config
.as_ref()
.is_some_and(|c| c.enabled)
{
let _resolved_operations = self
.collaboration
.lock()
.expect("Operation failed")
.resolve_conflicts()?;
}
Ok(())
}
pub fn config(&self) -> &DashboardConfig {
&self.config
}
pub fn state(&self) -> DashboardState {
self.state.read().expect("Operation failed").clone()
}
pub fn export_config(&self) -> Result<Value> {
serde_json::to_value(&self.config).map_err(|e| MetricsError::InvalidInput(e.to_string()))
}
pub fn import_config(&mut self, config_data: Value) -> Result<()> {
self.config = serde_json::from_value(config_data)
.map_err(|e| MetricsError::InvalidInput(e.to_string()))?;
Ok(())
}
}
impl Default for ViewState {
fn default() -> Self {
Self {
mode: ViewMode::Normal,
visible_widgets: Vec::new(),
z_order: Vec::new(),
layout_mode: "grid".to_string(),
}
}
}
impl Default for ViewportState {
fn default() -> Self {
Self {
zoom: 1.0,
pan_x: 0.0,
pan_y: 0.0,
bounds: ViewportBounds::default(),
}
}
}
impl Default for ViewportBounds {
fn default() -> Self {
Self {
min_x: 0.0,
max_x: 1200.0,
min_y: 0.0,
max_y: 800.0,
}
}
}