1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use core::ops::Range;

use xitca_io::bytes::Bytes;
use xitca_unsafe_collection::bytes::BytesStr;

use super::types::{FromSql, Type};

pub type FromSqlError = Box<dyn std::error::Error + Sync + Send>;

/// extension trait for [FromSql]
///
/// instead of working with explicit reference as `&[u8]` for parsing raw sql bytes this extension
/// offers cheap copy/slicing of [Bytes] type for reference counting based zero copy parsing.
///
/// # Examples
/// ```rust
/// # use xitca_postgres::row::Row;
/// use xitca_unsafe_collection::bytes::BytesStr; // a reference counted &str type has FromSqlExt impl
/// fn parse_row(row: Row<'_>) {
///     let s = row.get_zc::<BytesStr>(0); // parse index0 column with zero copy.
///     println!("{}", s.as_str());
/// }
/// ```
pub trait FromSqlExt<'a>: Sized {
    /// [Type] represents the Postgres type hint which Self must be matching.
    /// [Bytes] represents the reference of raw bytes of row data Self belongs to.
    /// [Range] represents the start and end indexing into the raw data for correctly parsing Self.
    /// When [Range] is an empty value it indicates trait implementor encounters a null pg value. It's
    /// suggested to call [Range::is_empty] to check for this case and properly handle it
    fn from_sql_nullable_ext(ty: &Type, col: (&Range<usize>, &'a Bytes)) -> Result<Self, FromSqlError>;

    /// Determines if a value of this type can be created from the specified Postgres [Type].
    fn accepts(ty: &Type) -> bool;
}

macro_rules! default_impl {
    ($ty: ty) => {
        impl<'a> FromSqlExt<'a> for $ty {
            default_impl!();
        }
    };
    () => {
        #[inline]
        fn from_sql_nullable_ext(ty: &Type, (range, buf): (&Range<usize>, &'a Bytes)) -> Result<Self, FromSqlError> {
            <Self as FromSql>::from_sql_nullable(ty, buf.get(range.clone()))
        }

        #[inline]
        fn accepts(ty: &Type) -> bool {
            <Self as FromSql>::accepts(ty)
        }
    };
}

impl<'a, T> FromSqlExt<'a> for Option<T>
where
    T: FromSql<'a>,
{
    default_impl!();
}

impl<'a, T> FromSqlExt<'a> for Vec<T>
where
    T: FromSql<'a>,
{
    default_impl!();
}

impl<'a, T> FromSqlExt<'a> for Box<[T]>
where
    T: FromSql<'a>,
{
    default_impl!();
}

// default_impl!(Vec<u8>);
default_impl!(&'a [u8]);
default_impl!(&'a str);
default_impl!(String);
default_impl!(Box<str>);
default_impl!(bool);
default_impl!(i8);
default_impl!(i16);
default_impl!(i32);
default_impl!(u32);
default_impl!(i64);
default_impl!(f32);
default_impl!(f64);

impl<'a> FromSqlExt<'a> for BytesStr {
    fn from_sql_nullable_ext(ty: &Type, (range, buf): (&Range<usize>, &'a Bytes)) -> Result<Self, FromSqlError> {
        // copy/paste from postgres-protocol dependency.
        fn adjust_range(name: &str, range: &Range<usize>, buf: &Bytes) -> Result<(usize, usize), FromSqlError> {
            if buf[range.start] == 1u8 {
                Ok((range.start + 1, range.end))
            } else {
                Err(format!("only {name} version 1 supported").into())
            }
        }

        if range.is_empty() {
            return <&str as FromSql>::from_sql_null(ty)
                .map(|_| unreachable!("<&str as FromSql>::from_sql_null should always yield Result::Err branch"));
        }

        let (start, end) = match ty.name() {
            "ltree" => adjust_range("ltree", range, buf)?,
            "lquery" => adjust_range("lquery", range, buf)?,
            "ltxtquery" => adjust_range("ltxtquery", range, buf)?,
            _ => (range.start, range.end),
        };

        BytesStr::try_from(buf.slice(start..end)).map_err(Into::into)
    }

    #[inline]
    fn accepts(ty: &Type) -> bool {
        <&str as FromSql>::accepts(ty)
    }
}

impl<'a> FromSqlExt<'a> for Bytes {
    fn from_sql_nullable_ext(ty: &Type, (range, buf): (&Range<usize>, &'a Bytes)) -> Result<Self, FromSqlError> {
        if range.is_empty() {
            return <&[u8] as FromSql>::from_sql_null(ty)
                .map(|_| unreachable!("<&[u8] as FromSql>::from_sql_null should always yield Result::Err branch"));
        }

        Ok(buf.slice(range.start..range.end))
    }

    #[inline]
    fn accepts(ty: &Type) -> bool {
        <&[u8] as FromSql>::accepts(ty)
    }
}