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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//! Types related to the _datamodel section_ in the PSL.
//!
//! Includes the `model`, `enum` and `type` definitions.

mod attributes;
mod composite_type;
mod default;
mod enumerator;
mod field;
mod field_type;
mod index;
mod model;
mod view;

pub use composite_type::CompositeType;
pub use default::DefaultValue;
pub use enumerator::{Enum, EnumVariant};
pub use field::Field;
pub use field_type::FieldType;
pub use index::{IdDefinition, IdFieldDefinition, IndexDefinition, IndexFieldInput, IndexOps, UniqueFieldAttribute};
pub use model::{Model, Relation};
pub use view::View;

use std::fmt;

/// The PSL data model declaration.
#[derive(Default, Debug)]
pub struct Datamodel<'a> {
    models: Vec<Model<'a>>,
    views: Vec<View<'a>>,
    enums: Vec<Enum<'a>>,
    composite_types: Vec<CompositeType<'a>>,
}

impl<'a> Datamodel<'a> {
    /// Create a new empty data model.
    pub fn new() -> Self {
        Self::default()
    }

    /// Add a model block to the data model.
    ///
    /// ```ignore
    /// model Foo {  // <
    ///   id Int @id // < this
    /// }            // <
    /// ```
    pub fn push_model(&mut self, model: Model<'a>) {
        self.models.push(model);
    }

    /// Add an enum block to the data model.
    ///
    /// ```ignore
    /// enum Foo { // <
    ///   Bar      // < this
    /// }          // <
    /// ```
    pub fn push_enum(&mut self, r#enum: Enum<'a>) {
        self.enums.push(r#enum);
    }

    /// Add a view block to the data model.
    ///
    /// ```ignore
    /// view Foo {   // <
    ///   id Int @id // < this
    /// }            // <
    /// ```
    pub fn push_view(&mut self, view: View<'a>) {
        self.views.push(view);
    }

    /// Add a composite type block to the data model.
    ///
    /// ```ignore
    /// type Address {  // <
    ///   street String // < this
    /// }               // <
    /// ```
    pub fn push_composite_type(&mut self, composite_type: CompositeType<'a>) {
        self.composite_types.push(composite_type);
    }

    /// True if the render output would be an empty string.
    pub fn is_empty(&self) -> bool {
        self.models.is_empty() && self.enums.is_empty() && self.composite_types.is_empty() && self.views.is_empty()
    }
}

impl<'a> fmt::Display for Datamodel<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for ct in self.composite_types.iter() {
            writeln!(f, "{ct}")?;
        }

        for model in self.models.iter() {
            writeln!(f, "{model}")?;
        }

        for view in self.views.iter() {
            writeln!(f, "{view}")?;
        }

        for r#enum in self.enums.iter() {
            writeln!(f, "{enum}")?;
        }

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use crate::value::Function;

    use super::*;
    use expect_test::expect;

    #[test]
    fn simple_data_model() {
        let mut data_model = Datamodel::new();

        let mut composite = CompositeType::new("Address");
        let field = Field::new("street", "String");
        composite.push_field(field);

        data_model.push_composite_type(composite);

        let mut model = Model::new("User");

        let mut field = Field::new("id", "Int");
        field.id(IdFieldDefinition::default());

        let dv = DefaultValue::function(Function::new("autoincrement"));
        field.default(dv);

        model.push_field(field);
        data_model.push_model(model);

        let mut traffic_light = Enum::new("TrafficLight");

        traffic_light.push_variant("Red");
        traffic_light.push_variant("Yellow");
        traffic_light.push_variant("Green");

        data_model.push_enum(traffic_light);

        let mut cat = Enum::new("Cat");
        cat.push_variant("Asleep");
        cat.push_variant("Hungry");

        data_model.push_enum(cat);

        let mut view = View::new("Meow");
        let mut field = Field::new("id", "Int");
        field.id(IdFieldDefinition::default());

        view.push_field(field);

        data_model.push_view(view);

        let expected = expect![[r#"
            type Address {
              street String
            }

            model User {
              id Int @id @default(autoincrement())
            }

            view Meow {
              id Int @id
            }

            enum TrafficLight {
              Red
              Yellow
              Green
            }

            enum Cat {
              Asleep
              Hungry
            }
        "#]];

        let rendered = psl::reformat(&format!("{data_model}"), 2).unwrap();
        expected.assert_eq(&rendered);
    }
}