1use crate::{
4 CmdInputDescription, CmdOutputDescription, CommandType, FlowId, FlowRunId, NodeId,
5 SolanaClientConfig, SolanaNet, UserId, ValueType, command::InstructionInfo,
6};
7use serde::{Deserialize, Serialize};
8use serde_json::{Value as JsonValue, value::RawValue};
9use serde_with::serde_as;
10use std::collections::HashMap;
11use uuid::Uuid;
12use value::default::bool_false;
13
14fn default_interflow_instruction_info() -> Result<InstructionInfo, String> {
15 Err("not available".to_string())
16}
17
18#[serde_as]
20#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
21pub struct FlowRow {
22 pub id: FlowId,
23 pub user_id: UserId,
24 pub nodes: Vec<Node>,
25 pub edges: Vec<Edge>,
26 #[serde(default)]
28 #[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
29 pub environment: HashMap<String, String>,
30 pub current_network: Network,
31 pub instructions_bundling: BundlingMode,
32 pub is_public: bool,
33 pub start_shared: bool,
34 pub start_unverified: bool,
35}
36
37impl FlowRow {
38 pub fn data(&self) -> Box<RawValue> {
40 serde_json::value::to_raw_value(self).unwrap()
41 }
42}
43
44#[serde_as]
45#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
46pub struct ClientConfig {
47 pub user_id: UserId,
48 pub id: FlowId,
49 pub nodes: Vec<Node>,
50 pub edges: Vec<Edge>,
51 #[serde(default)]
52 #[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
53 pub environment: HashMap<String, String>,
54 #[serde(default)]
55 #[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
56 pub sol_network: Network,
57 #[serde(default)]
58 pub instructions_bundling: BundlingMode,
59 #[serde(default)]
60 pub partial_config: Option<PartialConfig>,
61 #[serde(default)]
62 pub collect_instructions: bool,
63 #[serde(default)]
64 pub call_depth: u32,
65 #[serde(default = "default_origin")]
66 pub origin: FlowRunOrigin,
67 #[serde(default)]
68 pub signers: JsonValue,
69 #[serde(default = "default_interflow_instruction_info")]
70 pub interflow_instruction_info: Result<InstructionInfo, String>,
71}
72
73const fn default_origin() -> FlowRunOrigin {
74 FlowRunOrigin::Start {}
75}
76
77#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
78pub enum FlowRunOrigin {
79 Start {},
80 StartShared {
81 started_by: UserId,
82 },
83 Interflow {
84 flow_run_id: FlowRunId,
85 node_id: NodeId,
86 times: u32,
87 },
88}
89
90impl Default for FlowRunOrigin {
91 fn default() -> Self {
92 Self::Start {}
93 }
94}
95
96#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
97pub struct ValuesConfig {
98 #[serde(default)]
99 pub nodes: HashMap<NodeId, FlowRunId>,
100 pub default_run_id: Option<FlowRunId>,
101}
102
103#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
104pub struct PartialConfig {
105 pub only_nodes: Vec<NodeId>,
106 pub values_config: ValuesConfig,
107}
108
109#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
110pub enum BundlingMode {
111 #[default]
112 Off,
113 Automatic,
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
117pub struct Network {
118 pub url: String,
119 pub cluster: SolanaNet,
120}
121
122impl From<SolanaClientConfig> for Network {
123 fn from(value: SolanaClientConfig) -> Self {
124 Self {
125 url: value.url,
126 cluster: value.cluster,
127 }
128 }
129}
130
131impl Default for Network {
132 fn default() -> Self {
133 Self {
134 url: SolanaNet::Devnet.url(),
135 cluster: SolanaNet::Devnet,
136 }
137 }
138}
139
140#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
141pub struct Node {
142 pub id: NodeId,
143 pub data: NodeData,
144}
145
146#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
147pub struct NodeData {
148 pub r#type: CommandType,
149 pub node_id: String,
150 pub sources: Vec<Source>,
151 pub targets: Vec<Target>,
152 pub targets_form: TargetsForm,
153 pub instruction_info: Option<InstructionInfo>,
154}
155
156impl NodeData {
157 pub fn inputs(&self) -> Vec<CmdInputDescription> {
158 self.targets.iter().cloned().map(Into::into).collect()
159 }
160 pub fn outputs(&self) -> Vec<CmdOutputDescription> {
161 self.sources.iter().cloned().map(Into::into).collect()
162 }
163}
164
165#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
166pub struct NodeDataSkipWasm {
167 pub r#type: CommandType,
168 pub node_id: String,
169 pub sources: Vec<Source>,
170 pub targets: Vec<Target>,
171 pub targets_form: TargetsFormSkipWasm,
172 pub instruction_info: Option<InstructionInfo>,
173}
174
175impl From<NodeData> for NodeDataSkipWasm {
176 fn from(
177 NodeData {
178 r#type,
179 node_id,
180 sources,
181 targets,
182 targets_form,
183 instruction_info,
184 }: NodeData,
185 ) -> Self {
186 let TargetsForm {
187 form_data, extra, ..
188 } = targets_form;
189 NodeDataSkipWasm {
190 r#type,
191 node_id,
192 sources,
193 targets,
194 targets_form: TargetsFormSkipWasm { form_data, extra },
195 instruction_info,
196 }
197 }
198}
199
200#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
201pub struct Source {
202 pub id: Uuid,
203 pub name: String,
204 pub r#type: ValueType,
205 #[serde(default = "bool_false")]
206 pub optional: bool,
207}
208
209impl From<Source> for CmdOutputDescription {
210 fn from(
211 Source {
212 name,
213 r#type,
214 optional,
215 ..
216 }: Source,
217 ) -> Self {
218 Self {
219 name,
220 r#type,
221 optional,
222 }
223 }
224}
225
226#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
227pub struct Target {
228 pub id: Uuid,
229 pub name: String,
230 pub type_bounds: Vec<ValueType>,
231 pub required: bool,
232 pub passthrough: bool,
233}
234
235impl From<Target> for CmdInputDescription {
236 fn from(
237 Target {
238 name,
239 type_bounds,
240 required,
241 passthrough,
242 ..
243 }: Target,
244 ) -> Self {
245 Self {
246 name,
247 type_bounds,
248 required,
249 passthrough,
250 }
251 }
252}
253
254#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
255pub struct TargetsForm {
256 pub form_data: JsonValue,
257 #[serde(default)]
258 pub extra: Extra,
259
260 pub wasm_bytes: Option<bytes::Bytes>,
261}
262
263#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
264pub struct TargetsFormSkipWasm {
265 pub form_data: JsonValue,
266 #[serde(default)]
267 pub extra: Extra,
268}
269
270impl std::fmt::Debug for TargetsForm {
271 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272 f.debug_struct("TargetsForm")
273 .field("form_data", &self.form_data)
274 .field("extra", &self.extra)
275 .finish_non_exhaustive()
276 }
277}
278
279impl std::fmt::Debug for TargetsFormSkipWasm {
280 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281 f.debug_struct("TargetsForm")
282 .field("form_data", &self.form_data)
283 .field("extra", &self.extra)
284 .finish_non_exhaustive()
285 }
286}
287
288#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
289pub struct Extra {
290 pub supabase_id: Option<i64>,
292 #[serde(flatten)]
293 pub rest: HashMap<String, JsonValue>,
294}
295
296#[derive(Debug, Clone, PartialEq, Eq)]
297pub struct SourceHandle {
298 pub id: Uuid,
299 pub is_passthough: bool,
300}
301
302impl Serialize for SourceHandle {
303 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
304 where
305 S: serde::Serializer,
306 {
307 if self.is_passthough {
308 format!("passthrough-{}", self.id).serialize(s)
309 } else {
310 self.id.serialize(s)
311 }
312 }
313}
314
315impl<'de> Deserialize<'de> for SourceHandle {
316 fn deserialize<D>(d: D) -> Result<Self, D::Error>
317 where
318 D: serde::Deserializer<'de>,
319 {
320 const PREFIX: &str = "passthrough-";
321 let s = String::deserialize(d)?;
322 let (is_passthough, uuid_str) = if s.starts_with(PREFIX) {
323 (true, &s.as_str()[PREFIX.len()..])
324 } else {
325 (false, s.as_str())
326 };
327
328 Ok(Self {
329 is_passthough,
330 id: uuid_str.parse().map_err(serde::de::Error::custom)?,
331 })
332 }
333}
334
335#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
336pub struct Edge {
337 pub source: Uuid,
338 #[serde(rename = "sourceHandle")]
339 pub source_handle: SourceHandle,
340 pub target: Uuid,
341 #[serde(rename = "targetHandle")]
342 pub target_handle: Uuid,
343}