flow_lib/config/
client.rs

1//! Parse JS front-end flow config into back-end flow config
2
3use 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/// A row of `flows` table and `flow_deployments_flows.data` column
19#[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    // TODO: remove default
27    #[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    /// Serialize data for `flow_deployments_flows.data` column.
39    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    // for WASM node
291    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}