toasty_core/schema.rs
1//! Schema representation for Toasty, split into three layers.
2//!
3//! - [`app`] -- Model-level definitions: fields, relations, constraints. This
4//! is what the generated Rust code sees.
5//! - [`db`] -- Table/column-level definitions. This is what the database sees.
6//! - [`mapping`] -- Connects app fields to database columns, supporting
7//! non-1:1 mappings such as embedded structs and enums.
8//!
9//! The top-level [`Schema`] struct ties all three layers together and is
10//! constructed via [`Builder`].
11//!
12//! # Examples
13//!
14//! ```ignore
15//! use toasty_core::schema::{Schema, Builder};
16//!
17//! let schema = Builder::new()
18//! .build(app_schema, &driver_capability)
19//! .expect("schema should be valid");
20//!
21//! // Look up the database table backing a model
22//! let table = schema.table_for(model_id);
23//! ```
24
25/// Application-level (model-oriented) schema definitions.
26pub mod app;
27
28mod builder;
29pub use builder::Builder;
30
31/// Database-level (table/column-oriented) schema definitions.
32pub mod db;
33
34/// Schema-diff types: compare two schema versions and produce structural changes.
35pub mod diff;
36
37/// Mapping between the app layer and the database layer.
38pub mod mapping;
39use mapping::Mapping;
40
41mod name;
42pub use name::Name;
43
44mod verify;
45
46use crate::Result;
47use app::ModelId;
48use db::{Table, TableId};
49
50/// The combined schema: app-level models, database-level tables, and the
51/// mapping that connects them.
52///
53/// Constructed with [`Builder`] and validated on creation. Immutable at runtime.
54///
55/// # Examples
56///
57/// ```ignore
58/// use toasty_core::Schema;
59///
60/// fn inspect(schema: &Schema) {
61/// for (id, model) in &schema.app.models {
62/// let table = schema.table_for(*id);
63/// println!("{} -> {}", model.name().snake_case(), table.name);
64/// }
65/// }
66/// ```
67#[derive(Debug)]
68pub struct Schema {
69 /// Application-level schema.
70 pub app: app::Schema,
71
72 /// Database-level schema.
73 pub db: db::Schema,
74
75 /// Maps the app-level schema to the db-level schema.
76 pub mapping: Mapping,
77}
78
79impl Schema {
80 /// Returns a new [`Builder`] for constructing a `Schema`.
81 ///
82 /// # Examples
83 ///
84 /// ```ignore
85 /// use toasty_core::Schema;
86 ///
87 /// let builder = Schema::builder();
88 /// ```
89 pub fn builder() -> Builder {
90 Builder::default()
91 }
92
93 /// Returns the mapping for the given model.
94 ///
95 /// # Panics
96 ///
97 /// Panics if `id` does not correspond to a model in the schema.
98 ///
99 /// # Examples
100 ///
101 /// ```ignore
102 /// use toasty_core::Schema;
103 ///
104 /// let mapping = schema.mapping_for(model_id);
105 /// println!("table: {:?}", mapping.table);
106 /// ```
107 pub fn mapping_for(&self, id: impl Into<ModelId>) -> &mapping::Model {
108 self.mapping.model(id)
109 }
110
111 /// Returns the database table that stores the given model.
112 ///
113 /// # Panics
114 ///
115 /// Panics if `id` does not correspond to a model in the schema.
116 ///
117 /// # Examples
118 ///
119 /// ```ignore
120 /// let table = schema.table_for(model_id);
121 /// println!("table name: {}", table.name);
122 /// ```
123 pub fn table_for(&self, id: impl Into<ModelId>) -> &Table {
124 self.db.table(self.table_id_for(id))
125 }
126
127 /// Returns the [`TableId`] for the table that stores the given model.
128 ///
129 /// # Panics
130 ///
131 /// Panics if `id` does not correspond to a model in the schema.
132 pub fn table_id_for(&self, id: impl Into<ModelId>) -> TableId {
133 self.mapping.model(id).table
134 }
135}