Lines
9.37 %
Functions
3.33 %
Branches
0 %
use crate::{
crud::{CrudOperations, Transaction},
mapper::RowMapper,
};
#[cfg(feature = "mysql")]
use canyon_connection::mysql_async::{self, prelude::ToValue};
#[cfg(feature = "mssql")]
use canyon_connection::tiberius::{self, ColumnData, IntoSql};
#[cfg(feature = "postgres")]
use canyon_connection::tokio_postgres::{self, types::ToSql};
use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime, Utc};
use std::{any::Any, borrow::Cow};
/// Created for retrieve the field's name of a field of a struct, giving
/// the Canyon's autogenerated enum with the variants that maps this
/// fields.
///
/// ```
/// pub struct Struct<'a> {
/// pub some_field: &'a str
/// }
/// // Autogenerated enum
/// #[derive(Debug)]
/// #[allow(non_camel_case_types)]
/// pub enum StructField {
/// some_field
/// So, to retrieve the field's name, something like this w'd be used on some part
/// of the Canyon's Manager crate, to wire the necessary code to pass the field
/// name, retrieved from the enum variant, to a called.
/// // Something like:
/// `let struct_field_name_from_variant = StructField::some_field.field_name_as_str();`
pub trait FieldIdentifier<T>
where
T: Transaction<T> + CrudOperations<T> + RowMapper<T>,
{
fn as_str(&self) -> &'static str;
}
/// Represents some kind of introspection to make the implementors
/// able to retrieve a value inside some variant of an associated enum type.
/// and convert it to a tuple struct formed by the column name as an String,
/// and the dynamic value of the [`QueryParameter<'_>`] trait object contained
/// inside the variant requested,
/// enabling a conversion of that value into something
/// that can be part of an SQL query.
/// Ex:
/// `SELECT * FROM some_table WHERE id = 2`
/// That '2' it's extracted from some enum that implements [`FieldValueIdentifier`],
/// where usually the variant w'd be something like:
/// pub enum Enum {
/// IntVariant(i32)
pub trait FieldValueIdentifier<'a, T>
fn value(self) -> (&'static str, &'a dyn QueryParameter<'a>);
/// Bounds to some type T in order to make it callable over some fn parameter T
/// Represents the ability of an struct to be considered as candidate to perform
/// actions over it as it holds the 'parent' side of a foreign key relation.
/// Usually, it's used on the Canyon macros to retrieve the column that
/// this side of the relation it's representing
pub trait ForeignKeyable<T> {
/// Retrieves the field related to the column passed in
fn get_fk_column(&self, column: &str) -> Option<&dyn QueryParameter<'_>>;
/// Generic abstraction to represent any of the Row types
/// from the client crates
pub trait Row {
fn as_any(&self) -> &dyn Any;
impl Row for tokio_postgres::Row {
fn as_any(&self) -> &dyn Any {
self
impl Row for tiberius::Row {
impl Row for mysql_async::Row {
/// Generic abstraction for hold a Column type that will be one of the Column
/// types present in the dependent crates
// #[derive(Copy, Clone)]
pub struct Column<'a> {
name: Cow<'a, str>,
type_: ColumnType,
impl<'a> Column<'a> {
pub fn name(&self) -> &str {
&self.name
pub fn column_type(&self) -> &ColumnType {
&self.type_
// pub fn type_(&'a self) -> &'_ dyn Type {
// match (*self).type_ {
// #[cfg(feature = "postgres")] ColumnType::Postgres(v) => v as &'a dyn Type,
// #[cfg(feature = "mssql")] ColumnType::SqlServer(v) => v as &'a dyn Type,
// }
pub trait Type {
impl Type for tokio_postgres::types::Type {
impl Type for tiberius::ColumnType {
impl Type for mysql_async::consts::ColumnType {
/// Wrapper over the dependencies Column's types
pub enum ColumnType {
Postgres(tokio_postgres::types::Type),
SqlServer(tiberius::ColumnType),
MySQL(mysql_async::consts::ColumnType),
pub trait RowOperations {
fn get_postgres<'a, Output>(&'a self, col_name: &'a str) -> Output
Output: tokio_postgres::types::FromSql<'a>;
fn get_mssql<'a, Output>(&'a self, col_name: &'a str) -> Output
Output: tiberius::FromSql<'a>;
fn get_mysql<'a, Output>(&'a self, col_name: &'a str) -> Output
Output: mysql_async::prelude::FromValue;
fn get_postgres_opt<'a, Output>(&'a self, col_name: &'a str) -> Option<Output>
fn get_mssql_opt<'a, Output>(&'a self, col_name: &'a str) -> Option<Output>
fn get_mysql_opt<'a, Output>(&'a self, col_name: &'a str) -> Option<Output>
fn columns(&self) -> Vec<Column>;
impl RowOperations for &dyn Row {
Output: tokio_postgres::types::FromSql<'a>,
if let Some(row) = self.as_any().downcast_ref::<tokio_postgres::Row>() {
return row.get::<&str, Output>(col_name);
panic!() // TODO into result and propagate
Output: tiberius::FromSql<'a>,
if let Some(row) = self.as_any().downcast_ref::<tiberius::Row>() {
return row
.get::<Output, &str>(col_name)
.expect("Failed to obtain a row in the MSSQL migrations");
Output: mysql_async::prelude::FromValue,
self.get_mysql_opt(col_name)
.expect("Failed to obtain a column in the MySql")
return row.get::<&str, Option<Output>>(col_name);
return row.get::<Output, &str>(col_name);
if let Some(row) = self.as_any().downcast_ref::<mysql_async::Row>() {
fn columns(&self) -> Vec<Column> {
let mut cols = vec![];
if self.as_any().is::<tokio_postgres::Row>() {
self.as_any()
.downcast_ref::<tokio_postgres::Row>()
.expect("Not a tokio postgres Row for column")
.columns()
.iter()
.for_each(|c| {
cols.push(Column {
name: Cow::from(c.name()),
type_: ColumnType::Postgres(c.type_().to_owned()),
})
if self.as_any().is::<tiberius::Row>() {
.downcast_ref::<tiberius::Row>()
.expect("Not a Tiberius Row for column")
type_: ColumnType::SqlServer(c.column_type()),
if let Some(mysql_row) = self.as_any().downcast_ref::<mysql_async::Row>() {
mysql_row.columns_ref().iter().for_each(|c| {
name: c.name_str(),
type_: ColumnType::MySQL(c.column_type()),
cols
/// Defines a trait for represent type bounds against the allowed
/// data types supported by Canyon to be used as query parameters.
pub trait QueryParameter<'a>: std::fmt::Debug + Sync + Send {
fn as_postgres_param(&self) -> &(dyn ToSql + Sync);
fn as_sqlserver_param(&self) -> ColumnData<'_>;
fn as_mysql_param(&self) -> &dyn mysql_async::prelude::ToValue;
/// The implementation of the [`canyon_connection::tiberius`] [`IntoSql`] for the
/// query parameters.
/// This implementation is necessary because of the generic amplitude
/// of the arguments of the [`Transaction::query`], that should work with
/// a collection of [`QueryParameter<'a>`], in order to allow a workflow
/// that is not dependent of the specific type of the argument that holds
/// the query parameters of the database connectors
impl<'a> IntoSql<'a> for &'a dyn QueryParameter<'a> {
fn into_sql(self) -> ColumnData<'a> {
self.as_sqlserver_param()
//TODO Pending to review and see if it is necessary to apply something similar to the previous implementation.
impl<'a> QueryParameter<'a> for bool {
fn as_postgres_param(&self) -> &(dyn ToSql + Sync) {
fn as_sqlserver_param(&self) -> ColumnData<'_> {
ColumnData::Bit(Some(*self))
fn as_mysql_param(&self) -> &dyn ToValue {
impl<'a> QueryParameter<'a> for i16 {
ColumnData::I16(Some(*self))
fn as_mysql_param(&self) -> &dyn mysql_async::prelude::ToValue {
impl<'a> QueryParameter<'a> for &i16 {
ColumnData::I16(Some(**self))
impl<'a> QueryParameter<'a> for Option<i16> {
ColumnData::I16(*self)
impl<'a> QueryParameter<'a> for Option<&i16> {
ColumnData::I16(Some(*self.unwrap()))
impl<'a> QueryParameter<'a> for i32 {
ColumnData::I32(Some(*self))
impl<'a> QueryParameter<'a> for &i32 {
ColumnData::I32(Some(**self))
impl<'a> QueryParameter<'a> for Option<i32> {
ColumnData::I32(*self)
impl<'a> QueryParameter<'a> for Option<&i32> {
ColumnData::I32(Some(*self.unwrap()))
impl<'a> QueryParameter<'a> for f32 {
ColumnData::F32(Some(*self))
impl<'a> QueryParameter<'a> for &f32 {
ColumnData::F32(Some(**self))
impl<'a> QueryParameter<'a> for Option<f32> {
ColumnData::F32(*self)
impl<'a> QueryParameter<'a> for Option<&f32> {
ColumnData::F32(Some(
*self.expect("Error on an f32 value on QueryParameter<'_>"),
))
impl<'a> QueryParameter<'a> for f64 {
ColumnData::F64(Some(*self))
impl<'a> QueryParameter<'a> for &f64 {
ColumnData::F64(Some(**self))
impl<'a> QueryParameter<'a> for Option<f64> {
ColumnData::F64(*self)
impl<'a> QueryParameter<'a> for Option<&f64> {
ColumnData::F64(Some(
*self.expect("Error on an f64 value on QueryParameter<'_>"),
impl<'a> QueryParameter<'a> for i64 {
ColumnData::I64(Some(*self))
impl<'a> QueryParameter<'a> for &i64 {
ColumnData::I64(Some(**self))
impl<'a> QueryParameter<'a> for Option<i64> {
ColumnData::I64(*self)
impl<'a> QueryParameter<'a> for Option<&i64> {
ColumnData::I64(Some(*self.unwrap()))
impl<'a> QueryParameter<'a> for String {
ColumnData::String(Some(std::borrow::Cow::Owned(self.to_owned())))
impl<'a> QueryParameter<'a> for &String {
ColumnData::String(Some(std::borrow::Cow::Borrowed(self)))
impl<'a> QueryParameter<'a> for Option<String> {
match self {
Some(string) => ColumnData::String(Some(std::borrow::Cow::Owned(string.to_owned()))),
None => ColumnData::String(None),
impl<'a> QueryParameter<'a> for Option<&String> {
Some(string) => ColumnData::String(Some(std::borrow::Cow::Borrowed(string))),
impl<'a> QueryParameter<'a> for &'_ str {
ColumnData::String(Some(std::borrow::Cow::Borrowed(*self)))
impl<'a> QueryParameter<'a> for Option<&'_ str> {
match *self {
Some(str) => ColumnData::String(Some(std::borrow::Cow::Borrowed(str))),
impl<'a> QueryParameter<'a> for NaiveDate {
self.into_sql()
impl<'a> QueryParameter<'a> for Option<NaiveDate> {
impl<'a> QueryParameter<'a> for NaiveTime {
impl<'a> QueryParameter<'a> for Option<NaiveTime> {
impl<'a> QueryParameter<'a> for NaiveDateTime {
impl<'a> QueryParameter<'a> for Option<NaiveDateTime> {
//TODO pending
impl<'a> QueryParameter<'a> for DateTime<FixedOffset> {
todo!()
impl<'a> QueryParameter<'a> for Option<DateTime<FixedOffset>> {
impl<'a> QueryParameter<'a> for DateTime<Utc> {
impl<'a> QueryParameter<'a> for Option<DateTime<Utc>> {