kotoba_core/
schema.rs

1//! JSON Schema定義に基づいた型定義
2//! Process Network as GTS(DPO)+OpenGraph with Merkle DAG & PG view
3
4use serde::{Deserialize, Serialize};
5#[cfg(feature = "schemars")]
6use schemars::JsonSchema;
7use std::collections::HashMap;
8use sha2::{Sha256, Digest};
9use crate::types::Value;
10
11/// Content ID (CIDv1-like)
12#[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/// ID型(名前付き識別子)
38#[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        // パターン検証: ^[A-Za-z_][A-Za-z0-9_\-:.]{0,127}$
45        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
58/// 属性(プロパティ)型
59pub type Attrs = HashMap<String, Value>;
60
61/// ポート定義
62#[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/// ノード定義
87#[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/// エッジ定義
103#[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, // nodeCID or #nodeCID.portName
111    pub tgt: String, // nodeCID or #nodeCID.portName
112    #[serde(skip_serializing_if = "Option::is_none")]
113    pub attrs: Option<Attrs>,
114}
115
116/// 境界定義
117#[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>, // #nodeCID.portName
122    #[serde(skip_serializing_if = "Option::is_none")]
123    pub constraints: Option<Attrs>,
124}
125
126/// グラフのコア構造
127#[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/// タイピング情報
139#[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/// グラフ型
149#[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/// グラフインスタンス
160#[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/// グラフ種別
172#[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/// 写像定義
183#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
184#[cfg_attr(feature = "schemars", derive(JsonSchema))]
185pub struct Morphisms {
186    pub node_map: HashMap<String, String>, // fromCID -> toCID
187    #[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/// NAC(Negative Application Condition)
194#[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/// 適用条件
203#[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/// 効果定義
227#[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/// DPOルール定義
239#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
240#[cfg_attr(feature = "schemars", derive(JsonSchema))]
241pub struct RuleDPO {
242    pub id: Id,
243    pub l: GraphInstance, // Left-hand side (pattern)
244    pub k: GraphInstance, // Context
245    pub r: GraphInstance, // Right-hand side (replacement)
246    pub m_l: Morphisms,   // K -> L
247    pub m_r: Morphisms,   // K -> R
248    #[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/// コンポーネントインターフェース
257#[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/// コンポーネント定義
265#[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/// 戦略定義
277#[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/// 戦略本体
285#[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/// クエリ定義
305#[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/// クエリコスト
319#[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/// クエリ制限
339#[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/// Property Graph View
349#[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/// PG頂点
359#[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/// PGエッジ
371#[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/// PGマッピング
385#[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/// メインモデル
395#[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/// メタ情報
415#[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/// CIDアルゴリズム設定
430#[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}