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/// Mapping between the app layer and the database layer.
35pub mod mapping;
36use mapping::Mapping;
37
38mod name;
39pub use name::Name;
40
41mod verify;
42
43use crate::Result;
44use app::ModelId;
45use db::{Table, TableId};
46
47/// The combined schema: app-level models, database-level tables, and the
48/// mapping that connects them.
49///
50/// Constructed with [`Builder`] and validated on creation. Immutable at runtime.
51///
52/// # Examples
53///
54/// ```ignore
55/// use toasty_core::Schema;
56///
57/// fn inspect(schema: &Schema) {
58///     for (id, model) in &schema.app.models {
59///         let table = schema.table_for(*id);
60///         println!("{} -> {}", model.name().snake_case(), table.name);
61///     }
62/// }
63/// ```
64#[derive(Debug)]
65pub struct Schema {
66    /// Application-level schema.
67    pub app: app::Schema,
68
69    /// Database-level schema.
70    pub db: db::Schema,
71
72    /// Maps the app-level schema to the db-level schema.
73    pub mapping: Mapping,
74}
75
76impl Schema {
77    /// Returns a new [`Builder`] for constructing a `Schema`.
78    ///
79    /// # Examples
80    ///
81    /// ```ignore
82    /// use toasty_core::Schema;
83    ///
84    /// let builder = Schema::builder();
85    /// ```
86    pub fn builder() -> Builder {
87        Builder::default()
88    }
89
90    /// Returns the mapping for the given model.
91    ///
92    /// # Panics
93    ///
94    /// Panics if `id` does not correspond to a model in the schema.
95    ///
96    /// # Examples
97    ///
98    /// ```ignore
99    /// use toasty_core::Schema;
100    ///
101    /// let mapping = schema.mapping_for(model_id);
102    /// println!("table: {:?}", mapping.table);
103    /// ```
104    pub fn mapping_for(&self, id: impl Into<ModelId>) -> &mapping::Model {
105        self.mapping.model(id)
106    }
107
108    /// Returns the database table that stores the given model.
109    ///
110    /// # Panics
111    ///
112    /// Panics if `id` does not correspond to a model in the schema.
113    ///
114    /// # Examples
115    ///
116    /// ```ignore
117    /// let table = schema.table_for(model_id);
118    /// println!("table name: {}", table.name);
119    /// ```
120    pub fn table_for(&self, id: impl Into<ModelId>) -> &Table {
121        self.db.table(self.table_id_for(id))
122    }
123
124    /// Returns the [`TableId`] for the table that stores the given model.
125    ///
126    /// # Panics
127    ///
128    /// Panics if `id` does not correspond to a model in the schema.
129    pub fn table_id_for(&self, id: impl Into<ModelId>) -> TableId {
130        self.mapping.model(id).table
131    }
132}