-
Notifications
You must be signed in to change notification settings - Fork 216
/
datamodel.rs
167 lines (143 loc) Β· 6.37 KB
/
datamodel.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
use crate::field::{Field, RelationField, ScalarField};
use crate::model::Model;
use crate::r#enum::Enum;
use crate::relation_info::RelationInfo;
/// Entities in the datamodel can be flagged as `is_commented_out`. This let's the renderer
/// know that introspection encountered unsupported names or features and these are supposed
/// to be rendered as comments. Since the parser will not set these flags when reading a schema
/// string, only introspection and the lowering of the datamodel to the ast care about these flags.
/// The FieldType: Unsupported behaves in the same way.
/// Both of these are never converted into the internal datamodel.
#[derive(Debug, PartialEq, Clone, Default)]
pub struct Datamodel {
pub enums: Vec<Enum>,
pub models: Vec<Model>,
}
impl Datamodel {
pub fn new() -> Datamodel {
Datamodel { ..Default::default() }
}
/// Checks if a datamodel contains neither enums nor models.
pub fn is_empty(&self) -> bool {
self.enums.is_empty() && self.models.is_empty()
}
/// Adds an enum to this datamodel.
pub fn add_enum(&mut self, en: Enum) {
self.enums.push(en);
}
/// Adds a model to this datamodel.
pub fn add_model(&mut self, model: Model) {
self.models.push(model);
}
/// Gets an iterator over all models.
pub fn models(&self) -> std::slice::Iter<Model> {
self.models.iter()
}
/// Gets an iterator over all enums.
pub fn enums(&self) -> std::slice::Iter<Enum> {
self.enums.iter()
}
/// Gets a mutable iterator over all models.
pub fn models_mut(&mut self) -> std::slice::IterMut<Model> {
self.models.iter_mut()
}
/// Gets a mutable iterator over all enums.
pub fn enums_mut(&mut self) -> std::slice::IterMut<Enum> {
self.enums.iter_mut()
}
/// Finds a model by name.
pub fn find_model(&self, name: &str) -> Option<&Model> {
self.models().find(|model| model.name == name)
}
/// Finds a model by database name. This will only find models with a name
/// remapped to the provided `db_name`.
pub fn find_model_db_name(&self, db_name: &str) -> Option<&Model> {
self.models()
.find(|model| model.database_name.as_deref() == Some(db_name))
}
/// Finds parent model for a field reference.
pub fn find_model_by_relation_field_ref(&self, field: &RelationField) -> Option<&Model> {
self.find_model(&self.find_related_field_bang(&field).1.relation_info.to)
}
/// Finds a mutable field reference by a model and field name.
pub fn find_field_mut(&mut self, model: &str, field: &str) -> &mut Field {
self.find_model_mut(model).find_field_mut(field)
}
/// Finds a mutable scalar field reference by a model and field name.
pub fn find_scalar_field_mut(&mut self, model: &str, field: &str) -> &mut ScalarField {
// This uses the memory location of field for equality.
self.find_model_mut(model).find_scalar_field_mut(field)
}
/// Finds a mutable relation field reference by a model and field name.
pub fn find_relation_field_mut(&mut self, model: &str, field: &str) -> &mut RelationField {
self.find_model_mut(model).find_relation_field_mut(field)
}
/// Finds an enum by name.
pub fn find_enum(&self, name: &str) -> Option<&Enum> {
self.enums().find(|m| m.name == *name)
}
/// Finds an enum by database name.
pub fn find_enum_db_name(&self, db_name: &str) -> Option<&Enum> {
self.enums().find(|e| e.database_name == Some(db_name.to_owned()))
}
/// Finds a model by name and returns a mutable reference.
pub fn find_model_mut(&mut self, name: &str) -> &mut Model {
self.models_mut()
.find(|m| m.name == *name)
.expect("We assume an internally valid datamodel before mutating.")
}
/// Finds an enum by name and returns a mutable reference.
pub fn find_enum_mut(&mut self, name: &str) -> &mut Enum {
self.enums_mut()
.find(|m| m.name == *name)
.expect("We assume an internally valid datamodel before mutating.")
}
/// Returns (model_name, field_name) for all fields using a specific enum.
pub fn find_enum_fields(&self, enum_name: &str) -> Vec<(String, String)> {
let mut fields = vec![];
for model in self.models() {
for field in model.scalar_fields() {
if field.field_type.is_enum(enum_name) {
fields.push((model.name.clone(), field.name.clone()))
}
}
}
fields
}
/// Returns (model_name, field_name) for all relation fields pointing to a specific model.
pub fn find_relation_fields_for_model(&mut self, model_name: &str) -> Vec<(String, String)> {
let mut fields = vec![];
for model in self.models() {
for field in model.relation_fields() {
if field.relation_info.to == model_name {
fields.push((model.name.clone(), field.name.clone()))
}
}
}
fields
}
/// Finds a relation field related to a relation info. Returns a tuple (index_of_relation_field_in_model, relation_field).
pub fn find_related_field_for_info(&self, info: &RelationInfo, exclude: &str) -> Option<(usize, &RelationField)> {
self.find_model(&info.to)
.expect("The model referred to by a RelationInfo should always exist.")
.fields
.iter()
.enumerate()
.filter_map(|(idx, field)| field.as_relation_field().map(|f| (idx, f)))
.find(|(_idx, f)| {
f.relation_info.name == info.name
&& (f.relation_info.to != info.to ||
// This is to differentiate the opposite field from self in the self relation case.
f.name != exclude)
})
}
/// This finds the related field for a relationfield if available
pub fn find_related_field(&self, rf: &RelationField) -> Option<(usize, &RelationField)> {
self.find_related_field_for_info(&rf.relation_info, &rf.name)
}
/// This is used once we assume the datamodel to be internally valid
pub fn find_related_field_bang(&self, rf: &RelationField) -> (usize, &RelationField) {
self.find_related_field(rf)
.expect("Every RelationInfo should have a complementary RelationInfo on the opposite relation field.")
}
}