use argon2::Argon2;
use argon2::password_hash::{PasswordHasher, PasswordVerifier, phc::PasswordHash};
use chrono::{DateTime, NaiveDate, NaiveTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
pub trait RangoField {
type Inner;
fn column_type() -> crate::schema::ColumnType;
fn into_inner(self) -> Self::Inner;
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FieldBool(pub bool);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FieldSmallInt(pub i16);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FieldInt(pub i32);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FieldBigInt(pub i64);
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
pub struct FieldAutoInt(pub Option<i32>);
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
pub struct FieldAutoBigInt(pub Option<i64>);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FieldFloat(pub f32);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FieldDouble(pub f64);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FieldDecimal<const PRECISION: u8, const SCALE: u8>(pub rust_decimal::Decimal);
impl<const P: u8, const S: u8> FieldDecimal<P, S> {
pub fn from_decimal(mut value: rust_decimal::Decimal) -> Self {
value.rescale(u32::from(S));
Self(value)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FieldText(pub String);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "String")]
pub struct FieldVarchar<const MIN: usize, const MAX: usize>(String);
impl<const MIN: usize, const MAX: usize> FieldVarchar<MIN, MAX> {
pub fn new(s: impl Into<String>) -> Result<Self, FieldError> {
let s = s.into();
let len = s.chars().count();
if len < MIN {
return Err(FieldError(format!("Value too short (min {})", MIN)));
}
if len > MAX {
return Err(FieldError(format!("Value too long (max {})", MAX)));
}
Ok(Self(s))
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn into_inner(self) -> String {
self.0
}
#[doc(hidden)]
pub fn from_db(s: String) -> Self {
Self(s)
}
}
impl<const MIN: usize, const MAX: usize> TryFrom<String> for FieldVarchar<MIN, MAX> {
type Error = FieldError;
fn try_from(s: String) -> Result<Self, FieldError> {
Self::new(s)
}
}
impl<const MIN: usize, const MAX: usize> std::ops::Deref for FieldVarchar<MIN, MAX> {
type Target = str;
fn deref(&self) -> &str {
&self.0
}
}
impl<const MIN: usize, const MAX: usize> std::fmt::Display for FieldVarchar<MIN, MAX> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "String")]
pub struct FieldEmail(String);
impl FieldEmail {
pub fn new(s: impl Into<String>) -> Result<Self, FieldError> {
let s = s.into();
if !s.contains('@') || s.chars().count() > 254 {
return Err(FieldError("Invalid email address".into()));
}
Ok(Self(s))
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn into_inner(self) -> String {
self.0
}
#[doc(hidden)]
pub fn from_db(s: String) -> Self {
Self(s)
}
}
impl TryFrom<String> for FieldEmail {
type Error = FieldError;
fn try_from(s: String) -> Result<Self, FieldError> {
Self::new(s)
}
}
impl std::ops::Deref for FieldEmail {
type Target = str;
fn deref(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for FieldEmail {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Clone, PartialEq)]
pub struct FieldPassword<const MIN: usize, const MAX: usize> {
hash: String,
}
impl<const MIN: usize, const MAX: usize> std::fmt::Debug for FieldPassword<MIN, MAX> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "FieldPassword(<redacted>)")
}
}
impl<const MIN: usize, const MAX: usize> FieldPassword<MIN, MAX> {
pub fn new(plain: impl AsRef<str>) -> Result<Self, FieldError> {
let plain = plain.as_ref();
let len = plain.chars().count();
if len < MIN {
return Err(FieldError(format!("Password too short (min {})", MIN)));
}
if len > MAX {
return Err(FieldError(format!("Password too long (max {})", MAX)));
}
let hash = Argon2::default()
.hash_password(plain.as_bytes())
.map_err(|e| FieldError(e.to_string()))?;
Ok(Self {
hash: hash.to_string(),
})
}
pub fn verify(&self, candidate: &str) -> bool {
let Ok(parsed) = PasswordHash::new(&self.hash) else {
return false;
};
Argon2::default()
.verify_password(candidate.as_bytes(), &parsed)
.is_ok()
}
pub fn from_hash(hash: String) -> Self {
Self { hash }
}
pub fn hash(&self) -> &str {
&self.hash
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "String")]
pub struct FieldUrl(String);
impl FieldUrl {
pub fn new(s: impl Into<String>) -> Result<Self, FieldError> {
let s = s.into();
if !s.starts_with("http://") && !s.starts_with("https://") {
return Err(FieldError("URL must start with http:// or https://".into()));
}
Ok(Self(s))
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn into_inner(self) -> String {
self.0
}
#[doc(hidden)]
pub fn from_db(s: String) -> Self {
Self(s)
}
}
impl TryFrom<String> for FieldUrl {
type Error = FieldError;
fn try_from(s: String) -> Result<Self, FieldError> {
Self::new(s)
}
}
impl std::ops::Deref for FieldUrl {
type Target = str;
fn deref(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for FieldUrl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FieldBytes(pub Vec<u8>);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FieldUuid(pub Uuid);
impl FieldUuid {
pub fn new() -> Self {
Self(Uuid::new_v4())
}
}
impl Default for FieldUuid {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FieldDate(pub NaiveDate);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FieldTime(pub NaiveTime);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FieldDateTime(pub DateTime<Utc>);
impl FieldDateTime {
pub fn now() -> Self {
Self(Utc::now())
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FieldJson(pub serde_json::Value);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "i64")]
pub struct FieldRange<const MIN: i64, const MAX: i64>(i64);
impl<const MIN: i64, const MAX: i64> FieldRange<MIN, MAX> {
pub fn new(val: i64) -> Result<Self, FieldError> {
if val < MIN || val > MAX {
return Err(FieldError(format!(
"Value {} out of range [{}, {}]",
val, MIN, MAX
)));
}
Ok(Self(val))
}
pub fn get(&self) -> i64 {
self.0
}
#[doc(hidden)]
pub fn from_db(v: i64) -> Self {
Self(v)
}
}
impl<const MIN: i64, const MAX: i64> TryFrom<i64> for FieldRange<MIN, MAX> {
type Error = FieldError;
fn try_from(v: i64) -> Result<Self, FieldError> {
Self::new(v)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct FieldError(pub String);
impl std::fmt::Display for FieldError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::error::Error for FieldError {}
use crate::query::{SqlValue, ToSqlValue};
impl ToSqlValue for FieldBool {
fn to_sql_value(&self) -> SqlValue {
SqlValue::Bool(self.0)
}
}
impl ToSqlValue for FieldSmallInt {
fn to_sql_value(&self) -> SqlValue {
SqlValue::SmallInt(self.0)
}
}
impl ToSqlValue for FieldInt {
fn to_sql_value(&self) -> SqlValue {
SqlValue::Int(self.0)
}
}
impl ToSqlValue for FieldBigInt {
fn to_sql_value(&self) -> SqlValue {
SqlValue::BigInt(self.0)
}
}
impl ToSqlValue for FieldAutoInt {
fn to_sql_value(&self) -> SqlValue {
self.0.map_or(SqlValue::NullInt, SqlValue::Int)
}
}
impl ToSqlValue for FieldAutoBigInt {
fn to_sql_value(&self) -> SqlValue {
self.0.map_or(SqlValue::NullBigInt, SqlValue::BigInt)
}
}
impl ToSqlValue for FieldFloat {
fn to_sql_value(&self) -> SqlValue {
SqlValue::Float(self.0)
}
}
impl ToSqlValue for FieldDouble {
fn to_sql_value(&self) -> SqlValue {
SqlValue::Double(self.0)
}
}
impl ToSqlValue for FieldText {
fn to_sql_value(&self) -> SqlValue {
SqlValue::Text(self.0.clone())
}
}
impl ToSqlValue for FieldEmail {
fn to_sql_value(&self) -> SqlValue {
SqlValue::Text(self.0.clone())
}
}
impl ToSqlValue for FieldUrl {
fn to_sql_value(&self) -> SqlValue {
SqlValue::Text(self.0.clone())
}
}
impl ToSqlValue for FieldBytes {
fn to_sql_value(&self) -> SqlValue {
SqlValue::Bytes(self.0.clone())
}
}
impl ToSqlValue for Uuid {
fn to_sql_value(&self) -> SqlValue {
SqlValue::Uuid(*self)
}
}
impl ToSqlValue for i32 {
fn to_sql_value(&self) -> SqlValue {
SqlValue::Int(*self)
}
}
impl ToSqlValue for i64 {
fn to_sql_value(&self) -> SqlValue {
SqlValue::BigInt(*self)
}
}
impl ToSqlValue for FieldUuid {
fn to_sql_value(&self) -> SqlValue {
SqlValue::Uuid(self.0)
}
}
impl ToSqlValue for FieldDateTime {
fn to_sql_value(&self) -> SqlValue {
SqlValue::DateTime(self.0)
}
}
impl ToSqlValue for FieldDate {
fn to_sql_value(&self) -> SqlValue {
SqlValue::Date(self.0)
}
}
impl ToSqlValue for FieldTime {
fn to_sql_value(&self) -> SqlValue {
SqlValue::Time(self.0)
}
}
impl ToSqlValue for FieldJson {
fn to_sql_value(&self) -> SqlValue {
SqlValue::Json(self.0.clone())
}
}
impl<const MIN: usize, const MAX: usize> ToSqlValue for FieldVarchar<MIN, MAX> {
fn to_sql_value(&self) -> SqlValue {
SqlValue::Text(self.0.clone())
}
}
impl<const MIN: usize, const MAX: usize> ToSqlValue for FieldPassword<MIN, MAX> {
fn to_sql_value(&self) -> SqlValue {
SqlValue::Text(self.hash().to_string())
}
}
impl<const MIN: i64, const MAX: i64> ToSqlValue for FieldRange<MIN, MAX> {
fn to_sql_value(&self) -> SqlValue {
SqlValue::BigInt(self.0)
}
}
impl<const P: u8, const S: u8> ToSqlValue for FieldDecimal<P, S> {
fn to_sql_value(&self) -> SqlValue {
SqlValue::Decimal(self.0)
}
}
impl<const P: u8, const S: u8> ToSqlValue for Option<FieldDecimal<P, S>> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or(SqlValue::NullDecimal, |v| v.to_sql_value())
}
}
impl ToSqlValue for Option<FieldBool> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or(SqlValue::NullBool, |v| v.to_sql_value())
}
}
impl ToSqlValue for Option<FieldSmallInt> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or(SqlValue::NullSmallInt, |v| v.to_sql_value())
}
}
impl ToSqlValue for Option<FieldInt> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or(SqlValue::NullInt, |v| v.to_sql_value())
}
}
impl ToSqlValue for Option<FieldBigInt> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or(SqlValue::NullBigInt, |v| v.to_sql_value())
}
}
impl ToSqlValue for Option<FieldFloat> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or(SqlValue::NullFloat, |v| v.to_sql_value())
}
}
impl ToSqlValue for Option<FieldDouble> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or(SqlValue::NullDouble, |v| v.to_sql_value())
}
}
impl ToSqlValue for Option<FieldText> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or(SqlValue::NullText, |v| v.to_sql_value())
}
}
impl ToSqlValue for Option<FieldEmail> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or(SqlValue::NullText, |v| v.to_sql_value())
}
}
impl ToSqlValue for Option<FieldUrl> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or(SqlValue::NullText, |v| v.to_sql_value())
}
}
impl ToSqlValue for Option<FieldBytes> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or(SqlValue::NullBytes, |v| v.to_sql_value())
}
}
impl ToSqlValue for Option<FieldUuid> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or(SqlValue::NullUuid, |v| v.to_sql_value())
}
}
impl ToSqlValue for Option<FieldDateTime> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or(SqlValue::NullDateTime, |v| v.to_sql_value())
}
}
impl ToSqlValue for Option<FieldDate> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or(SqlValue::NullDate, |v| v.to_sql_value())
}
}
impl ToSqlValue for Option<FieldTime> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or(SqlValue::NullTime, |v| v.to_sql_value())
}
}
impl ToSqlValue for Option<FieldJson> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or(SqlValue::NullJson, |v| v.to_sql_value())
}
}
impl<const MIN: usize, const MAX: usize> ToSqlValue for Option<FieldVarchar<MIN, MAX>> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or(SqlValue::NullText, |v| v.to_sql_value())
}
}
impl<const MIN: usize, const MAX: usize> ToSqlValue for Option<FieldPassword<MIN, MAX>> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or(SqlValue::NullText, |v| v.to_sql_value())
}
}
impl<const MIN: i64, const MAX: i64> ToSqlValue for Option<FieldRange<MIN, MAX>> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or(SqlValue::NullBigInt, |v| v.to_sql_value())
}
}
impl<M: crate::Model> ToSqlValue for Option<ForeignKey<M>> {
fn to_sql_value(&self) -> SqlValue {
self.as_ref()
.map_or_else(<M::Pk as crate::PrimaryKey>::null_value, |v| {
v.to_sql_value()
})
}
}
#[derive(Serialize, Deserialize)]
#[serde(bound(
serialize = "<M as crate::Model>::Pk: Serialize",
deserialize = "<M as crate::Model>::Pk: serde::de::DeserializeOwned"
))]
pub struct ManyToMany<M: crate::Model>(
)`
pub Vec<<M as crate::Model>::Pk>,
std::marker::PhantomData<M>,
);
impl<M: crate::Model> Clone for ManyToMany<M> {
fn clone(&self) -> Self {
Self(self.0.clone(), std::marker::PhantomData)
}
}
impl<M: crate::Model> Default for ManyToMany<M> {
fn default() -> Self {
Self::empty()
}
}
impl<M: crate::Model> std::fmt::Debug for ManyToMany<M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ManyToMany").field(&self.0).finish()
}
}
impl<M: crate::Model> ManyToMany<M> {
pub fn empty() -> Self {
Self(Vec::new(), std::marker::PhantomData)
}
}
#[derive(Serialize, Deserialize)]
#[serde(bound(
serialize = "<M as crate::Model>::Pk: Serialize",
deserialize = "<M as crate::Model>::Pk: serde::de::DeserializeOwned"
))]
pub struct ForeignKey<M: crate::Model>(pub <M as crate::Model>::Pk, std::marker::PhantomData<M>);
impl<M: crate::Model> Clone for ForeignKey<M> {
fn clone(&self) -> Self {
*self
}
}
impl<M: crate::Model> Copy for ForeignKey<M> {}
impl<M: crate::Model> PartialEq for ForeignKey<M> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<M: crate::Model> Eq for ForeignKey<M> {}
impl<M: crate::Model> std::hash::Hash for ForeignKey<M> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<M: crate::Model> std::fmt::Debug for ForeignKey<M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ForeignKey").field(&self.0).finish()
}
}
impl<M: crate::Model> ForeignKey<M> {
pub fn new(id: <M as crate::Model>::Pk) -> Self {
Self(id, std::marker::PhantomData)
}
pub fn id(&self) -> <M as crate::Model>::Pk {
self.0
}
}
impl<M: crate::Model> ToSqlValue for ForeignKey<M> {
fn to_sql_value(&self) -> SqlValue {
self.0.to_sql_value()
}
}
impl From<bool> for FieldBool {
fn from(v: bool) -> Self {
Self(v)
}
}
impl From<i16> for FieldSmallInt {
fn from(v: i16) -> Self {
Self(v)
}
}
impl From<i32> for FieldInt {
fn from(v: i32) -> Self {
Self(v)
}
}
impl From<i64> for FieldBigInt {
fn from(v: i64) -> Self {
Self(v)
}
}
impl From<i32> for FieldAutoInt {
fn from(v: i32) -> Self {
Self(Some(v))
}
}
impl From<i64> for FieldAutoBigInt {
fn from(v: i64) -> Self {
Self(Some(v))
}
}
impl From<f32> for FieldFloat {
fn from(v: f32) -> Self {
Self(v)
}
}
impl From<f64> for FieldDouble {
fn from(v: f64) -> Self {
Self(v)
}
}
impl From<String> for FieldText {
fn from(v: String) -> Self {
Self(v)
}
}
impl From<&str> for FieldText {
fn from(v: &str) -> Self {
Self(v.to_string())
}
}
impl From<Vec<u8>> for FieldBytes {
fn from(v: Vec<u8>) -> Self {
Self(v)
}
}
impl From<Uuid> for FieldUuid {
fn from(v: Uuid) -> Self {
Self(v)
}
}
impl From<NaiveDate> for FieldDate {
fn from(v: NaiveDate) -> Self {
Self(v)
}
}
impl From<NaiveTime> for FieldTime {
fn from(v: NaiveTime) -> Self {
Self(v)
}
}
impl From<DateTime<Utc>> for FieldDateTime {
fn from(v: DateTime<Utc>) -> Self {
Self(v)
}
}
impl From<serde_json::Value> for FieldJson {
fn from(v: serde_json::Value) -> Self {
Self(v)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn validated_fields_display_their_value() {
let name = FieldVarchar::<1, 10>::new("ada").unwrap();
let email = FieldEmail::new("ada@x.io").unwrap();
assert_eq!(format!("{name} <{email}>"), "ada <ada@x.io>");
}
#[test]
fn varchar_counts_chars_not_bytes() {
assert!(FieldVarchar::<1, 3>::new("éèà").is_ok());
assert!(FieldVarchar::<4, 10>::new("éèà").is_err());
}
#[test]
fn varchar_rejects_out_of_bounds() {
assert!(FieldVarchar::<1, 100>::new("").is_err());
assert!(FieldVarchar::<1, 100>::new("a".repeat(101)).is_err());
assert_eq!(FieldVarchar::<1, 100>::new("ok").unwrap().as_str(), "ok");
}
#[test]
fn deserialize_validates() {
assert!(serde_json::from_str::<FieldVarchar<1, 5>>("\"\"").is_err());
assert!(serde_json::from_str::<FieldEmail>("\"nope\"").is_err());
assert!(serde_json::from_str::<FieldUrl>("\"ftp://x\"").is_err());
assert!(serde_json::from_str::<FieldRange<0, 10>>("11").is_err());
assert!(serde_json::from_str::<FieldRange<0, 10>>("10").is_ok());
}
#[test]
fn field_password_new_rejects_too_short() {
assert!(FieldPassword::<8, 128>::new("short").is_err());
}
#[test]
fn field_password_new_rejects_too_long() {
let long = "a".repeat(129);
assert!(FieldPassword::<8, 128>::new(long).is_err());
}
#[test]
fn field_password_verify_accepts_correct_and_rejects_wrong() {
let password = FieldPassword::<8, 128>::new("correct horse battery staple").unwrap();
assert!(password.verify("correct horse battery staple"));
assert!(!password.verify("wrong password"));
}
#[test]
fn field_password_hash_is_argon2id() {
let password = FieldPassword::<8, 128>::new("secret password").unwrap();
assert!(password.hash().starts_with("$argon2id$"));
}
#[test]
fn field_password_debug_does_not_leak_plaintext() {
let password = FieldPassword::<8, 128>::new("super secret").unwrap();
let debug = format!("{:?}", password);
assert!(!debug.contains("super secret"));
assert!(debug.contains("FieldPassword(<redacted>)"));
}
}
use crate::column::FilterValue;
macro_rules! filter_values {
($($field:ty => $($value:ty),+;)+) => {$($(
impl FilterValue<$field> for $value {
fn into_sql_value(self) -> SqlValue {
self.into()
}
}
)+)+};
}
filter_values! {
FieldBool => bool;
FieldSmallInt => i16;
FieldInt => i32;
FieldBigInt => i64;
FieldAutoInt => i32;
FieldAutoBigInt => i64;
FieldFloat => f32;
FieldDouble => f64;
FieldText => String, &str;
FieldEmail => String, &str;
FieldUrl => String, &str;
FieldBytes => Vec<u8>;
FieldUuid => Uuid;
FieldDateTime => DateTime<Utc>;
FieldDate => NaiveDate;
FieldTime => NaiveTime;
FieldJson => serde_json::Value;
}
impl<const MIN: usize, const MAX: usize> FilterValue<FieldVarchar<MIN, MAX>> for String {
fn into_sql_value(self) -> SqlValue {
SqlValue::Text(self)
}
}
impl<const MIN: usize, const MAX: usize> FilterValue<FieldVarchar<MIN, MAX>> for &str {
fn into_sql_value(self) -> SqlValue {
SqlValue::Text(self.to_string())
}
}
impl<const MIN: i64, const MAX: i64> FilterValue<FieldRange<MIN, MAX>> for i64 {
fn into_sql_value(self) -> SqlValue {
SqlValue::BigInt(self)
}
}
impl<const P: u8, const S: u8> FilterValue<FieldDecimal<P, S>> for rust_decimal::Decimal {
fn into_sql_value(self) -> SqlValue {
SqlValue::Decimal(self)
}
}
impl<M: crate::Model<Pk = Uuid>> FilterValue<ForeignKey<M>> for Uuid {
fn into_sql_value(self) -> SqlValue {
SqlValue::Uuid(self)
}
}
impl<M: crate::Model<Pk = i32>> FilterValue<ForeignKey<M>> for i32 {
fn into_sql_value(self) -> SqlValue {
SqlValue::Int(self)
}
}
impl<M: crate::Model<Pk = i64>> FilterValue<ForeignKey<M>> for i64 {
fn into_sql_value(self) -> SqlValue {
SqlValue::BigInt(self)
}
}
impl<M: crate::Model<Pk = Uuid>> FilterValue<ForeignKey<M>> for FieldUuid {
fn into_sql_value(self) -> SqlValue {
SqlValue::Uuid(self.0)
}
}