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