1use extism_convert::{FromBytes, Json, ToBytes};
2use lazy_regex::regex;
3use serde::{Deserialize, Serialize};
4use serde_yaml::Value;
5use std::collections::{HashMap, HashSet};
6use std::fmt;
7use std::path::PathBuf;
8use std::time::SystemTime;
9
10#[derive(PartialEq, Eq, Hash, Debug, Clone, Deserialize, Serialize)]
14pub struct ArtifactMapping {
15 pub local_file: PathBuf,
16 pub root_relative_target_dir: PathBuf,
17}
18
19#[derive(ToBytes, Serialize, FromBytes, Deserialize)]
20#[encoding(Json)]
21pub struct BoolPayload {
22 pub value: bool,
23}
24
25#[derive(ToBytes, Serialize, FromBytes, Deserialize)]
26#[encoding(Json)]
27pub struct SystemTimePayload {
28 pub value: SystemTime,
29}
30
31#[derive(ToBytes, Serialize, FromBytes, Deserialize, Debug)]
32#[encoding(Json)]
33pub struct DirectoryStructurePayload {
34 pub entries: Vec<FileEntry>,
35}
36
37#[derive(ToBytes, Serialize, FromBytes, Deserialize)]
38#[encoding(Json)]
39pub struct FileWriteOperationPayload {
40 pub relative_path: String,
41 pub contents: String,
42}
43
44#[derive(ToBytes, Serialize, FromBytes, Deserialize)]
45#[encoding(Json)]
46pub struct FileReadOperationInPayload {
47 pub relative_path: String,
48}
49
50#[derive(ToBytes, Serialize, FromBytes, Deserialize)]
51#[encoding(Json)]
52pub struct FileReadOperationOutPayload {
53 pub contents: String,
54}
55
56#[derive(ToBytes, Serialize, FromBytes, Deserialize)]
57#[encoding(Json)]
58pub struct FileReadBase64OperationInPayload {
59 pub relative_path: String,
60}
61
62#[derive(ToBytes, Serialize, FromBytes, Deserialize)]
63#[encoding(Json)]
64pub struct FileReadBase64OperationOutPayload {
65 pub contents: String,
66}
67
68#[derive(Serialize, Deserialize, Debug, FromBytes, ToBytes)]
69#[encoding(Json)]
70pub struct FileEntry {
71 pub relative_path: String,
72 pub is_dir: bool,
73 pub size: u64,
74 pub permissions: String,
75 pub modified: Option<String>,
76 pub created: Option<String>,
77}
78
79#[derive(ToBytes, FromBytes, Serialize, Deserialize, Debug)]
81#[encoding(Json)]
82pub struct NodeProcessingPayload {
83 pub parameter_values: HashMap<String, serde_yaml::Value>,
84 pub node: Node,
85 pub cluster_path: PathBuf,
86}
87
88#[derive(ToBytes, FromBytes, Serialize, Deserialize, Debug)]
89#[encoding(Json)]
90pub struct DummyPayload {}
91
92#[derive(ToBytes, FromBytes, Serialize, Deserialize, Debug)]
93#[encoding(Json)]
94pub struct ExtensionFieldProcessingPayload {
95 pub node_processing_payload: NodeProcessingPayload,
96 pub field_name: String,
97 pub value: serde_yaml::Value,
98}
99
100#[derive(ToBytes, FromBytes, Serialize, Deserialize, Debug)]
101#[encoding(Json)]
102pub struct ClusterProcessingPayload {
103 pub parameter_values: HashMap<String, serde_yaml::Value>,
104 pub cluster_path: PathBuf,
105}
106
107#[derive(ToBytes, FromBytes, Serialize, Deserialize, Debug)]
108#[encoding(Json)]
109pub struct ArchivePayload {
110 pub parameter_values: HashMap<String, serde_yaml::Value>,
111 pub cluster_paths: Vec<PathBuf>,
112}
113
114#[derive(ToBytes, FromBytes, Serialize, Deserialize, Debug)]
115#[encoding(Json)]
116pub struct ExtensionFieldProcessingResult {
120 pub result: anyhow::Result<HashSet<ArtifactMapping>, NodeProcessingError>,
121}
122
123#[derive(ToBytes, FromBytes, Serialize, Deserialize, Debug)]
124#[encoding(Json)]
125pub struct ParamsSchema {
126 pub schema: HashMap<String, (bool, serde_json::Value)>,
127}
128
129#[derive(ToBytes, FromBytes, Serialize, Deserialize, Debug)]
130#[encoding(Json)]
131pub struct ClusterProcessingResult {
132 pub hash_set: HashSet<ArtifactMapping>,
135}
136
137#[derive(Debug, Serialize, Deserialize)]
138pub enum NodeProcessingError {
139 CannotProcessFieldType,
140 Remarks(Vec<String>),
141}
142
143impl NodeProcessingError {
144 pub fn indicates_inability_to_process_field(&self) -> bool {
146 match self {
147 Self::CannotProcessFieldType => true,
148 _ => false,
149 }
150 }
151}
152
153#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
155pub enum EdgeType {
156 All,
157 AtLeastOne,
158}
159
160#[derive(Clone, Debug)]
161pub struct TypedEdge {
162 pub start_id: NodeID,
163 pub end_id: NodeID,
164 pub kind: EdgeType,
165}
166
167#[derive(Debug, Serialize)]
168pub struct UnlockingCondition {
169 pub all_of: HashSet<NodeID>,
170 pub one_of: HashSet<NodeID>,
171}
172
173#[derive(Clone, Debug)]
174pub struct UnloadedPlugin {
175 pub path: String,
176 pub parameters: HashMap<String, Value>,
177}
178
179#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
180pub struct NodeID {
181 pub namespace: String,
183 pub local_id: String,
187}
188
189impl std::fmt::Display for NodeID {
190 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
191 write!(f, "{}__{}", self.namespace, self.local_id)
192 }
193}
194
195impl NodeID {
196 pub fn from_two_part_string(string: &str) -> Result<NodeID, StructuralError> {
197 let string = string;
198 let identifier_regex = regex!("[a-z][a-z_]*");
199 let parts = string.split("__").collect::<Vec<_>>();
200 let invalid_part = parts.iter().find(|p| !identifier_regex.is_match(p));
201 if let Some(part) = invalid_part {
202 Err(StructuralError::InvalidIdentifierError(part.to_string()).into())
203 } else if parts.len() == 1 {
204 Err(StructuralError::NodeMissingNamespace(string.to_string()).into())
205 } else if parts.len() > 2 {
206 Err(StructuralError::NodeMultipleNamespace(string.to_string()).into())
207 } else {
208 Ok(NodeID {
209 namespace: parts[0].to_owned(),
210 local_id: parts[1].to_owned(),
211 })
212 }
213 }
214}
215
216#[derive(Clone, Debug, Serialize, Deserialize)]
221pub struct Node {
222 pub node_id: NodeID,
223 pub title: String,
227 pub extension_fields: HashMap<String, Value>,
228}
229
230#[derive(Debug)]
232pub enum StructuralError {
233 DoubleNode(NodeID), MissingInternalEndpoint(NodeID, NodeID, NodeID), NodeMissingNamespace(String),
236 NodeMultipleNamespace(String),
237 EdgeMultipleNamespace(String, String, String), ClusterBoundary(String, NodeID), InvalidComponentGraph,
240 Cycle(NodeID),
241 DependentRootNode(NodeID, NodeID),
242 UndeclaredRoot(NodeID),
243 IncomingAnyEdge(NodeID, NodeID),
244 OutgoingAllEdge(NodeID, NodeID),
245 InvalidIdentifierError(String),
246}
247
248impl fmt::Display for StructuralError {
249 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250 match self {
251 Self::DoubleNode(id) => write!(f, "Node defined multiple times: {id}"),
252 Self::MissingInternalEndpoint(start_id, end_id, missing_id) => write!(f, "Node {missing_id} mentioned in edge {start_id} → {end_id} does not exist"),
253 Self::NodeMissingNamespace(id) => write!(f, "Node lacks a namespace: {id}"),
254 Self::NodeMultipleNamespace(id) => write!(f, "Node has multiple namespaces: {id}"),
255 Self::EdgeMultipleNamespace(start_id, end_id, namespaced_id) => write!(f, "Node {namespaced_id} mentioned in edge {start_id} → {end_id} is incorrectly namespaced. There should only be one namespace and it should only be explicit if it is not that of the defining cluster."),
256 Self::ClusterBoundary(cluster,reference) => write!(f, "Cluster {} refers to non-existent external node {}", cluster, reference),
257 Self::InvalidComponentGraph => write!(f, "At least one component graph is invalid"),
258 Self::Cycle(id) => write!(f, "Node {} is involved in a cycle", id),
259 Self::DependentRootNode(id, start_id) => write!(f, "Node {} is declared as a root and has at least one incoming edge (from {}). Roots should not have incoming edges.", id, start_id),
260 Self::UndeclaredRoot(id) => write!(f, "Root {} is not declared as a node in the cluster.", id),
261 Self::IncomingAnyEdge(start_id,end_id) => write!(f, "\"At least one\" type edge from {} to {}. These edges can only connect to other clusters in the \"out\" direction.", start_id, end_id),
262 Self::OutgoingAllEdge(start_id,end_id) => write!(f, "\"All\" type edge from {} to {}. These edges can only connect to other clusters in the \"in\" direction.", start_id, end_id),
263 Self::InvalidIdentifierError(identifier) => write!(f, "Invalid identifier {}.", identifier)
264 }
265 }
266}
267
268impl std::error::Error for StructuralError {}
269
270pub type NodeData = (NodeID, String);
272
273pub type EdgeData = EdgeType;
275
276pub type Graph = petgraph::Graph<NodeData, EdgeData>;