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)]
13#[cfg_attr(feature = "schemars", derive(JsonSchema))]
14pub struct Cid(String);
15
16impl Cid {
17    /// CIDを作成
18    pub fn new(hash: &str) -> Self {
19        Self(hash.to_string())
20    }
21
22    /// CID文字列を取得
23    pub fn as_str(&self) -> &str {
24        &self.0
25    }
26
27    /// SHA-256ハッシュからCIDを作成
28    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    /// BLAKE3ハッシュからCIDを作成
37    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/// ID型(名前付き識別子)
50#[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        // パターン検証: ^[A-Za-z_][A-Za-z0-9_\-:.]{0,127}$
57        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
70/// 属性(プロパティ)型
71pub type Attrs = HashMap<String, Value>;
72
73/// ポート定義
74#[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/// ノード定義
99#[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/// エッジ定義
115#[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, // nodeCID or #nodeCID.portName
123    pub tgt: String, // nodeCID or #nodeCID.portName
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub attrs: Option<Attrs>,
126}
127
128/// 境界定義
129#[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>, // #nodeCID.portName
134    #[serde(skip_serializing_if = "Option::is_none")]
135    pub constraints: Option<Attrs>,
136}
137
138/// グラフのコア構造
139#[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/// タイピング情報
151#[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/// グラフ型
161#[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/// グラフインスタンス
172#[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/// グラフ種別
184#[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/// 写像定義
194#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
195#[cfg_attr(feature = "schemars", derive(JsonSchema))]
196pub struct Morphisms {
197    pub node_map: HashMap<String, String>, // fromCID -> toCID
198    #[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/// NAC(Negative Application Condition)
205#[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/// 適用条件
214#[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/// 効果定義
238#[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/// DPOルール定義
250#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
251#[cfg_attr(feature = "schemars", derive(JsonSchema))]
252pub struct RuleDPO {
253    pub id: Id,
254    pub l: GraphInstance, // Left-hand side (pattern)
255    pub k: GraphInstance, // Context
256    pub r: GraphInstance, // Right-hand side (replacement)
257    pub m_l: Morphisms,   // K -> L
258    pub m_r: Morphisms,   // K -> R
259    #[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/// コンポーネントインターフェース
268#[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/// コンポーネント定義
276#[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/// 戦略定義
288#[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/// 戦略本体
296#[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/// クエリ定義
316#[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/// クエリコスト
330#[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/// クエリ制限
350#[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/// Property Graph View
360#[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/// PG頂点
370#[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/// PGエッジ
382#[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/// PGマッピング
396#[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/// メインモデル
406#[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/// メタ情報
426#[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/// CIDアルゴリズム設定
441#[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}