use crate::{
CmdInputDescription, CmdOutputDescription, CommandType, FlowId, FlowRunId, NodeId,
SolanaClientConfig, SolanaNet, UserId, ValueType, command::InstructionInfo,
};
use serde::{Deserialize, Serialize};
use serde_json::{Value as JsonValue, value::RawValue};
use serde_with::serde_as;
use std::collections::HashMap;
use uuid::Uuid;
use value::default::bool_false;
fn default_interflow_instruction_info() -> Result<InstructionInfo, String> {
Err("not available".to_string())
}
#[serde_as]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FlowRow {
pub id: FlowId,
pub user_id: UserId,
pub nodes: Vec<Node>,
pub edges: Vec<Edge>,
#[serde(default)]
#[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
pub environment: HashMap<String, String>,
pub current_network: Network,
pub instructions_bundling: BundlingMode,
pub is_public: bool,
pub start_shared: bool,
pub start_unverified: bool,
}
impl FlowRow {
pub fn data(&self) -> Box<RawValue> {
serde_json::value::to_raw_value(self).unwrap()
}
}
#[serde_as]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ClientConfig {
pub user_id: UserId,
pub id: FlowId,
pub nodes: Vec<Node>,
pub edges: Vec<Edge>,
#[serde(default)]
#[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
pub environment: HashMap<String, String>,
#[serde(default)]
#[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
pub sol_network: Network,
#[serde(default)]
pub instructions_bundling: BundlingMode,
#[serde(default)]
pub partial_config: Option<PartialConfig>,
#[serde(default)]
pub collect_instructions: bool,
#[serde(default)]
pub call_depth: u32,
#[serde(default = "default_origin")]
pub origin: FlowRunOrigin,
#[serde(default)]
pub signers: JsonValue,
#[serde(default = "default_interflow_instruction_info")]
pub interflow_instruction_info: Result<InstructionInfo, String>,
}
const fn default_origin() -> FlowRunOrigin {
FlowRunOrigin::Start {}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum FlowRunOrigin {
Start {},
StartShared {
started_by: UserId,
},
Interflow {
flow_run_id: FlowRunId,
node_id: NodeId,
times: u32,
},
}
impl Default for FlowRunOrigin {
fn default() -> Self {
Self::Start {}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ValuesConfig {
#[serde(default)]
pub nodes: HashMap<NodeId, FlowRunId>,
pub default_run_id: Option<FlowRunId>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PartialConfig {
pub only_nodes: Vec<NodeId>,
pub values_config: ValuesConfig,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum BundlingMode {
#[default]
Off,
Automatic,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Network {
pub url: String,
pub cluster: SolanaNet,
}
impl From<SolanaClientConfig> for Network {
fn from(value: SolanaClientConfig) -> Self {
Self {
url: value.url,
cluster: value.cluster,
}
}
}
impl Default for Network {
fn default() -> Self {
Self {
url: SolanaNet::Devnet.url(),
cluster: SolanaNet::Devnet,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Node {
pub id: NodeId,
pub data: NodeData,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NodeData {
pub r#type: CommandType,
pub node_id: String,
pub sources: Vec<Source>,
pub targets: Vec<Target>,
pub targets_form: TargetsForm,
pub instruction_info: Option<InstructionInfo>,
}
impl NodeData {
pub fn inputs(&self) -> Vec<CmdInputDescription> {
self.targets.iter().cloned().map(Into::into).collect()
}
pub fn outputs(&self) -> Vec<CmdOutputDescription> {
self.sources.iter().cloned().map(Into::into).collect()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NodeDataSkipWasm {
pub r#type: CommandType,
pub node_id: String,
pub sources: Vec<Source>,
pub targets: Vec<Target>,
pub targets_form: TargetsFormSkipWasm,
pub instruction_info: Option<InstructionInfo>,
}
impl From<NodeData> for NodeDataSkipWasm {
fn from(
NodeData {
r#type,
node_id,
sources,
targets,
targets_form,
instruction_info,
}: NodeData,
) -> Self {
let TargetsForm {
form_data, extra, ..
} = targets_form;
NodeDataSkipWasm {
r#type,
node_id,
sources,
targets,
targets_form: TargetsFormSkipWasm { form_data, extra },
instruction_info,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Source {
pub id: Uuid,
pub name: String,
pub r#type: ValueType,
#[serde(default = "bool_false")]
pub optional: bool,
}
impl From<Source> for CmdOutputDescription {
fn from(
Source {
name,
r#type,
optional,
..
}: Source,
) -> Self {
Self {
name,
r#type,
optional,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Target {
pub id: Uuid,
pub name: String,
pub type_bounds: Vec<ValueType>,
pub required: bool,
pub passthrough: bool,
}
impl From<Target> for CmdInputDescription {
fn from(
Target {
name,
type_bounds,
required,
passthrough,
..
}: Target,
) -> Self {
Self {
name,
type_bounds,
required,
passthrough,
}
}
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TargetsForm {
pub form_data: JsonValue,
#[serde(default)]
pub extra: Extra,
pub wasm_bytes: Option<bytes::Bytes>,
}
impl Default for TargetsForm {
fn default() -> Self {
Self {
form_data: JsonValue::Object(<_>::default()),
extra: Extra::default(),
wasm_bytes: None,
}
}
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TargetsFormSkipWasm {
pub form_data: JsonValue,
#[serde(default)]
pub extra: Extra,
}
impl std::fmt::Debug for TargetsForm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TargetsForm")
.field("form_data", &self.form_data)
.field("extra", &self.extra)
.finish_non_exhaustive()
}
}
impl std::fmt::Debug for TargetsFormSkipWasm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TargetsForm")
.field("form_data", &self.form_data)
.field("extra", &self.extra)
.finish_non_exhaustive()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct Extra {
pub supabase_id: Option<i64>,
#[serde(flatten)]
pub rest: HashMap<String, JsonValue>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SourceHandle {
pub id: Uuid,
pub is_passthough: bool,
}
impl Serialize for SourceHandle {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if self.is_passthough {
format!("passthrough-{}", self.id).serialize(s)
} else {
self.id.serialize(s)
}
}
}
impl<'de> Deserialize<'de> for SourceHandle {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
const PREFIX: &str = "passthrough-";
let s = String::deserialize(d)?;
let (is_passthough, uuid_str) = if s.starts_with(PREFIX) {
(true, &s.as_str()[PREFIX.len()..])
} else {
(false, s.as_str())
};
Ok(Self {
is_passthough,
id: uuid_str.parse().map_err(serde::de::Error::custom)?,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Edge {
pub source: Uuid,
#[serde(rename = "sourceHandle")]
pub source_handle: SourceHandle,
pub target: Uuid,
#[serde(rename = "targetHandle")]
pub target_handle: Uuid,
}