1use serde::{Deserialize, Serialize};
5#[cfg(feature = "schemars")]
6use schemars::JsonSchema;
7use std::collections::HashMap;
8use sha2::{Sha256, Digest};
9use crate::types::Value;
10
11#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
13#[cfg_attr(feature = "schemars", derive(JsonSchema))]
14pub struct Cid(String);
15
16impl Cid {
17 pub fn new(hash: &str) -> Self {
19 Self(hash.to_string())
20 }
21
22 pub fn as_str(&self) -> &str {
24 &self.0
25 }
26
27 pub fn from_sha256(hash: [u8; 32]) -> Self {
29 use sha2::{Sha256, Digest};
30 let mut hasher = Sha256::new();
31 hasher.update(hash);
32 let result = hasher.finalize();
33 Self(hex::encode(result))
34 }
35
36 pub fn from_blake3(hash: [u8; 32]) -> Self {
38 let result = blake3::hash(&hash);
39 Self(result.to_hex().to_string())
40 }
41}
42
43impl std::fmt::Display for Cid {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 write!(f, "{}", self.0)
46 }
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
51#[cfg_attr(feature = "schemars", derive(JsonSchema))]
52pub struct Id(String);
53
54impl Id {
55 pub fn new(s: &str) -> Result<Self, String> {
56 let pattern = regex::Regex::new(r"^[A-Za-z_][A-Za-z0-9_\-:.]{0,127}$").unwrap();
58 if pattern.is_match(s) {
59 Ok(Self(s.to_string()))
60 } else {
61 Err("Invalid ID format".to_string())
62 }
63 }
64
65 pub fn as_str(&self) -> &str {
66 &self.0
67 }
68}
69
70pub type Attrs = HashMap<String, Value>;
72
73#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
75#[cfg_attr(feature = "schemars", derive(JsonSchema))]
76pub struct Port {
77 pub name: String,
78 pub direction: PortDirection,
79 #[serde(skip_serializing_if = "Option::is_none")]
80 pub r#type: Option<String>,
81 #[serde(skip_serializing_if = "Option::is_none")]
82 pub multiplicity: Option<String>,
83 #[serde(skip_serializing_if = "Option::is_none")]
84 pub attrs: Option<Attrs>,
85}
86
87#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
88#[cfg_attr(feature = "schemars", derive(JsonSchema))]
89pub enum PortDirection {
90 #[serde(rename = "in")]
91 In,
92 #[serde(rename = "out")]
93 Out,
94 #[serde(rename = "bidirectional")]
95 Bidirectional,
96}
97
98#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
100#[cfg_attr(feature = "schemars", derive(JsonSchema))]
101pub struct Node {
102 pub cid: Cid,
103 #[serde(skip_serializing_if = "Vec::is_empty")]
104 pub labels: Vec<String>,
105 pub r#type: String,
106 #[serde(default)]
107 pub ports: Vec<Port>,
108 #[serde(skip_serializing_if = "Option::is_none")]
109 pub attrs: Option<Attrs>,
110 #[serde(skip_serializing_if = "Option::is_none")]
111 pub component_ref: Option<String>,
112}
113
114#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
116#[cfg_attr(feature = "schemars", derive(JsonSchema))]
117pub struct Edge {
118 pub cid: Cid,
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub label: Option<String>,
121 pub r#type: String,
122 pub src: String, pub tgt: String, #[serde(skip_serializing_if = "Option::is_none")]
125 pub attrs: Option<Attrs>,
126}
127
128#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
130#[cfg_attr(feature = "schemars", derive(JsonSchema))]
131pub struct Boundary {
132 #[serde(skip_serializing_if = "Vec::is_empty")]
133 pub expose: Vec<String>, #[serde(skip_serializing_if = "Option::is_none")]
135 pub constraints: Option<Attrs>,
136}
137
138#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
140#[cfg_attr(feature = "schemars", derive(JsonSchema))]
141pub struct GraphCore {
142 pub nodes: Vec<Node>,
143 pub edges: Vec<Edge>,
144 #[serde(skip_serializing_if = "Option::is_none")]
145 pub boundary: Option<Boundary>,
146 #[serde(skip_serializing_if = "Option::is_none")]
147 pub attrs: Option<Attrs>,
148}
149
150#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
152#[cfg_attr(feature = "schemars", derive(JsonSchema))]
153pub struct Typing {
154 #[serde(skip_serializing_if = "HashMap::is_empty")]
155 pub node_types: HashMap<String, String>,
156 #[serde(skip_serializing_if = "HashMap::is_empty")]
157 pub edge_types: HashMap<String, String>,
158}
159
160#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
162#[cfg_attr(feature = "schemars", derive(JsonSchema))]
163pub struct GraphType {
164 #[serde(flatten)]
165 pub core: GraphCore,
166 pub kind: GraphKind,
167 pub cid: Cid,
168 pub typing: Option<Typing>,
169}
170
171#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
173#[cfg_attr(feature = "schemars", derive(JsonSchema))]
174pub struct GraphInstance {
175 #[serde(flatten)]
176 pub core: GraphCore,
177 pub kind: GraphKind,
178 pub cid: Cid,
179 #[serde(skip_serializing_if = "Option::is_none")]
180 pub typing: Option<Typing>,
181}
182
183#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
185#[cfg_attr(feature = "schemars", derive(JsonSchema))]
186pub enum GraphKind {
187 #[serde(rename = "type")]
188 Type,
189 #[serde(rename = "instance")]
190 Instance,
191}
192
193#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
195#[cfg_attr(feature = "schemars", derive(JsonSchema))]
196pub struct Morphisms {
197 pub node_map: HashMap<String, String>, #[serde(skip_serializing_if = "HashMap::is_empty")]
199 pub edge_map: HashMap<String, String>,
200 #[serde(skip_serializing_if = "HashMap::is_empty")]
201 pub port_map: HashMap<String, String>,
202}
203
204#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
206#[cfg_attr(feature = "schemars", derive(JsonSchema))]
207pub struct Nac {
208 pub id: Id,
209 pub graph: GraphInstance,
210 pub morphism_from_l: Morphisms,
211}
212
213#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
215#[cfg_attr(feature = "schemars", derive(JsonSchema))]
216pub struct ApplicationCondition {
217 #[serde(default = "default_injective")]
218 pub injective: bool,
219 #[serde(default = "default_dangling")]
220 pub dangling: DanglingMode,
221 #[serde(skip_serializing_if = "Option::is_none")]
222 pub attrs_guard: Option<Attrs>,
223}
224
225fn default_injective() -> bool { true }
226fn default_dangling() -> DanglingMode { DanglingMode::Forbid }
227
228#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
229#[cfg_attr(feature = "schemars", derive(JsonSchema))]
230pub enum DanglingMode {
231 #[serde(rename = "forbid")]
232 Forbid,
233 #[serde(rename = "allow-with-cleanup")]
234 AllowWithCleanup,
235}
236
237#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
239#[cfg_attr(feature = "schemars", derive(JsonSchema))]
240pub struct Effects {
241 #[serde(skip_serializing_if = "Option::is_none")]
242 pub cost: Option<f64>,
243 #[serde(skip_serializing_if = "Vec::is_empty")]
244 pub labels_add: Vec<String>,
245 #[serde(skip_serializing_if = "Vec::is_empty")]
246 pub labels_remove: Vec<String>,
247}
248
249#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
251#[cfg_attr(feature = "schemars", derive(JsonSchema))]
252pub struct RuleDPO {
253 pub id: Id,
254 pub l: GraphInstance, pub k: GraphInstance, pub r: GraphInstance, pub m_l: Morphisms, pub m_r: Morphisms, #[serde(skip_serializing_if = "Vec::is_empty")]
260 pub nacs: Vec<Nac>,
261 #[serde(skip_serializing_if = "Option::is_none")]
262 pub app_cond: Option<ApplicationCondition>,
263 #[serde(skip_serializing_if = "Option::is_none")]
264 pub effects: Option<Effects>,
265}
266
267#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
269#[cfg_attr(feature = "schemars", derive(JsonSchema))]
270pub struct ComponentInterface {
271 pub in_ports: Vec<String>,
272 pub out_ports: Vec<String>,
273}
274
275#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
277#[cfg_attr(feature = "schemars", derive(JsonSchema))]
278pub struct Component {
279 pub id: Id,
280 pub graph: GraphInstance,
281 pub interface: ComponentInterface,
282 #[serde(skip_serializing_if = "Option::is_none")]
283 pub params: Option<Attrs>,
284 pub cid: Cid,
285}
286
287#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
289#[cfg_attr(feature = "schemars", derive(JsonSchema))]
290pub struct Strategy {
291 pub id: Id,
292 pub body: StrategyBody,
293}
294
295#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
297#[cfg_attr(feature = "schemars", derive(JsonSchema))]
298pub struct StrategyBody {
299 #[serde(skip_serializing_if = "Vec::is_empty")]
300 pub seq: Vec<Strategy>,
301 #[serde(skip_serializing_if = "Vec::is_empty")]
302 pub choice: Vec<Strategy>,
303 #[serde(skip_serializing_if = "Option::is_none")]
304 pub repeat: Option<Box<Strategy>>,
305 #[serde(skip_serializing_if = "Option::is_none")]
306 pub guard: Option<Box<Query>>,
307 #[serde(skip_serializing_if = "Option::is_none")]
308 pub apply: Option<String>,
309 #[serde(skip_serializing_if = "Option::is_none")]
310 pub scope: Option<String>,
311 #[serde(skip_serializing_if = "Option::is_none")]
312 pub max_parallel: Option<u32>,
313}
314
315#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
317#[cfg_attr(feature = "schemars", derive(JsonSchema))]
318pub struct Query {
319 pub id: Id,
320 pub pattern: GraphInstance,
321 #[serde(skip_serializing_if = "Vec::is_empty")]
322 pub nacs: Vec<Nac>,
323 #[serde(skip_serializing_if = "Option::is_none")]
324 pub cost: Option<QueryCost>,
325 #[serde(skip_serializing_if = "Option::is_none")]
326 pub limits: Option<QueryLimits>,
327}
328
329#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
331#[cfg_attr(feature = "schemars", derive(JsonSchema))]
332pub struct QueryCost {
333 #[serde(default = "default_objective")]
334 pub objective: CostObjective,
335 pub expr: String,
336}
337
338fn default_objective() -> CostObjective { CostObjective::Min }
339
340#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
341#[cfg_attr(feature = "schemars", derive(JsonSchema))]
342pub enum CostObjective {
343 #[serde(rename = "min")]
344 Min,
345 #[serde(rename = "max")]
346 Max,
347}
348
349#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
351#[cfg_attr(feature = "schemars", derive(JsonSchema))]
352pub struct QueryLimits {
353 #[serde(skip_serializing_if = "Option::is_none")]
354 pub max_steps: Option<u32>,
355 #[serde(skip_serializing_if = "Option::is_none")]
356 pub timeout_ms: Option<u64>,
357}
358
359#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
361#[cfg_attr(feature = "schemars", derive(JsonSchema))]
362pub struct PGView {
363 pub vertices: Vec<PGVertex>,
364 pub edges: Vec<PGEdge>,
365 #[serde(skip_serializing_if = "Option::is_none")]
366 pub mapping: Option<PGMapping>,
367}
368
369#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
371#[cfg_attr(feature = "schemars", derive(JsonSchema))]
372pub struct PGVertex {
373 pub id: String,
374 #[serde(skip_serializing_if = "Vec::is_empty")]
375 pub labels: Vec<String>,
376 #[serde(skip_serializing_if = "Option::is_none")]
377 pub properties: Option<Attrs>,
378 pub origin_cid: Cid,
379}
380
381#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
383#[cfg_attr(feature = "schemars", derive(JsonSchema))]
384pub struct PGEdge {
385 pub id: String,
386 #[serde(skip_serializing_if = "Option::is_none")]
387 pub label: Option<String>,
388 pub out_v: String,
389 pub in_v: String,
390 #[serde(skip_serializing_if = "Option::is_none")]
391 pub properties: Option<Attrs>,
392 pub origin_cid: Cid,
393}
394
395#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
397#[cfg_attr(feature = "schemars", derive(JsonSchema))]
398pub struct PGMapping {
399 #[serde(skip_serializing_if = "HashMap::is_empty")]
400 pub node_to_vertex: HashMap<String, String>,
401 #[serde(skip_serializing_if = "HashMap::is_empty")]
402 pub edge_to_edge: HashMap<String, String>,
403}
404
405#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
407#[cfg_attr(feature = "schemars", derive(JsonSchema))]
408pub struct ProcessNetwork {
409 #[serde(skip_serializing_if = "Option::is_none")]
410 pub meta: Option<MetaInfo>,
411 pub type_graph: GraphType,
412 pub graphs: Vec<GraphInstance>,
413 #[serde(skip_serializing_if = "Vec::is_empty")]
414 pub components: Vec<Component>,
415 #[serde(skip_serializing_if = "Vec::is_empty")]
416 pub rules: Vec<RuleDPO>,
417 #[serde(skip_serializing_if = "Vec::is_empty")]
418 pub strategies: Vec<Strategy>,
419 #[serde(skip_serializing_if = "Vec::is_empty")]
420 pub queries: Vec<Query>,
421 #[serde(skip_serializing_if = "Option::is_none")]
422 pub pg_view: Option<PGView>,
423}
424
425#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
427#[cfg_attr(feature = "schemars", derive(JsonSchema))]
428pub struct MetaInfo {
429 #[serde(default = "default_model")]
430 pub model: String,
431 #[serde(default = "default_version")]
432 pub version: String,
433 #[serde(skip_serializing_if = "Option::is_none")]
434 pub cid_algo: Option<CidAlgorithm>,
435}
436
437fn default_model() -> String { "GTS-DPO-OpenGraph-Merkle".to_string() }
438fn default_version() -> String { "0.2.0".to_string() }
439
440#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
442#[cfg_attr(feature = "schemars", derive(JsonSchema))]
443pub struct CidAlgorithm {
444 pub hash: HashAlgorithm,
445 #[serde(default = "default_multicodec")]
446 pub multicodec: String,
447 #[serde(default = "default_canonical_json")]
448 pub canonical_json: CanonicalJsonMode,
449}
450
451fn default_multicodec() -> String { "dag-json".to_string() }
452fn default_canonical_json() -> CanonicalJsonMode { CanonicalJsonMode::JCS }
453
454#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
455#[cfg_attr(feature = "schemars", derive(JsonSchema))]
456pub enum HashAlgorithm {
457 #[serde(rename = "sha2-256")]
458 Sha2256,
459 #[serde(rename = "blake3-256")]
460 Blake3256,
461}
462
463#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
464#[cfg_attr(feature = "schemars", derive(JsonSchema))]
465pub enum CanonicalJsonMode {
466 #[serde(rename = "JCS-RFC8785")]
467 JCS,
468}