use crate::decode::Decode;
use crate::encode::{Encode, IsNull};
use crate::error::BoxDynError;
use crate::type_info::FirebirdSqlType;
use crate::{Firebird, FirebirdTypeInfo, FirebirdValueFormat, FirebirdValueRef};
use sqlx_core::types::Type;
use std::sync::Arc;
fn int_compatible(ty: &FirebirdTypeInfo) -> bool {
matches!(
ty.r#type,
FirebirdSqlType::Short | FirebirdSqlType::Long | FirebirdSqlType::Int64
) && ty.sqlscale == 0
}
fn real_compatible(ty: &FirebirdTypeInfo) -> bool {
matches!(
ty.r#type,
FirebirdSqlType::Float | FirebirdSqlType::Double
)
}
fn text_compatible(ty: &FirebirdTypeInfo) -> bool {
matches!(
ty.r#type,
FirebirdSqlType::Text | FirebirdSqlType::Varying
)
}
fn blob_compatible(ty: &FirebirdTypeInfo) -> bool {
matches!(ty.r#type, FirebirdSqlType::Blob)
}
fn int_decode(value: FirebirdValueRef<'_>) -> Result<i64, BoxDynError> {
match value.format() {
FirebirdValueFormat::Text => Ok(value.as_str()?.parse()?),
FirebirdValueFormat::Binary => {
let buf = value.as_bytes()?;
match buf.len() {
2 => Ok(i16::from_le_bytes(buf.try_into()?) as i64),
4 => Ok(i32::from_le_bytes(buf.try_into()?) as i64),
8 => Ok(i64::from_le_bytes(buf.try_into()?)),
other => Err(format!(
"expected 2, 4, or 8 bytes for integer value, got {other}"
)
.into()),
}
}
}
}
impl Type<Firebird> for bool {
fn type_info() -> FirebirdTypeInfo {
FirebirdTypeInfo::new(FirebirdSqlType::Boolean)
}
fn compatible(ty: &FirebirdTypeInfo) -> bool {
matches!(
ty.r#type,
FirebirdSqlType::Boolean
| FirebirdSqlType::Short
| FirebirdSqlType::Long
| FirebirdSqlType::Int64
)
}
}
impl Encode<'_, Firebird> for bool {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
buf.push(firebirust::Param::Boolean(*self));
Ok(IsNull::No)
}
}
impl Decode<'_, Firebird> for bool {
fn decode(value: FirebirdValueRef<'_>) -> Result<Self, BoxDynError> {
match value.format() {
FirebirdValueFormat::Text => {
let s = value.as_str()?;
Ok(s != "0" && !s.is_empty())
}
FirebirdValueFormat::Binary => {
let buf = value.as_bytes()?;
Ok(buf.iter().any(|&b| b != 0))
}
}
}
}
impl Type<Firebird> for i8 {
fn type_info() -> FirebirdTypeInfo {
FirebirdTypeInfo::new(FirebirdSqlType::Short)
}
fn compatible(ty: &FirebirdTypeInfo) -> bool {
int_compatible(ty)
}
}
impl Encode<'_, Firebird> for i8 {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
buf.push(firebirust::Param::Short(*self as i16));
Ok(IsNull::No)
}
}
impl Decode<'_, Firebird> for i8 {
fn decode(value: FirebirdValueRef<'_>) -> Result<Self, BoxDynError> {
int_decode(value)?.try_into().map_err(Into::into)
}
}
impl Type<Firebird> for i16 {
fn type_info() -> FirebirdTypeInfo {
FirebirdTypeInfo::new(FirebirdSqlType::Short)
}
fn compatible(ty: &FirebirdTypeInfo) -> bool {
int_compatible(ty)
}
}
impl Encode<'_, Firebird> for i16 {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
buf.push(firebirust::Param::Short(*self));
Ok(IsNull::No)
}
}
impl Decode<'_, Firebird> for i16 {
fn decode(value: FirebirdValueRef<'_>) -> Result<Self, BoxDynError> {
int_decode(value)?.try_into().map_err(Into::into)
}
}
impl Type<Firebird> for i32 {
fn type_info() -> FirebirdTypeInfo {
FirebirdTypeInfo::new(FirebirdSqlType::Long)
}
fn compatible(ty: &FirebirdTypeInfo) -> bool {
int_compatible(ty)
}
}
impl Encode<'_, Firebird> for i32 {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
buf.push(firebirust::Param::Long(*self));
Ok(IsNull::No)
}
}
impl Decode<'_, Firebird> for i32 {
fn decode(value: FirebirdValueRef<'_>) -> Result<Self, BoxDynError> {
int_decode(value)?.try_into().map_err(Into::into)
}
}
impl Type<Firebird> for i64 {
fn type_info() -> FirebirdTypeInfo {
FirebirdTypeInfo::new(FirebirdSqlType::Int64)
}
fn compatible(ty: &FirebirdTypeInfo) -> bool {
int_compatible(ty)
}
}
impl Encode<'_, Firebird> for i64 {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
buf.push(firebirust::Param::Int64(*self));
Ok(IsNull::No)
}
}
impl Decode<'_, Firebird> for i64 {
fn decode(value: FirebirdValueRef<'_>) -> Result<Self, BoxDynError> {
int_decode(value)
}
}
impl Type<Firebird> for u8 {
fn type_info() -> FirebirdTypeInfo {
FirebirdTypeInfo::new(FirebirdSqlType::Short)
}
fn compatible(ty: &FirebirdTypeInfo) -> bool {
int_compatible(ty)
}
}
impl Encode<'_, Firebird> for u8 {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
buf.push(firebirust::Param::Short(*self as i16));
Ok(IsNull::No)
}
}
impl Decode<'_, Firebird> for u8 {
fn decode(value: FirebirdValueRef<'_>) -> Result<Self, BoxDynError> {
int_decode(value)?.try_into().map_err(Into::into)
}
}
impl Type<Firebird> for u16 {
fn type_info() -> FirebirdTypeInfo {
FirebirdTypeInfo::new(FirebirdSqlType::Long)
}
fn compatible(ty: &FirebirdTypeInfo) -> bool {
int_compatible(ty)
}
}
impl Encode<'_, Firebird> for u16 {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
buf.push(firebirust::Param::Long(*self as i32));
Ok(IsNull::No)
}
}
impl Decode<'_, Firebird> for u16 {
fn decode(value: FirebirdValueRef<'_>) -> Result<Self, BoxDynError> {
int_decode(value)?.try_into().map_err(Into::into)
}
}
impl Type<Firebird> for u32 {
fn type_info() -> FirebirdTypeInfo {
FirebirdTypeInfo::new(FirebirdSqlType::Int64)
}
fn compatible(ty: &FirebirdTypeInfo) -> bool {
int_compatible(ty)
}
}
impl Encode<'_, Firebird> for u32 {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
buf.push(firebirust::Param::Int64(*self as i64));
Ok(IsNull::No)
}
}
impl Decode<'_, Firebird> for u32 {
fn decode(value: FirebirdValueRef<'_>) -> Result<Self, BoxDynError> {
int_decode(value)?.try_into().map_err(Into::into)
}
}
impl Type<Firebird> for u64 {
fn type_info() -> FirebirdTypeInfo {
FirebirdTypeInfo::new(FirebirdSqlType::Int64)
}
fn compatible(ty: &FirebirdTypeInfo) -> bool {
int_compatible(ty)
}
}
impl Decode<'_, Firebird> for u64 {
fn decode(value: FirebirdValueRef<'_>) -> Result<Self, BoxDynError> {
int_decode(value)?.try_into().map_err(Into::into)
}
}
impl Type<Firebird> for f32 {
fn type_info() -> FirebirdTypeInfo {
FirebirdTypeInfo::new(FirebirdSqlType::Float)
}
fn compatible(ty: &FirebirdTypeInfo) -> bool {
real_compatible(ty)
}
}
impl Encode<'_, Firebird> for f32 {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
buf.push(firebirust::Param::Float(*self));
Ok(IsNull::No)
}
}
impl Decode<'_, Firebird> for f32 {
fn decode(value: FirebirdValueRef<'_>) -> Result<Self, BoxDynError> {
match value.format() {
FirebirdValueFormat::Text => Ok(value.as_str()?.parse()?),
FirebirdValueFormat::Binary => {
let buf = value.as_bytes()?;
match buf.len() {
4 => Ok(f32::from_le_bytes(buf.try_into()?)),
#[allow(clippy::cast_possible_truncation)]
8 => Ok(f64::from_le_bytes(buf.try_into()?) as f32),
other => Err(format!(
"expected 4 or 8 bytes for float value, got {other}"
)
.into()),
}
}
}
}
}
impl Type<Firebird> for f64 {
fn type_info() -> FirebirdTypeInfo {
FirebirdTypeInfo::new(FirebirdSqlType::Double)
}
fn compatible(ty: &FirebirdTypeInfo) -> bool {
real_compatible(ty)
}
}
impl Encode<'_, Firebird> for f64 {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
buf.push(firebirust::Param::Double(*self));
Ok(IsNull::No)
}
}
impl Decode<'_, Firebird> for f64 {
fn decode(value: FirebirdValueRef<'_>) -> Result<Self, BoxDynError> {
match value.format() {
FirebirdValueFormat::Text => Ok(value.as_str()?.parse()?),
FirebirdValueFormat::Binary => {
let buf = value.as_bytes()?;
match buf.len() {
4 => Ok(f32::from_le_bytes(buf.try_into()?) as f64),
8 => Ok(f64::from_le_bytes(buf.try_into()?)),
other => Err(format!(
"expected 4 or 8 bytes for double value, got {other}"
)
.into()),
}
}
}
}
}
impl Type<Firebird> for str {
fn type_info() -> FirebirdTypeInfo {
FirebirdTypeInfo::new(FirebirdSqlType::Varying)
}
fn compatible(ty: &FirebirdTypeInfo) -> bool {
text_compatible(ty)
}
}
impl Encode<'_, Firebird> for &'_ str {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
buf.push(firebirust::Param::Text(self.to_string()));
Ok(IsNull::No)
}
}
impl<'r> Decode<'r, Firebird> for &'r str {
fn decode(value: FirebirdValueRef<'r>) -> Result<Self, BoxDynError> {
value.as_str()
}
}
impl Type<Firebird> for String {
fn type_info() -> FirebirdTypeInfo {
<str as Type<Firebird>>::type_info()
}
fn compatible(ty: &FirebirdTypeInfo) -> bool {
<str as Type<Firebird>>::compatible(ty)
}
}
impl Encode<'_, Firebird> for String {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
buf.push(firebirust::Param::Text(self.clone()));
Ok(IsNull::No)
}
}
impl Decode<'_, Firebird> for String {
fn decode(value: FirebirdValueRef<'_>) -> Result<Self, BoxDynError> {
Ok(value.as_str()?.to_owned())
}
}
sqlx_core::forward_encode_impl!(Arc<str>, &str, Firebird);
sqlx_core::forward_encode_impl!(Box<str>, &str, Firebird);
impl Type<Firebird> for [u8] {
fn type_info() -> FirebirdTypeInfo {
FirebirdTypeInfo::new(FirebirdSqlType::Blob)
}
fn compatible(ty: &FirebirdTypeInfo) -> bool {
blob_compatible(ty)
}
}
impl Encode<'_, Firebird> for &'_ [u8] {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
buf.push(firebirust::Param::Blob(self.to_vec()));
Ok(IsNull::No)
}
}
impl<'r> Decode<'r, Firebird> for &'r [u8] {
fn decode(value: FirebirdValueRef<'r>) -> Result<Self, BoxDynError> {
value.as_bytes()
}
}
impl Type<Firebird> for Vec<u8> {
fn type_info() -> FirebirdTypeInfo {
<[u8] as Type<Firebird>>::type_info()
}
fn compatible(ty: &FirebirdTypeInfo) -> bool {
<[u8] as Type<Firebird>>::compatible(ty)
}
}
impl Encode<'_, Firebird> for Vec<u8> {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
buf.push(firebirust::Param::Blob(self.clone()));
Ok(IsNull::No)
}
}
impl Decode<'_, Firebird> for Vec<u8> {
fn decode(value: FirebirdValueRef<'_>) -> Result<Self, BoxDynError> {
Ok(value.as_bytes()?.to_vec())
}
}
#[cfg(feature = "chrono")]
impl Type<Firebird> for chrono::NaiveDate {
fn type_info() -> FirebirdTypeInfo {
FirebirdTypeInfo::new(FirebirdSqlType::Date)
}
}
#[cfg(feature = "chrono")]
impl Encode<'_, Firebird> for chrono::NaiveDate {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
buf.push(firebirust::Param::Date(*self));
Ok(IsNull::No)
}
}
#[cfg(feature = "chrono")]
impl Decode<'_, Firebird> for chrono::NaiveDate {
fn decode(value: FirebirdValueRef<'_>) -> Result<Self, BoxDynError> {
let s = value.as_str()?;
Ok(s.parse()?)
}
}
#[cfg(feature = "chrono")]
impl Type<Firebird> for chrono::NaiveTime {
fn type_info() -> FirebirdTypeInfo {
FirebirdTypeInfo::new(FirebirdSqlType::Time)
}
}
#[cfg(feature = "chrono")]
impl Encode<'_, Firebird> for chrono::NaiveTime {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
buf.push(firebirust::Param::Time(*self));
Ok(IsNull::No)
}
}
#[cfg(feature = "chrono")]
impl Decode<'_, Firebird> for chrono::NaiveTime {
fn decode(value: FirebirdValueRef<'_>) -> Result<Self, BoxDynError> {
let s = value.as_str()?;
Ok(s.parse()?)
}
}
#[cfg(feature = "chrono")]
impl Type<Firebird> for chrono::NaiveDateTime {
fn type_info() -> FirebirdTypeInfo {
FirebirdTypeInfo::new(FirebirdSqlType::Timestamp)
}
}
#[cfg(feature = "chrono")]
impl Encode<'_, Firebird> for chrono::NaiveDateTime {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
buf.push(firebirust::Param::TimeStamp(*self));
Ok(IsNull::No)
}
}
#[cfg(feature = "chrono")]
impl Decode<'_, Firebird> for chrono::NaiveDateTime {
fn decode(value: FirebirdValueRef<'_>) -> Result<Self, BoxDynError> {
let s = value.as_str()?;
let s = s.replace(' ', "T");
Ok(s.parse()?)
}
}
#[cfg(feature = "chrono")]
impl Type<Firebird> for chrono::DateTime<chrono::Utc> {
fn type_info() -> FirebirdTypeInfo {
FirebirdTypeInfo::new(FirebirdSqlType::Timestamp)
}
fn compatible(ty: &FirebirdTypeInfo) -> bool {
matches!(
ty.r#type,
FirebirdSqlType::Timestamp | FirebirdSqlType::TimestampTz
)
}
}
#[cfg(feature = "chrono")]
impl Encode<'_, Firebird> for chrono::DateTime<chrono::Utc> {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
buf.push(firebirust::Param::TimeStamp(self.naive_utc()));
Ok(IsNull::No)
}
}
#[cfg(feature = "chrono")]
impl Decode<'_, Firebird> for chrono::DateTime<chrono::Utc> {
fn decode(value: FirebirdValueRef<'_>) -> Result<Self, BoxDynError> {
let s = value.as_str()?;
let s = s.replace(' ', "T");
let naive: chrono::NaiveDateTime = s.parse()?;
Ok(chrono::DateTime::from_naive_utc_and_offset(naive, chrono::Utc))
}
}
#[cfg(feature = "rust_decimal")]
impl Type<Firebird> for rust_decimal::Decimal {
fn type_info() -> FirebirdTypeInfo {
FirebirdTypeInfo::new(FirebirdSqlType::DecFixed)
}
fn compatible(ty: &FirebirdTypeInfo) -> bool {
matches!(
ty.r#type,
FirebirdSqlType::DecFixed | FirebirdSqlType::Dec64 | FirebirdSqlType::Dec128
) || (matches!(
ty.r#type,
FirebirdSqlType::Short | FirebirdSqlType::Long | FirebirdSqlType::Int64 | FirebirdSqlType::Int128
) && ty.sqlscale != 0)
}
}
#[cfg(feature = "rust_decimal")]
impl Encode<'_, Firebird> for rust_decimal::Decimal {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
buf.push(firebirust::Param::Decimal(*self));
Ok(IsNull::No)
}
}
#[cfg(feature = "rust_decimal")]
impl Decode<'_, Firebird> for rust_decimal::Decimal {
fn decode(value: FirebirdValueRef<'_>) -> Result<Self, BoxDynError> {
let s = value.as_str()?;
Ok(s.parse()?)
}
}
#[cfg(feature = "json")]
impl<T> Type<Firebird> for sqlx_core::types::Json<T> {
fn type_info() -> FirebirdTypeInfo {
FirebirdTypeInfo::new(FirebirdSqlType::Varying)
}
fn compatible(ty: &FirebirdTypeInfo) -> bool {
text_compatible(ty) || blob_compatible(ty)
}
}
#[cfg(feature = "json")]
impl<T: serde::Serialize> Encode<'_, Firebird> for sqlx_core::types::Json<T> {
fn encode_by_ref(&self, buf: &mut Vec<firebirust::Param>) -> Result<IsNull, BoxDynError> {
let json_str = serde_json::to_string(&self.0)?;
buf.push(firebirust::Param::Text(json_str));
Ok(IsNull::No)
}
}
#[cfg(feature = "json")]
impl<'r, T: serde::Deserialize<'r>> Decode<'r, Firebird> for sqlx_core::types::Json<T> {
fn decode(value: FirebirdValueRef<'r>) -> Result<Self, BoxDynError> {
let s = value.as_str()?;
Ok(sqlx_core::types::Json(serde_json::from_str(s)?))
}
}