Skip to main content

rustauth_core/db/
schema_handles.rs

1use crate::error::RustAuthError;
2
3use super::adapter::{Create, Sort, SortDirection, Update, Where, WhereOperator};
4use super::schema::DbSchema;
5use super::{DbRecord, DbValue};
6
7/// Schema view for validated table and field names.
8#[derive(Debug, Clone, Copy)]
9pub struct AuthSchema<'a> {
10    inner: &'a DbSchema,
11}
12
13/// Validated logical table bound to the merged auth schema.
14///
15/// Use this handle to validate field names and map adapter records back to
16/// logical keys. Execute queries with the normal adapter types
17/// ([`FindOne`](super::FindOne), [`Create`](super::Create), …) using
18/// [`SchemaTable::model`].
19#[derive(Debug, Clone)]
20pub struct SchemaTable<'a> {
21    schema: &'a DbSchema,
22    logical: String,
23}
24
25impl<'a> AuthSchema<'a> {
26    pub fn new(schema: &'a DbSchema) -> Self {
27        Self { inner: schema }
28    }
29
30    /// Resolve a logical table name and return a handle for validation/mapping.
31    pub fn table(&self, logical_name: &str) -> Result<SchemaTable<'a>, RustAuthError> {
32        SchemaTable::new(self.inner, logical_name)
33    }
34
35    /// Resolve a logical table when it exists in the merged schema.
36    pub fn try_table(&self, logical_name: &str) -> Option<SchemaTable<'a>> {
37        SchemaTable::new(self.inner, logical_name).ok()
38    }
39}
40
41impl<'a> SchemaTable<'a> {
42    pub fn new(schema: &'a DbSchema, logical_name: &str) -> Result<Self, RustAuthError> {
43        schema
44            .table(logical_name)
45            .ok_or_else(|| RustAuthError::TableNotFound {
46                table: logical_name.to_owned(),
47            })?;
48        Ok(Self {
49            schema,
50            logical: logical_name.to_owned(),
51        })
52    }
53
54    /// Logical model name used in adapter queries (`"user"`, `"session"`, …).
55    pub fn model(&self) -> &str {
56        &self.logical
57    }
58
59    pub fn logical_name(&self) -> &str {
60        &self.logical
61    }
62
63    pub fn physical_name(&self) -> Result<&str, RustAuthError> {
64        self.schema.table_name(&self.logical)
65    }
66
67    pub fn create(&self) -> Create {
68        Create::new(&self.logical)
69    }
70
71    /// Build a predicate on a logical field name (defaults to equality).
72    pub fn where_eq(&self, field: &str, value: DbValue) -> Result<Where, RustAuthError> {
73        self.where_op(field, WhereOperator::Eq, value)
74    }
75
76    /// Build a predicate on a logical field name with an explicit operator.
77    pub fn where_op(
78        &self,
79        field: &str,
80        operator: WhereOperator,
81        value: DbValue,
82    ) -> Result<Where, RustAuthError> {
83        self.schema.field(&self.logical, field)?;
84        Ok(Where::new(field, value).operator(operator))
85    }
86
87    pub fn sort_by(&self, field: &str, direction: SortDirection) -> Result<Sort, RustAuthError> {
88        self.schema.field(&self.logical, field)?;
89        Ok(Sort::new(field, direction))
90    }
91
92    /// Validate logical field names exist in the schema.
93    pub fn ensure_field(&self, logical_field: &str) -> Result<(), RustAuthError> {
94        self.schema.field(&self.logical, logical_field)?;
95        Ok(())
96    }
97
98    pub fn ensure_fields<const N: usize>(&self, fields: [&str; N]) -> Result<(), RustAuthError> {
99        for field in fields {
100            self.ensure_field(field)?;
101        }
102        Ok(())
103    }
104
105    /// Attach a column value to a create builder using a logical field name.
106    pub fn with_data(
107        &self,
108        create: Create,
109        field: &str,
110        value: DbValue,
111    ) -> Result<Create, RustAuthError> {
112        self.ensure_field(field)?;
113        Ok(create.data(field, value))
114    }
115
116    /// Attach a column value to an update builder using a logical field name.
117    pub fn with_update_data(
118        &self,
119        update: Update,
120        field: &str,
121        value: DbValue,
122    ) -> Result<Update, RustAuthError> {
123        self.ensure_field(field)?;
124        Ok(update.data(field, value))
125    }
126
127    /// Map a database record's physical column keys to logical field names.
128    pub fn map_record(&self, record: DbRecord) -> Result<DbRecord, RustAuthError> {
129        self.schema.map_record_to_logical(&self.logical, record)
130    }
131}