Skip to main content

jellyflow_runtime/profile/
domain.rs

1use serde::{Deserialize, Serialize};
2
3use jellyflow_core::core::{EdgeKind, NodeKindKey, PortKey};
4use jellyflow_core::types::TypeDesc;
5
6/// Renderer-neutral domain metadata exposed by a graph profile.
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
8pub struct GraphProfileMetadata {
9    /// Stable profile key for adapters and docs.
10    #[serde(default, skip_serializing_if = "Option::is_none")]
11    pub key: Option<String>,
12    /// Human-readable title.
13    #[serde(default, skip_serializing_if = "Option::is_none")]
14    pub title: Option<String>,
15    /// Node field schemas keyed by node kind.
16    #[serde(default, skip_serializing_if = "Vec::is_empty")]
17    pub node_fields: Vec<NodeFieldSchemaSet>,
18    /// Variable surfaces available to nodes or edges.
19    #[serde(default, skip_serializing_if = "Vec::is_empty")]
20    pub variable_surfaces: Vec<VariableSurfaceDescriptor>,
21    /// Connection rule labels for adapter display.
22    #[serde(default, skip_serializing_if = "Vec::is_empty")]
23    pub connection_rules: Vec<ConnectionRuleDescriptor>,
24}
25
26impl GraphProfileMetadata {
27    pub fn new(key: impl Into<String>, title: impl Into<String>) -> Self {
28        Self {
29            key: Some(key.into()),
30            title: Some(title.into()),
31            ..Self::default()
32        }
33    }
34
35    pub fn with_node_fields(mut self, fields: NodeFieldSchemaSet) -> Self {
36        self.node_fields.push(fields);
37        self
38    }
39
40    pub fn with_variable_surface(mut self, surface: VariableSurfaceDescriptor) -> Self {
41        self.variable_surfaces.push(surface);
42        self
43    }
44
45    pub fn with_connection_rule(mut self, rule: ConnectionRuleDescriptor) -> Self {
46        self.connection_rules.push(rule);
47        self
48    }
49}
50
51/// Field schemas attached to one node kind.
52#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
53pub struct NodeFieldSchemaSet {
54    pub node_kind: NodeKindKey,
55    #[serde(default, skip_serializing_if = "Vec::is_empty")]
56    pub fields: Vec<FieldSchema>,
57}
58
59impl NodeFieldSchemaSet {
60    pub fn new(node_kind: impl Into<NodeKindKey>) -> Self {
61        Self {
62            node_kind: node_kind.into(),
63            fields: Vec::new(),
64        }
65    }
66
67    pub fn with_field(mut self, field: FieldSchema) -> Self {
68        self.fields.push(field);
69        self
70    }
71}
72
73/// Renderer-neutral node parameter field schema.
74#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
75pub struct FieldSchema {
76    pub key: String,
77    pub label: String,
78    #[serde(default, skip_serializing_if = "Option::is_none")]
79    pub ty: Option<TypeDesc>,
80    #[serde(default)]
81    pub required: bool,
82    #[serde(default, skip_serializing_if = "Vec::is_empty")]
83    pub hints: Vec<ValidationHint>,
84    #[serde(default, skip_serializing_if = "Option::is_none")]
85    pub port_anchor: Option<PortKey>,
86}
87
88impl FieldSchema {
89    pub fn new(key: impl Into<String>, label: impl Into<String>) -> Self {
90        Self {
91            key: key.into(),
92            label: label.into(),
93            ty: None,
94            required: false,
95            hints: Vec::new(),
96            port_anchor: None,
97        }
98    }
99
100    pub fn with_type(mut self, ty: TypeDesc) -> Self {
101        self.ty = Some(ty);
102        self
103    }
104
105    pub fn required(mut self) -> Self {
106        self.required = true;
107        self
108    }
109
110    pub fn with_hint(mut self, hint: ValidationHint) -> Self {
111        self.hints.push(hint);
112        self
113    }
114
115    pub fn with_port_anchor(mut self, port: impl Into<PortKey>) -> Self {
116        self.port_anchor = Some(port.into());
117        self
118    }
119}
120
121/// Adapter-facing validation hint.
122#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
123pub struct ValidationHint {
124    pub code: String,
125    pub message: String,
126}
127
128impl ValidationHint {
129    pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
130        Self {
131            code: code.into(),
132            message: message.into(),
133        }
134    }
135}
136
137/// Variables available from one domain surface.
138#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
139pub struct VariableSurfaceDescriptor {
140    pub key: String,
141    pub label: String,
142    #[serde(default, skip_serializing_if = "Vec::is_empty")]
143    pub variables: Vec<VariableDescriptor>,
144}
145
146impl VariableSurfaceDescriptor {
147    pub fn new(key: impl Into<String>, label: impl Into<String>) -> Self {
148        Self {
149            key: key.into(),
150            label: label.into(),
151            variables: Vec::new(),
152        }
153    }
154
155    pub fn with_variable(mut self, variable: VariableDescriptor) -> Self {
156        self.variables.push(variable);
157        self
158    }
159}
160
161/// One variable exposed by a domain profile.
162#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
163pub struct VariableDescriptor {
164    pub key: String,
165    pub label: String,
166    #[serde(default, skip_serializing_if = "Option::is_none")]
167    pub ty: Option<TypeDesc>,
168}
169
170impl VariableDescriptor {
171    pub fn new(key: impl Into<String>, label: impl Into<String>) -> Self {
172        Self {
173            key: key.into(),
174            label: label.into(),
175            ty: None,
176        }
177    }
178
179    pub fn with_type(mut self, ty: TypeDesc) -> Self {
180        self.ty = Some(ty);
181        self
182    }
183}
184
185/// Human-readable connection rule metadata for adapters.
186#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
187pub struct ConnectionRuleDescriptor {
188    pub key: String,
189    pub label: String,
190    #[serde(default, skip_serializing_if = "Option::is_none")]
191    pub edge_kind: Option<EdgeKind>,
192}
193
194impl ConnectionRuleDescriptor {
195    pub fn new(key: impl Into<String>, label: impl Into<String>) -> Self {
196        Self {
197            key: key.into(),
198            label: label.into(),
199            edge_kind: None,
200        }
201    }
202
203    pub fn for_edge_kind(mut self, edge_kind: EdgeKind) -> Self {
204        self.edge_kind = Some(edge_kind);
205        self
206    }
207}