use std::{iter::once, marker::PhantomData};
use field_access::FieldAccess;
use sqlx::{Database, Encode, Error, QueryBuilder, Type};
use crate::common::{
conversion::ValueConvert, error::QueryError, fields::batch_extract, helper::get_table_name, types::PrimaryKey
};
pub struct Insert<'a, ET, DB, VAL>
where
ET: FieldAccess,
DB: Database,
VAL: Encode<'a, DB> + Type<DB> + 'a,
{
query_builder: QueryBuilder<'a, DB>,
columns_specified: bool,
_phantom: PhantomData<(ET, VAL)>,
}
impl<'a, ET, DB, VAL> Insert<'a, ET, DB, VAL>
where
ET: FieldAccess,
DB: Database,
VAL: Encode<'a, DB> + Type<DB> + ValueConvert + 'a,
{
pub fn table() -> Self {
let table_name = get_table_name::<ET>();
Self::with_table(&table_name)
}
pub fn with_table(table_name: impl Into<String>) -> Self {
Self::from_query_with_table(QueryBuilder::new(""), table_name)
}
pub fn from_query(qb: QueryBuilder<'a, DB>) -> Self {
Self::from_query_with_table(qb, &get_table_name::<ET>())
}
pub fn from_query_with_table(mut query_builder: QueryBuilder<'a, DB>, table_name: impl Into<String>) -> Self {
query_builder.push("INSERT INTO ").push(table_name.into());
Self {
query_builder,
columns_specified: false,
_phantom: PhantomData,
}
}
pub fn columns<I, S>(mut self, columns: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let cols: Vec<String> = columns.into_iter().map(|s| s.as_ref().to_string()).collect();
if !cols.is_empty() {
self.query_builder.push(" (");
let mut separated = self.query_builder.separated(", ");
for col in cols {
separated.push(col);
}
self.query_builder.push(")");
self.columns_specified = true;
}
self
}
pub fn many(
models: impl IntoIterator<Item = &'a ET>,
primary_key: &PrimaryKey<'a>
) -> Result<QueryBuilder<'a, DB>, Error>
{
let models: Vec<_> = models.into_iter().collect();
if models.is_empty() {
return Err(QueryError::NoEntitiesProvided.into());
}
let keys = if primary_key.auto_generate() {
primary_key.get_keys()
} else {
vec![]
};
let (names, values) = batch_extract::<ET, VAL>(&models, &keys, false);
let mut query_builder = Self::table().query_builder;
query_builder.push(" (").push(names.join(", ")).push(") ");
query_builder.push_values(
values,
|mut b, row| {
for value in row {
b.push_bind(value);
}
}
);
Ok(query_builder)
}
pub fn one(
model: &'a ET,
primary_key: &PrimaryKey<'a>,
) -> Result<QueryBuilder<'a, DB>, Error>
{
Self::many(once(model), primary_key)
}
#[cfg(any(feature = "sqlite" , feature = "postgres"))]
pub fn returning<I, S>(mut self, columns: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
self.query_builder.push(" RETURNING ");
let cols = columns.into_iter();
let mut separated = self.query_builder.separated(", ");
for col in cols {
separated.push(col.as_ref());
}
self
}
pub fn custom<F>(mut self, build_fn: F) -> Self
where
F: FnOnce(&mut QueryBuilder<'a, DB>),
{
build_fn(&mut self.query_builder);
self
}
pub fn finish(self) -> QueryBuilder<'a, DB> {
self.query_builder
}
}