use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::collections::HashMap;
use std::collections::HashSet;
#[cfg(target_arch = "wasm32")]
use tsify::*;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[derive(Clone, Serialize, Deserialize, Debug, Default, PartialEq, Eq)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
#[serde(rename_all = "camelCase")]
pub struct GraphNode {
pub id: String,
pub component: String,
#[cfg_attr(target_arch = "wasm32", tsify(type = "Map<string, any> | undefined"))]
pub metadata: Option<HashMap<String, Value>>,
}
impl GraphNode {
pub fn component_spec(&self) -> Option<ComponentSpec> {
self.metadata
.as_ref()
.and_then(|m| m.get("componentSpec"))
.and_then(|v| serde_json::from_value(v.clone()).ok())
}
pub fn set_component_spec(&mut self, spec: ComponentSpec) {
let meta = self.metadata.get_or_insert_with(HashMap::new);
if let Ok(v) = serde_json::to_value(&spec) {
meta.insert("componentSpec".to_string(), v);
}
}
pub fn script_runtime(&self) -> Option<&str> {
match self.component_spec() {
Some(ComponentSpec::Script { script }) => Some(
match script.runtime.as_str() {
"python" => "python",
"nodejs" => "nodejs",
"ruby" => "ruby",
"lua" => "lua",
_ => return None,
},
),
_ => None,
}
}
pub fn get_metadata<T: serde::de::DeserializeOwned>(&self, key: &str) -> Option<T> {
self.metadata
.as_ref()
.and_then(|m| m.get(key))
.and_then(|v| serde_json::from_value(v.clone()).ok())
}
pub fn set_metadata<T: Serialize>(&mut self, key: &str, value: &T) {
let meta = self.metadata.get_or_insert_with(HashMap::new);
if let Ok(v) = serde_json::to_value(value) {
meta.insert(key.to_string(), v);
}
}
}
#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq, Eq)]
pub enum ScriptRuntime {
Python,
JavaScript,
}
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
#[derive(Default)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum ComponentSpec {
#[default]
Native,
Script { script: ScriptSpec },
Wasm {
source: String,
handler: String,
},
Subgraph {
graph: String,
},
}
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
#[serde(rename_all = "camelCase")]
pub struct ScriptSpec {
pub runtime: String,
#[serde(default = "default_source_type")]
pub source: ScriptSourceType,
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(default = "default_handler")]
pub handler: String,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
#[cfg_attr(
target_arch = "wasm32",
tsify(type = "Map<string, string> | undefined")
)]
pub dependencies: HashMap<String, String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout_seconds: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub memory_mb: Option<u32>,
}
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Default)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
#[serde(rename_all = "camelCase")]
pub enum ScriptSourceType {
#[default]
Inline,
File,
}
fn default_source_type() -> ScriptSourceType {
ScriptSourceType::Inline
}
fn default_handler() -> String {
"handler".to_string()
}
#[derive(Clone, Serialize, Deserialize, Debug, Default)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
#[serde(rename_all = "camelCase")]
pub struct GraphEdge {
pub port_name: String,
pub port_id: String,
pub node_id: String,
pub index: Option<usize>,
pub expose: bool,
pub data: Option<Value>,
#[cfg_attr(target_arch = "wasm32", tsify(type = "Map<string, any> | undefined"))]
pub metadata: Option<HashMap<String, Value>>,
pub port_type: PortType,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
#[serde(tag = "type", content = "value")]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
pub enum PortType {
#[default]
#[serde(rename = "any")]
Any,
#[serde(rename = "flow")]
Flow,
#[serde(rename = "event")]
Event,
#[serde(rename = "boolean")]
Boolean,
#[serde(rename = "integer")]
Integer,
#[serde(rename = "float")]
Float,
#[serde(rename = "string")]
String,
#[serde(rename = "object")]
Object(String),
#[serde(rename = "array")]
Array(Box<PortType>),
#[serde(rename = "encoded")]
Encoded,
#[serde(rename = "bytes")]
Bytes,
#[serde(rename = "stream")]
Stream,
#[serde(rename = "option")]
Option(Box<PortType>),
}
#[cfg(target_arch = "wasm32")]
impl From<PortType> for JsValue {
fn from(port_type: PortType) -> Self {
use gloo_utils::format::JsValueSerdeExt;
JsValue::from_serde(&port_type).unwrap()
}
}
#[cfg(target_arch = "wasm32")]
impl TryFrom<JsValue> for PortType {
type Error = serde_json::Error;
fn try_from(value: JsValue) -> Result<Self, Self::Error> {
use gloo_utils::format::JsValueSerdeExt;
value.into_serde()
}
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(typescript_custom_section))]
#[allow(dead_code)]
const TS_PORT_TYPE_DEF: &str = r#"
export type PortType =
| { type: "flow" }
| { type: "event" }
| { type: "boolean" }
| { type: "integer" }
| { type: "float" }
| { type: "string" }
| { type: "object", value: string }
| { type: "array", value: PortType }
| { type: "bytes" }
| { type: "stream" }
| { type: "encoded" }
| { type: "any" }
| { type: "option", value: PortType };
"#;
#[derive(Clone, Serialize, Deserialize, Debug, Default)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
#[serde(rename_all = "camelCase")]
pub struct GraphConnection {
pub from: GraphEdge,
pub to: GraphEdge,
#[cfg_attr(target_arch = "wasm32", tsify(type = "Map<string, any> | undefined"))]
pub metadata: Option<HashMap<String, Value>>,
pub data: Option<Value>,
}
#[derive(Clone, Serialize, Deserialize, Debug, Default)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
pub struct GraphIIP {
pub to: GraphEdge,
#[cfg_attr(target_arch = "wasm32", tsify(type = "any"))]
pub data: Value,
#[cfg_attr(target_arch = "wasm32", tsify(type = "Map<string, any> | undefined"))]
pub metadata: Option<HashMap<String, Value>>,
}
#[derive(Clone, Serialize, Deserialize, Debug, Default)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
#[serde(rename_all = "camelCase")]
pub struct GraphGroup {
pub id: String,
pub nodes: Vec<String>,
#[cfg_attr(target_arch = "wasm32", tsify(type = "Map<string, any> | undefined"))]
pub metadata: Option<HashMap<String, Value>>,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
#[serde(rename_all = "camelCase")]
pub struct GraphDependency {
pub graph_name: String,
pub namespace: Option<String>,
pub version_constraint: Option<String>,
pub required: bool,
pub description: Option<String>,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
#[serde(rename_all = "camelCase")]
pub struct ExternalConnection {
pub connection_id: String,
pub target_graph: String,
pub target_namespace: Option<String>,
pub from_process: String,
pub from_port: String,
pub to_process: String,
pub to_port: String,
pub description: Option<String>,
#[cfg_attr(target_arch = "wasm32", tsify(type = "Map<string, any> | undefined"))]
pub metadata: Option<HashMap<String, Value>>,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
#[serde(rename_all = "camelCase")]
pub struct InterfaceDefinition {
pub interface_id: String,
pub process_name: String,
pub port_name: String,
pub data_type: Option<String>,
pub description: Option<String>,
pub required: bool,
#[cfg_attr(target_arch = "wasm32", tsify(type = "Map<string, any> | undefined"))]
pub metadata: Option<HashMap<String, Value>>,
}
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
#[serde(rename_all = "camelCase")]
pub struct GraphExport {
pub case_sensitive: bool,
#[cfg_attr(
target_arch = "wasm32",
tsify(type = "Map<string, any>"),
serde(default = "default_properties")
)]
pub properties: HashMap<String, Value>,
#[serde(default = "default_port")]
pub inports: HashMap<String, GraphEdge>,
#[serde(default = "default_port")]
pub outports: HashMap<String, GraphEdge>,
#[serde(default = "default_groups")]
pub groups: Vec<GraphGroup>,
#[serde(default = "default_processes")]
pub processes: HashMap<String, GraphNode>,
#[serde(default = "default_connections")]
pub connections: Vec<GraphConnection>,
#[serde(
default = "default_graph_dependencies",
skip_serializing_if = "Vec::is_empty"
)]
pub graph_dependencies: Vec<GraphDependency>,
#[serde(
default = "default_external_connections",
skip_serializing_if = "Vec::is_empty"
)]
pub external_connections: Vec<ExternalConnection>,
#[serde(
default = "default_provided_interfaces",
skip_serializing_if = "HashMap::is_empty"
)]
pub provided_interfaces: HashMap<String, InterfaceDefinition>,
#[serde(
default = "default_required_interfaces",
skip_serializing_if = "HashMap::is_empty"
)]
pub required_interfaces: HashMap<String, InterfaceDefinition>,
}
pub fn default_properties() -> HashMap<String, Value> {
HashMap::from_iter([("name".to_string(), json!("My Graph"))])
}
pub fn default_processes() -> HashMap<String, GraphNode> {
HashMap::new()
}
pub fn default_port() -> HashMap<String, GraphEdge> {
HashMap::new()
}
pub fn default_groups() -> Vec<GraphGroup> {
Vec::new()
}
pub fn default_connections() -> Vec<GraphConnection> {
Vec::new()
}
pub fn default_graph_dependencies() -> Vec<GraphDependency> {
Vec::new()
}
pub fn default_external_connections() -> Vec<ExternalConnection> {
Vec::new()
}
pub fn default_provided_interfaces() -> HashMap<String, InterfaceDefinition> {
HashMap::new()
}
pub fn default_required_interfaces() -> HashMap<String, InterfaceDefinition> {
HashMap::new()
}
type EventValue = Value;
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(namespace))]
#[serde(tag = "_type")]
pub enum GraphEvents {
AddNode(EventValue),
RemoveNode(EventValue),
RenameNode(EventValue),
ChangeNode(EventValue),
AddConnection(EventValue),
RemoveConnection(EventValue),
ChangeConnection(EventValue),
AddInitial(EventValue),
RemoveInitial(EventValue),
ChangeProperties(EventValue),
AddGroup(EventValue),
RemoveGroup(EventValue),
RenameGroup(EventValue),
ChangeGroup(EventValue),
AddInport(EventValue),
RemoveInport(EventValue),
RenameInport(EventValue),
ChangeInport(EventValue),
AddOutport(EventValue),
RemoveOutport(EventValue),
RenameOutport(EventValue),
ChangeOutport(EventValue),
StartTransaction(EventValue),
EndTransaction(EventValue),
Transaction(EventValue),
#[default]
None,
}
#[derive(Debug, Clone)]
pub enum GraphError {
NodeNotFound(String),
DuplicateNode(String),
InvalidConnection { from: String, to: String },
CycleDetected,
InvalidOperation(String),
}
impl std::fmt::Display for GraphError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GraphError::NodeNotFound(id) => write!(f, "Node not found: {}", id),
GraphError::DuplicateNode(id) => write!(f, "Node already exists: {}", id),
GraphError::InvalidConnection { from, to } => {
write!(f, "Invalid connection from {} to {}", from, to)
}
GraphError::CycleDetected => write!(f, "Cycle detected in graph"),
GraphError::InvalidOperation(msg) => write!(f, "Invalid operation: {}", msg),
}
}
}
impl std::error::Error for GraphError {}
#[derive(Clone, Serialize, Deserialize, Debug)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
#[serde(rename_all = "camelCase")]
pub struct WorkspaceGraphExport {
#[serde(flatten)]
pub graph: GraphExport,
pub workspace_metadata: WorkspaceMetadata,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
#[serde(rename_all = "camelCase")]
pub struct WorkspaceMetadata {
pub discovered_namespace: String,
pub source_path: String,
pub source_format: WorkspaceFileFormat,
pub discovered_at: String,
pub file_size: u64,
pub last_modified: Option<String>,
pub resolved_dependencies: Vec<ResolvedDependency>,
pub auto_connections: Vec<AutoDiscoveredConnection>,
pub interface_analysis: InterfaceAnalysis,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
pub enum WorkspaceFileFormat {
Json,
Yaml,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
#[serde(rename_all = "camelCase")]
pub struct ResolvedDependency {
pub target_graph: String,
pub target_namespace: String,
pub resolved: bool,
pub version_constraint: Option<String>,
pub resolution_status: DependencyResolutionStatus,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
pub enum DependencyResolutionStatus {
Resolved,
NotFound,
VersionMismatch,
CircularDependency,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
#[serde(rename_all = "camelCase")]
pub struct AutoDiscoveredConnection {
pub connection_id: String,
pub from_graph: String,
pub from_namespace: String,
pub from_interface: String,
pub to_graph: String,
pub to_namespace: String,
pub to_interface: String,
pub confidence: f64,
pub discovery_method: DiscoveryMethod,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
pub enum DiscoveryMethod {
ExplicitDeclaration,
InterfaceMatching,
DataTypeCompatibility,
NamingConvention,
DependencyAnalysis,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
#[serde(rename_all = "camelCase")]
#[derive(Default)]
pub struct InterfaceAnalysis {
pub provided_count: usize,
pub required_count: usize,
pub compatibility_scores: HashMap<String, f64>,
pub type_mismatches: Vec<InterfaceTypeMismatch>,
pub unused_provided: Vec<String>,
pub unsatisfied_required: Vec<String>,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
#[serde(rename_all = "camelCase")]
pub struct InterfaceTypeMismatch {
pub provided_interface: String,
pub provided_type: Option<String>,
pub required_interface: String,
pub required_type: Option<String>,
pub target_graph: String,
pub severity: MismatchSeverity,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
#[cfg_attr(target_arch = "wasm32", tsify(into_wasm_abi))]
#[cfg_attr(target_arch = "wasm32", tsify(from_wasm_abi))]
pub enum MismatchSeverity {
Warning,
Error,
Critical,
}
impl WorkspaceGraphExport {
pub fn from_graph_export(graph: GraphExport, workspace_metadata: WorkspaceMetadata) -> Self {
WorkspaceGraphExport {
graph,
workspace_metadata,
}
}
pub fn into_graph_export(self) -> GraphExport {
self.graph
}
pub fn graph_export(&self) -> &GraphExport {
&self.graph
}
pub fn graph_export_mut(&mut self) -> &mut GraphExport {
&mut self.graph
}
pub fn graph_name(&self) -> Option<&str> {
self.graph.properties.get("name").and_then(|v| v.as_str())
}
pub fn namespace(&self) -> &str {
&self.workspace_metadata.discovered_namespace
}
pub fn has_unresolved_dependencies(&self) -> bool {
self.workspace_metadata
.resolved_dependencies
.iter()
.any(|dep| !dep.resolved)
}
pub fn get_confident_auto_connections(&self, threshold: f64) -> Vec<&AutoDiscoveredConnection> {
self.workspace_metadata
.auto_connections
.iter()
.filter(|conn| conn.confidence >= threshold)
.collect()
}
pub fn is_compatible_with(&self, other_graph_name: &str) -> Option<f64> {
self.workspace_metadata
.interface_analysis
.compatibility_scores
.get(other_graph_name)
.copied()
}
}
impl Default for WorkspaceMetadata {
fn default() -> Self {
WorkspaceMetadata {
discovered_namespace: "default".to_string(),
source_path: "unknown".to_string(),
source_format: WorkspaceFileFormat::Json,
discovered_at: chrono::Utc::now().to_rfc3339(),
file_size: 0,
last_modified: None,
resolved_dependencies: Vec::new(),
auto_connections: Vec::new(),
interface_analysis: InterfaceAnalysis::default(),
}
}
}
#[derive(Debug, Default)]
pub struct FlowValidation {
pub cycles: Vec<Vec<String>>,
pub orphaned_nodes: Vec<String>,
pub port_mismatches: Vec<PortMismatch>,
}
#[derive(Debug)]
pub struct DataFlowPath {
pub nodes: Vec<String>,
pub transforms: Vec<DataTransform>,
}
#[derive(Debug)]
pub struct DataTransform {
pub node: String,
pub operation: String,
pub input_type: String,
pub output_type: String,
}
#[derive(Debug, Clone)]
pub struct ExecutionPath {
pub nodes: Vec<String>,
pub estimated_time: f32,
pub resource_requirements: HashMap<String, f32>,
}
#[derive(Debug, Default)]
pub struct ParallelismAnalysis {
pub parallel_branches: Vec<Subgraph>,
pub pipeline_stages: Vec<PipelineStage>,
pub max_parallelism: usize,
}
#[derive(Debug)]
pub enum Bottleneck {
HighDegree(String),
SequentialChain(Vec<String>),
}
#[derive(Debug, Default)]
pub struct Subgraph {
pub nodes: Vec<String>,
pub internal_connections: Vec<GraphConnection>,
pub entry_points: Vec<String>,
pub exit_points: Vec<String>,
}
#[derive(Debug)]
pub struct PipelineStage {
pub level: usize,
pub nodes: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct SubgraphAnalysis {
pub node_count: usize,
pub connection_count: usize,
pub entry_points: Vec<String>,
pub exit_points: Vec<String>,
pub is_cyclic: bool,
pub max_depth: usize,
pub branching_factor: f64,
}
#[derive(Debug)]
pub struct CycleAnalysis {
pub total_cycles: usize,
pub cycle_lengths: Vec<usize>,
pub nodes_in_cycles: HashSet<String>,
pub longest_cycle: Option<Vec<String>>,
pub shortest_cycle: Option<Vec<String>>,
}
#[derive(Debug)]
pub struct OrphanedNodeAnalysis {
pub total_orphaned: usize,
pub completely_isolated: Vec<String>,
pub unreachable: Vec<String>,
pub disconnected_groups: Vec<Vec<String>>,
}
#[derive(Debug, Clone)]
pub struct PortMismatch {
pub from_node: String,
pub from_port: String,
pub from_type: PortType,
pub to_node: String,
pub to_port: String,
pub to_type: PortType,
pub reason: String,
}
impl std::fmt::Display for PortMismatch {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Port type mismatch: {}:{} ({:?}) -> {}:{} ({:?}): {}",
self.from_node,
self.from_port,
self.from_type,
self.to_node,
self.to_port,
self.to_type,
self.reason
)
}
}
#[derive(Clone, Debug)]
pub struct NodePosition {
pub x: f32,
pub y: f32,
}
#[derive(Clone, Debug)]
pub struct NodeDimensions {
pub width: f32,
pub height: f32,
pub anchor: AnchorPoint,
}
#[derive(Clone, Debug)]
pub struct AnchorPoint {
pub x: f32, pub y: f32, }
#[derive(Clone)]
pub enum OptimizationSuggestion {
ParallelizableChain {
nodes: Vec<String>,
},
RedundantNode {
node: String,
reason: String,
},
ResourceBottleneck {
resource: String,
severity: f64,
},
DataTypeOptimization {
from: String,
to: String,
suggestion: String,
},
}
#[derive(Default)]
pub struct EnhancedGraphAnalysis {
pub parallelism: ParallelismAnalysis,
pub estimated_execution_time: f64,
pub resource_requirements: HashMap<String, f64>,
pub optimization_suggestions: Vec<OptimizationSuggestion>,
pub performance_bottlenecks: Vec<Bottleneck>,
}