elefant-client 0.1.0

A pure rust implementation of a postgres client that is independent of the executor runtime
Documentation
mod enum_type;
mod from_sql_row;
mod oid;
mod standard_types;

// Refactored type implementations
mod binary;
mod bool;
mod char;
mod collections;
#[cfg(feature = "time")]
mod datetime;
#[cfg(feature = "json")]
mod json_type;
mod nullable;
mod numbers;
mod text;
#[cfg(feature = "uuid")]
mod uuid_type;
#[cfg(feature = "json")]
pub use json_type::{Json, Jsonb};
#[cfg(feature = "decimal")]
mod numeric_type;
mod point_type;
pub use point_type::Point;
mod network_type;
pub use enum_type::*;
pub use network_type::{Cidr, Inet};

use crate::protocol::FieldDescription;
use crate::ElefantClientError;
pub use from_sql_row::*;
pub use oid::*;
use std::error::Error;

/// Base trait providing shared functionality for both binary and text format trait implementations
pub trait FromSqlBase<'a>: Sized {
    fn accepts(field: &FieldDescription) -> bool {
        Self::accepts_postgres_type(field.data_type_oid)
    }

    fn accepts_postgres_type(oid: i32) -> bool;

    /// Extended accepts check with access to the enum type registry.
    /// Override for enum types and collections of enums.
    /// Default delegates to the static `accepts` check.
    fn accepts_with_registry(field: &FieldDescription, _registry: &EnumTypeRegistry) -> bool {
        Self::accepts(field)
    }

    fn from_null(field: &FieldDescription) -> Result<Self, ElefantClientError> {
        Err(ElefantClientError::UnexpectedNullValue {
            postgres_field: field.clone(),
        })
    }
}

/// Trait for types that can be deserialized from PostgreSQL binary format data
pub trait FromSqlBinary<'a>: FromSqlBase<'a> {
    fn from_sql_binary(
        raw: &'a [u8],
        field: &FieldDescription,
    ) -> Result<Self, Box<dyn Error + Sync + Send>>;
}

/// Trait for types that can be deserialized from PostgreSQL text format data
pub trait FromSqlText<'a>: FromSqlBase<'a> {
    fn from_sql_text(
        raw: &'a str,
        field: &FieldDescription,
    ) -> Result<Self, Box<dyn Error + Sync + Send>>;
}

/// Compatibility trait that provides both binary and text format support
/// This maintains backward compatibility with existing code
pub trait FromSql<'a>: FromSqlBinary<'a> + FromSqlText<'a> {}

/// Automatic implementation of FromSql for types that implement both binary and text variants
impl<'a, T> FromSql<'a> for T where T: FromSqlBinary<'a> + FromSqlText<'a> {}

/// A trait for types which can be created from a Postgres value without borrowing any data.
/// This is primarily useful for trait bounds on functions.
pub trait FromSqlOwned: for<'owned> FromSql<'owned> {}

impl<T> FromSqlOwned for T where T: for<'a> FromSql<'a> {}

/// A trait for types which can be created from binary Postgres values without borrowing any data.
pub trait FromSqlBinaryOwned: for<'owned> FromSqlBinary<'owned> {}

impl<T> FromSqlBinaryOwned for T where T: for<'a> FromSqlBinary<'a> {}

/// A trait for types which can be created from text Postgres values without borrowing any data.
pub trait FromSqlTextOwned: for<'owned> FromSqlText<'owned> {}

impl<T> FromSqlTextOwned for T where T: for<'a> FromSqlText<'a> {}

pub trait ToSql {
    fn to_sql_binary(
        &self,
        target_buffer: &mut Vec<u8>,
    ) -> Result<(), Box<dyn Error + Sync + Send>>;
    fn is_null(&self) -> bool {
        false
    }
}

pub trait PostgresNamedType {
    const PG_NAME: &'static str;
}

pub trait DomainType {
    type Inner: for<'owned> FromSql<'owned>;

    fn from_inner(inner: Self::Inner) -> Self;

    fn accepts(field: &FieldDescription) -> bool {
        Self::accepts_postgres_type(field.data_type_oid)
    }

    fn accepts_postgres_type(oid: i32) -> bool;
}

#[macro_export]
macro_rules! impl_from_sql_for_domain_type {
    ($typ: ty) => {
        impl<'a> FromSqlBase<'a> for $typ {
            fn accepts(field: &FieldDescription) -> bool {
                <Self as DomainType>::accepts(field)
            }

            fn accepts_postgres_type(oid: i32) -> bool {
                <Self as DomainType>::accepts_postgres_type(oid)
            }
        }

        impl<'a> FromSqlBinary<'a> for $typ {
            fn from_sql_binary(
                raw: &'a [u8],
                field: &FieldDescription,
            ) -> Result<Self, Box<dyn Error + Sync + Send>> {
                let inner = <Self as DomainType>::Inner::from_sql_binary(raw, field)?;
                Ok(Self::from_inner(inner))
            }
        }

        impl<'a> FromSqlText<'a> for $typ {
            fn from_sql_text(
                raw: &'a str,
                field: &FieldDescription,
            ) -> Result<Self, Box<dyn Error + Sync + Send>> {
                let inner = <Self as DomainType>::Inner::from_sql_text(raw, field)?;
                Ok(Self::from_inner(inner))
            }
        }
    };
}

pub struct PostgresType {
    pub(crate) oid: i32,
    name: &'static str,
    /// The underlying type
    element: Option<&'static PostgresType>,
    /// True if this is an array type
    is_array: bool,

    array_delimiter: char,
}

impl PostgresType {
    fn inner_most(&self) -> &PostgresType {
        match self.element {
            Some(element) => element.inner_most(),
            None => self,
        }
    }
}