use std::hash::Hash;
use std::marker::PhantomData;
use crate::Row;
use crate::relations::ReferenceMeta;
#[derive(Clone, Debug)]
pub struct RecordSchema<T> {
pub(crate) table_name: &'static str,
pub(crate) table_schema: Option<&'static str>,
pub(crate) root_name: Option<&'static str>,
pub(crate) references: Vec<ReferenceMeta>,
pub(crate) column_names: Vec<String>,
pub(crate) bind_column_names: Vec<String>,
_marker: PhantomData<fn() -> T>,
}
impl<T> RecordSchema<T> {
pub fn new(table_name: &'static str) -> Self {
Self {
table_name,
table_schema: None,
root_name: None,
references: Vec::new(),
column_names: Vec::new(),
bind_column_names: Vec::new(),
_marker: PhantomData,
}
}
pub fn schema(mut self, schema: impl Into<Option<&'static str>>) -> Self {
self.table_schema = schema.into();
self
}
pub fn root(mut self, root: impl Into<Option<&'static str>>) -> Self {
self.root_name = root.into();
self
}
pub fn reference(mut self, reference: ReferenceMeta) -> Self {
self.references.push(reference);
self
}
pub fn references(mut self, references: Vec<ReferenceMeta>) -> Self {
self.references = references;
self
}
pub fn column(mut self, name: impl Into<String>) -> Self {
self.column_names.push(name.into());
self
}
pub fn columns(mut self, columns: Vec<String>) -> Self {
self.column_names = columns;
self
}
pub fn bind_column(mut self, name: impl Into<String>) -> Self {
self.bind_column_names.push(name.into());
self
}
pub fn bind_columns(mut self, columns: Vec<String>) -> Self {
self.bind_column_names = columns;
self
}
}
#[derive(Clone, Debug)]
pub struct ModelSchema<T> {
pub(crate) record: RecordSchema<T>,
pub(crate) primary_key_columns: &'static [&'static str],
}
impl<T> ModelSchema<T> {
pub fn new(record: RecordSchema<T>, primary_key_columns: &'static [&'static str]) -> Self {
Self {
record,
primary_key_columns,
}
}
pub fn record(&self) -> &RecordSchema<T> {
&self.record
}
pub fn primary_key_columns(&self) -> &'static [&'static str] {
self.primary_key_columns
}
}
pub trait Record: Sized {
fn record_schema() -> RecordSchema<Self>;
fn record_table_name() -> &'static str {
Self::record_schema().table_name
}
fn record_table_schema() -> Option<&'static str> {
Self::record_schema().table_schema
}
fn record_root_name() -> Option<&'static str> {
Self::record_schema().root_name
}
fn record_references() -> Vec<ReferenceMeta> {
Self::record_schema().references
}
fn record_column_names() -> Vec<String> {
Self::record_schema().column_names
}
fn record_bind_column_names() -> Vec<String> {
Self::record_schema().bind_column_names
}
fn record_bind_values(&self, _args: &mut crate::Arguments<'static>) -> Result<(), sqlx::Error> {
Ok(())
}
fn record_bind_selected(
&self,
columns: &[&str],
args: &mut crate::Arguments<'static>,
) -> Result<(), sqlx::Error> {
let expected = Self::record_bind_column_names();
if columns.len() != expected.len() {
return Err(sqlx::Error::Protocol(
"record does not support selective binding".to_string(),
));
}
if columns.iter().zip(expected.iter()).all(|(a, b)| *a == b) {
return self.record_bind_values(args);
}
Err(sqlx::Error::Protocol(
"record does not support selective binding".to_string(),
))
}
fn record_scan_ordered(_row: &Row, _start_idx: &mut usize) -> Result<Self, sqlx::Error> {
Err(sqlx::Error::Protocol(
"record does not support row scanning".to_string(),
))
}
fn record_scan_unordered(_row: &Row) -> Result<Self, sqlx::Error> {
Err(sqlx::Error::Protocol(
"record does not support unordered row scanning".to_string(),
))
}
fn record_scan(row: &Row) -> Result<Self, sqlx::Error> {
let mut idx = 0;
Self::record_scan_ordered(row, &mut idx)
}
}
pub trait Model: Record + crate::IntoTable {
type PrimaryKey: Clone + Hash + Eq;
fn model_schema() -> ModelSchema<Self>
where
Self: Sized;
fn table_name() -> &'static str {
Self::record_table_name()
}
fn table_schema() -> Option<&'static str> {
Self::record_table_schema()
}
fn table() -> crate::queries::__private::ModelTable<Self>
where
Self: Sized + crate::queries::__private::HasCols,
{
let table = match Self::table_schema() {
Some(schema) => crate::queries::__private::table_schema(schema, Self::table_name()),
None => crate::queries::__private::table(Self::table_name()),
};
crate::queries::__private::ModelTable::new_with_columns(table, Self::record_column_names())
}
fn primary_key(&self) -> Self::PrimaryKey;
fn primary_key_columns() -> &'static [&'static str]
where
Self: Sized,
{
Self::model_schema().primary_key_columns
}
fn primary_key_column() -> Option<&'static str>
where
Self: Sized,
{
match Self::primary_key_columns() {
[column] => Some(*column),
_ => None,
}
}
}