Skip to main content

nounsql_core/
ir.rs

1use indexmap::IndexMap;
2
3use crate::dict::Compound;
4use crate::span::Span;
5
6#[cfg(feature = "serde")]
7use serde::Serialize;
8
9/// 定義がどこから来たか。診断で定義元を指すのに使う。
10///
11/// 直列化では種別と名前だけを出す。span はソースが無いと意味を持たない。
12#[cfg_attr(
13    feature = "serde",
14    derive(Serialize),
15    serde(tag = "kind", rename_all = "camelCase")
16)]
17#[derive(Debug, Clone)]
18pub enum Origin {
19    /// テーブル定義に直接書かれた。
20    Own,
21    Mixin {
22        name: String,
23        #[cfg_attr(feature = "serde", serde(skip))]
24        def_span: Span,
25    },
26    Blueprint {
27        name: String,
28        #[cfg_attr(feature = "serde", serde(skip))]
29        def_span: Span,
30        #[cfg_attr(feature = "serde", serde(skip))]
31        apply_span: Span,
32    },
33    /// `belongs_to` / `associate` が生成した。
34    Generated { by: String },
35}
36
37#[cfg_attr(feature = "serde", derive(Serialize), serde(rename_all = "camelCase"))]
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub enum Val {
40    Literal(String),
41    Eval(String),
42}
43
44#[cfg_attr(feature = "serde", derive(Serialize), serde(rename_all = "camelCase"))]
45#[derive(Debug, Clone)]
46pub struct Column {
47    pub name: String,
48    #[cfg_attr(feature = "serde", serde(rename = "type"))]
49    pub ty: String,
50    pub null: bool,
51    pub default: Option<Val>,
52    pub on_update: Option<Val>,
53    pub comment: Option<String>,
54    pub origin: Origin,
55    #[cfg_attr(feature = "serde", serde(skip))]
56    pub span: Span,
57}
58
59#[cfg_attr(feature = "serde", derive(Serialize), serde(rename_all = "camelCase"))]
60#[derive(Debug, Clone)]
61pub struct Index {
62    pub name: String,
63    pub columns: Vec<String>,
64    pub unique: bool,
65    #[cfg_attr(feature = "serde", serde(skip))]
66    pub span: Span,
67}
68
69#[cfg_attr(feature = "serde", derive(Serialize), serde(rename_all = "camelCase"))]
70#[derive(Debug, Clone)]
71pub struct ForeignKey {
72    /// FKを持つ側の関連名。DDL には出ない。
73    pub alias: String,
74    pub columns: Vec<String>,
75    pub ref_table: String,
76    pub ref_columns: Vec<String>,
77    pub on_delete: String,
78    pub on_update: String,
79    #[cfg_attr(feature = "serde", serde(skip))]
80    pub span: Span,
81}
82
83/// 参照される側から見た関連。DDL には出ない。
84#[cfg_attr(feature = "serde", derive(Serialize), serde(rename_all = "camelCase"))]
85#[derive(Debug, Clone)]
86pub struct Reverse {
87    pub alias: String,
88    /// FKを持つテーブル。
89    pub from_table: String,
90    /// 対応するFK列。
91    pub via: Vec<String>,
92    /// one_to_one か。
93    pub unique: bool,
94    #[cfg_attr(feature = "serde", serde(skip))]
95    pub span: Span,
96}
97
98#[cfg_attr(feature = "serde", derive(Serialize), serde(rename_all = "camelCase"))]
99#[derive(Debug, Clone)]
100pub struct Table {
101    /// 最終テーブル名。
102    pub name: String,
103    /// 由来した名詞。名前の組み立てに使う内部表現。
104    #[cfg_attr(feature = "serde", serde(skip))]
105    pub noun: Option<Compound>,
106    /// 単数形。ORM のモデル名などに使う。
107    pub singular: Option<String>,
108    /// 複数形。
109    pub plural: Option<String>,
110    pub comment: Option<String>,
111    pub columns: IndexMap<String, Column>,
112    pub pk: Vec<String>,
113    pub indexes: Vec<Index>,
114    pub foreign_keys: Vec<ForeignKey>,
115    pub reverses: Vec<Reverse>,
116    pub origin: Origin,
117    #[cfg_attr(feature = "serde", serde(skip))]
118    pub span: Span,
119}
120
121#[cfg_attr(feature = "serde", derive(Serialize), serde(rename_all = "camelCase"))]
122#[derive(Debug, Clone, Default)]
123pub struct Schema {
124    pub tables: Vec<Table>,
125}
126
127impl Schema {
128    pub fn table(&self, name: &str) -> Option<&Table> {
129        self.tables.iter().find(|t| t.name == name)
130    }
131
132    /// 単一の名詞から作られたテーブルを引く。
133    pub fn table_by_noun(&self, name: &str) -> Option<&Table> {
134        self.tables
135            .iter()
136            .find(|t| t.noun.as_ref().and_then(Compound::as_single_noun) == Some(name))
137    }
138}