use crate::bind::{BindValue, NullableBindValue};
use crate::expr::{Expr, NonNull, Null, Nullability, Scalar};
use crate::param::ParamBind;
use crate::traits::{SQLParam, ToSQL};
use crate::types::DataType;
use crate::{Param, SQL};
use core::fmt;
use core::marker::PhantomData;
#[derive(Default, Debug, Clone, Hash, Copy, PartialEq, Eq)]
pub struct Placeholder {
pub name: Option<&'static str>,
}
#[derive(Default, Debug, Clone, Hash, Copy, PartialEq, Eq)]
pub struct TypedPlaceholder<T: DataType, N: Nullability = NonNull> {
inner: Placeholder,
_marker: PhantomData<fn() -> (T, N)>,
}
impl Placeholder {
#[must_use]
pub const fn named(name: &'static str) -> Self {
Self { name: Some(name) }
}
#[must_use]
pub const fn anonymous() -> Self {
Self { name: None }
}
#[must_use]
pub const fn typed<T: DataType>(name: &'static str) -> TypedPlaceholder<T, NonNull> {
TypedPlaceholder {
inner: Self::named(name),
_marker: PhantomData,
}
}
#[must_use]
pub const fn typed_nullable<T: DataType>(name: &'static str) -> TypedPlaceholder<T, Null> {
TypedPlaceholder {
inner: Self::named(name),
_marker: PhantomData,
}
}
}
impl<T: DataType, N: Nullability> TypedPlaceholder<T, N> {
#[must_use]
pub const fn named(name: &'static str) -> Self {
Self {
inner: Placeholder::named(name),
_marker: PhantomData,
}
}
pub fn bind<'a, V, R>(self, value: R) -> ParamBind<'a, V>
where
V: SQLParam,
R: BindValue<'a, V, T>,
{
ParamBind {
name: self.inner.name.unwrap_or(""),
value: value.into_bind_value(),
}
}
#[must_use]
pub const fn name(self) -> Option<&'static str> {
self.inner.name
}
#[must_use]
pub const fn into_placeholder(self) -> Placeholder {
self.inner
}
}
impl<T: DataType> TypedPlaceholder<T, Null> {
pub fn bind_opt<'a, V, R>(self, value: Option<R>) -> ParamBind<'a, V>
where
V: SQLParam,
Option<R>: NullableBindValue<'a, V, T>,
{
ParamBind {
name: self.inner.name.unwrap_or(""),
value: value.into_nullable_bind_value(),
}
}
}
impl<T: DataType, N: Nullability> From<TypedPlaceholder<T, N>> for Placeholder {
fn from(value: TypedPlaceholder<T, N>) -> Self {
value.inner
}
}
impl<'a, V: SQLParam + 'a> ToSQL<'a, V> for Placeholder {
fn to_sql(&self) -> SQL<'a, V> {
SQL {
chunks: smallvec::smallvec![crate::SQLChunk::Param(Param {
value: None,
placeholder: *self,
})],
}
}
}
impl<'a, V: SQLParam + 'a> Expr<'a, V> for Placeholder {
type SQLType = crate::types::Placeholder;
type Nullable = NonNull;
type Aggregate = Scalar;
}
impl<'a, V: SQLParam + 'a, T: DataType, N: Nullability> ToSQL<'a, V> for TypedPlaceholder<T, N> {
fn to_sql(&self) -> SQL<'a, V> {
self.inner.to_sql()
}
}
impl<'a, V: SQLParam + 'a, T: DataType, N: Nullability> Expr<'a, V> for TypedPlaceholder<T, N> {
type SQLType = T;
type Nullable = N;
type Aggregate = Scalar;
}
impl fmt::Display for Placeholder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.name {
Some(name) => write!(f, ":{name}"),
None => write!(f, "?"),
}
}
}