use std::borrow::Cow;
#[cfg(feature = "chrono")]
use chrono::{Offset as _, Timelike as _};
use crate::rows::RowRef;
use crate::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct ExecResult {
pub rows_affected: u64,
pub last_insert_id: Option<u64>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Null,
I64(i64),
U64(u64),
F64(f64),
Date(Date),
Time(Time),
DateTime(DateTime),
DateTimeTz(DateTimeTz),
Uuid([u8; 16]),
Bytes(Vec<u8>),
String(String),
}
impl From<&str> for Value {
fn from(value: &str) -> Self {
Self::String(value.into())
}
}
impl From<String> for Value {
fn from(value: String) -> Self {
Self::String(value)
}
}
impl From<Vec<u8>> for Value {
fn from(value: Vec<u8>) -> Self {
Self::Bytes(value)
}
}
impl From<i64> for Value {
fn from(value: i64) -> Self {
Self::I64(value)
}
}
impl From<u64> for Value {
fn from(value: u64) -> Self {
Self::U64(value)
}
}
impl From<f64> for Value {
fn from(value: f64) -> Self {
Self::F64(value)
}
}
macro_rules! impl_signed_value_from {
($($ty:ty),* $(,)?) => {
$(
impl From<$ty> for Value {
fn from(value: $ty) -> Self {
Self::I64(value as i64)
}
}
)*
};
}
macro_rules! impl_unsigned_value_from {
($($ty:ty),* $(,)?) => {
$(
impl From<$ty> for Value {
fn from(value: $ty) -> Self {
Self::U64(value as u64)
}
}
)*
};
}
impl_signed_value_from!(i8, i16, i32, isize);
impl_unsigned_value_from!(u8, u16, u32, usize);
#[derive(Debug, Clone, PartialEq)]
pub enum ParamValue<'a> {
Null,
I64(i64),
U64(u64),
F64(f64),
Str(Cow<'a, str>),
Bytes(Cow<'a, [u8]>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Date {
pub year: i32,
pub month: u8,
pub day: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Time {
pub hour: u8,
pub minute: u8,
pub second: u8,
pub microsecond: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DateTime {
pub date: Date,
pub time: Time,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DateTimeTz {
pub datetime: DateTime,
pub offset_seconds: i32,
}
pub trait EncodeTarget {
fn encode_param(&mut self, value: ParamValue<'_>);
fn encode_null(&mut self);
fn encode_i64(&mut self, value: i64);
fn encode_u64(&mut self, value: u64);
fn encode_f64(&mut self, value: f64);
fn encode_bool(&mut self, value: bool);
fn encode_date(&mut self, value: Date);
fn encode_time(&mut self, value: Time);
fn encode_datetime(&mut self, value: DateTime);
fn encode_datetime_tz(&mut self, value: DateTimeTz);
fn encode_uuid(&mut self, value: [u8; 16]);
fn encode_str(&mut self, value: &str);
fn encode_string(&mut self, value: String);
fn encode_bytes(&mut self, value: &[u8]);
fn encode_bytes_owned(&mut self, value: Vec<u8>);
}
pub struct Encoder<'a> {
target: &'a mut dyn EncodeTarget,
}
impl<'a> Encoder<'a> {
pub fn new(target: &'a mut dyn EncodeTarget) -> Self {
Self { target }
}
pub fn encode_param(self, value: ParamValue<'_>) {
self.target.encode_param(value);
}
pub fn encode_null(self) {
self.target.encode_null();
}
pub fn encode_i64(self, value: i64) {
self.target.encode_i64(value);
}
pub fn encode_u64(self, value: u64) {
self.target.encode_u64(value);
}
pub fn encode_f64(self, value: f64) {
self.target.encode_f64(value);
}
pub fn encode_bool(self, value: bool) {
self.target.encode_bool(value);
}
pub fn encode_date(self, value: Date) {
self.target.encode_date(value);
}
pub fn encode_time(self, value: Time) {
self.target.encode_time(value);
}
pub fn encode_datetime(self, value: DateTime) {
self.target.encode_datetime(value);
}
pub fn encode_datetime_tz(self, value: DateTimeTz) {
self.target.encode_datetime_tz(value);
}
pub fn encode_uuid(self, value: [u8; 16]) {
self.target.encode_uuid(value);
}
pub fn encode_str(self, value: &str) {
self.target.encode_str(value);
}
pub fn encode_string(self, value: String) {
self.target.encode_string(value);
}
pub fn encode_bytes(self, value: &[u8]) {
self.target.encode_bytes(value);
}
pub fn encode_bytes_owned(self, value: Vec<u8>) {
self.target.encode_bytes_owned(value);
}
}
pub trait Encode {
fn encode(&self, out: Encoder<'_>);
}
pub struct Decoder<'r> {
input: ValueInput<'r>,
}
enum ValueInput<'r> {
Value(&'r Value),
RowColumn { row: &'r RowRef<'r>, index: usize },
}
impl<'r> Decoder<'r> {
pub fn value(value: &'r Value) -> Self {
Self {
input: ValueInput::Value(value),
}
}
pub fn row_column(row: &'r RowRef<'r>, index: usize) -> Self {
Self {
input: ValueInput::RowColumn { row, index },
}
}
pub fn is_null(&self) -> Result<bool> {
match self.input {
ValueInput::Value(value) => Ok(matches!(value, Value::Null)),
ValueInput::RowColumn { row, index } => row.is_null(index),
}
}
pub fn decode_i64(&self) -> Result<i64> {
match self.input {
ValueInput::Value(value) => match value {
Value::I64(value) => Ok(*value),
Value::U64(value) => i64::try_from(*value)
.map_err(|_| Error::Unsupported("u64 value is out of i64 range".into())),
Value::Null => Err(Error::Unsupported("column is null".into())),
other => Err(Error::Unsupported(format!("column is not i64: {other:?}"))),
},
ValueInput::RowColumn { row, index } => row.get_i64(index),
}
}
pub fn decode_u64(&self) -> Result<u64> {
match self.input {
ValueInput::Value(value) => match value {
Value::U64(value) => Ok(*value),
Value::I64(value) if *value >= 0 => Ok(*value as u64),
Value::I64(_) => Err(Error::Unsupported(
"negative i64 value cannot be represented as u64".into(),
)),
Value::Null => Err(Error::Unsupported("column is null".into())),
other => Err(Error::Unsupported(format!("column is not u64: {other:?}"))),
},
ValueInput::RowColumn { row, index } => row.get_u64(index),
}
}
pub fn decode_f64(&self) -> Result<f64> {
match self.input {
ValueInput::Value(value) => match value {
Value::F64(value) => Ok(*value),
Value::I64(value) => Ok(*value as f64),
Value::U64(value) => Ok(*value as f64),
Value::Null => Err(Error::Unsupported("column is null".into())),
other => Err(Error::Unsupported(format!("column is not f64: {other:?}"))),
},
ValueInput::RowColumn { row, index } => row.get_f64(index),
}
}
pub fn decode_bool(&self) -> Result<bool> {
if self.is_null()? {
return Err(Error::Unsupported("column is null".into()));
}
match self.input {
ValueInput::Value(value) => match value {
Value::I64(value) => Ok(bool_from_i64(*value)),
Value::U64(value) => Ok(*value != 0),
Value::String(value) => parse_bool(value),
Value::Bytes(value) => std::str::from_utf8(value)
.map_err(|_| Error::Unsupported("column is not valid utf-8 bool".into()))
.and_then(parse_bool),
Value::Null => Err(Error::Unsupported("column is null".into())),
other => Err(Error::Unsupported(format!("column is not bool: {other:?}"))),
},
ValueInput::RowColumn { row, index } => match row.get_i64(index) {
Ok(value) => Ok(bool_from_i64(value)),
Err(_) => parse_bool(row.get_str(index)?),
},
}
}
pub fn decode_str(&self) -> Result<&'r str> {
match self.input {
ValueInput::Value(value) => match value {
Value::String(value) => Ok(value),
Value::Bytes(value) => std::str::from_utf8(value)
.map_err(|_| Error::Unsupported("column is not valid utf-8 text".into())),
Value::Null => Err(Error::Unsupported("column is null".into())),
other => Err(Error::Unsupported(format!("column is not text: {other:?}"))),
},
ValueInput::RowColumn { row, index } => row.get_str(index),
}
}
pub fn decode_bytes(&self) -> Result<&'r [u8]> {
match self.input {
ValueInput::Value(value) => match value {
Value::Bytes(value) => Ok(value),
Value::String(value) => Ok(value.as_bytes()),
Value::Null => Err(Error::Unsupported("column is null".into())),
other => Err(Error::Unsupported(format!(
"column is not bytes: {other:?}"
))),
},
ValueInput::RowColumn { row, index } => row.get_bytes(index),
}
}
pub fn decode_owned(&self) -> Result<Value> {
match self.input {
ValueInput::Value(value) => Ok(value.clone()),
ValueInput::RowColumn { row, index } => {
let owned = row.to_owned()?;
owned
.values
.get(index)
.cloned()
.ok_or_else(|| Error::Unsupported("column index out of bounds".into()))
}
}
}
}
pub trait Decode: Sized {
fn decode(value: &mut Decoder<'_>) -> Result<Self>;
}
pub(crate) fn decode_value<T>(value: &Value) -> Result<T>
where
T: Decode,
{
let mut value = Decoder::value(value);
T::decode(&mut value)
}
pub(crate) fn decode_row_column<T>(row: &RowRef<'_>, index: usize) -> Result<T>
where
T: Decode,
{
let mut value = Decoder::row_column(row, index);
T::decode(&mut value)
}
pub trait FromRow: Sized {
fn from_row(row: &Row) -> Result<Self>;
fn from_row_ref(row: &RowRef<'_>) -> Result<Self> {
let owned = row.to_owned()?;
Self::from_row(&owned)
}
}
pub trait FromColumnRef<'r>: Sized {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self>;
}
pub trait FromRowRef<'r>: Sized {
fn from_row_ref(row: &'r RowRef<'r>) -> Result<Self>;
}
#[derive(Debug, Clone)]
pub enum ColumnType {
Mysql(u32),
Postgres(u32),
Sqlite(Option<String>),
}
#[derive(Debug, Clone)]
pub struct Column {
pub name: String,
pub nullable: bool,
pub kind: ColumnType,
}
#[derive(Debug, Clone)]
pub struct Row {
pub columns: Vec<Column>,
pub values: Vec<Value>,
}
impl Row {
pub fn decode<T>(&self) -> Result<T>
where
T: FromRow,
{
T::from_row(self)
}
pub fn get<T>(&self, index: impl ColumnIndex) -> Result<T>
where
T: Decode,
{
let idx = index.index(&self.columns)?;
let value = self
.values
.get(idx)
.ok_or_else(|| Error::Unsupported("column index out of bounds".into()))?;
decode_value(value)
}
pub fn get_i64(&self, index: impl ColumnIndex) -> Result<i64> {
match self.values.get(index.index(&self.columns)?) {
Some(Value::I64(value)) => Ok(*value),
Some(Value::U64(value)) => i64::try_from(*value)
.map_err(|_| Error::Unsupported("u64 value is out of i64 range".into())),
Some(Value::Null) => Err(Error::Unsupported("column is null".into())),
Some(other) => Err(Error::Unsupported(format!("column is not i64: {other:?}"))),
None => Err(Error::Unsupported("column index out of bounds".into())),
}
}
pub fn get_u64(&self, index: impl ColumnIndex) -> Result<u64> {
match self.values.get(index.index(&self.columns)?) {
Some(Value::U64(value)) => Ok(*value),
Some(Value::I64(value)) if *value >= 0 => Ok(*value as u64),
Some(Value::I64(_)) => Err(Error::Unsupported(
"negative i64 value cannot be represented as u64".into(),
)),
Some(Value::Null) => Err(Error::Unsupported("column is null".into())),
Some(other) => Err(Error::Unsupported(format!("column is not u64: {other:?}"))),
None => Err(Error::Unsupported("column index out of bounds".into())),
}
}
pub fn get_f64(&self, index: impl ColumnIndex) -> Result<f64> {
match self.values.get(index.index(&self.columns)?) {
Some(Value::F64(value)) => Ok(*value),
Some(Value::I64(value)) => Ok(*value as f64),
Some(Value::U64(value)) => Ok(*value as f64),
Some(Value::Null) => Err(Error::Unsupported("column is null".into())),
Some(other) => Err(Error::Unsupported(format!("column is not f64: {other:?}"))),
None => Err(Error::Unsupported("column index out of bounds".into())),
}
}
pub fn get_str(&self, index: impl ColumnIndex) -> Result<&str> {
match self.values.get(index.index(&self.columns)?) {
Some(Value::String(value)) => Ok(value),
Some(Value::Null) => Err(Error::Unsupported("column is null".into())),
Some(other) => Err(Error::Unsupported(format!("column is not text: {other:?}"))),
None => Err(Error::Unsupported("column index out of bounds".into())),
}
}
pub fn get_bytes(&self, index: impl ColumnIndex) -> Result<&[u8]> {
match self.values.get(index.index(&self.columns)?) {
Some(Value::Bytes(value)) => Ok(value),
Some(Value::String(value)) => Ok(value.as_bytes()),
Some(Value::Uuid(value)) => Ok(value),
Some(Value::Null) => Err(Error::Unsupported("column is null".into())),
Some(other) => Err(Error::Unsupported(format!(
"column is not bytes: {other:?}"
))),
None => Err(Error::Unsupported("column index out of bounds".into())),
}
}
}
pub trait ColumnIndex {
fn index(&self, columns: &[Column]) -> Result<usize>;
}
impl ColumnIndex for usize {
fn index(&self, columns: &[Column]) -> Result<usize> {
if *self < columns.len() {
Ok(*self)
} else {
Err(Error::Unsupported(format!("column {self} out of bounds")))
}
}
}
impl ColumnIndex for &str {
fn index(&self, columns: &[Column]) -> Result<usize> {
columns
.iter()
.position(|column| column.name == *self)
.ok_or_else(|| Error::Unsupported(format!("unknown column {}", self)))
}
}
impl Encode for Value {
fn encode(&self, out: Encoder<'_>) {
match self {
Value::Null => out.encode_null(),
Value::I64(value) => out.encode_i64(*value),
Value::U64(value) => out.encode_u64(*value),
Value::F64(value) => out.encode_f64(*value),
Value::Date(value) => out.encode_date(*value),
Value::Time(value) => out.encode_time(*value),
Value::DateTime(value) => out.encode_datetime(*value),
Value::DateTimeTz(value) => out.encode_datetime_tz(*value),
Value::Uuid(value) => out.encode_uuid(*value),
Value::Bytes(value) => out.encode_bytes(value.as_slice()),
Value::String(value) => out.encode_str(value.as_str()),
}
}
}
macro_rules! impl_signed_to_param {
($($ty:ty),* $(,)?) => {
$(
impl Encode for $ty {
fn encode(&self, out: Encoder<'_>) {
out.encode_i64(*self as i64);
}
}
)*
};
}
macro_rules! impl_unsigned_to_param {
($($ty:ty),* $(,)?) => {
$(
impl Encode for $ty {
fn encode(&self, out: Encoder<'_>) {
out.encode_u64(*self as u64);
}
}
)*
};
}
impl_signed_to_param!(i8, i16, i32, i64, isize);
impl_unsigned_to_param!(u8, u16, u32, u64, usize);
macro_rules! impl_signed_decode {
($($ty:ty),* $(,)?) => {
$(
impl Decode for $ty {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
<$ty>::try_from(value.decode_i64()?)
.map_err(|_| Error::Unsupported(format!("value is out of range for {}", stringify!($ty))))
}
}
)*
};
}
macro_rules! impl_unsigned_decode {
($($ty:ty),* $(,)?) => {
$(
impl Decode for $ty {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
<$ty>::try_from(value.decode_u64()?)
.map_err(|_| Error::Unsupported(format!("value is out of range for {}", stringify!($ty))))
}
}
)*
};
}
impl_signed_decode!(i8, i16, i32, i64, isize);
impl_unsigned_decode!(u8, u16, u32, u64, usize);
impl Encode for f32 {
fn encode(&self, out: Encoder<'_>) {
out.encode_f64(*self as f64);
}
}
impl Encode for f64 {
fn encode(&self, out: Encoder<'_>) {
out.encode_f64(*self);
}
}
impl Encode for str {
fn encode(&self, out: Encoder<'_>) {
out.encode_str(self);
}
}
impl Encode for String {
fn encode(&self, out: Encoder<'_>) {
out.encode_str(self.as_str());
}
}
impl Encode for [u8] {
fn encode(&self, out: Encoder<'_>) {
out.encode_bytes(self);
}
}
impl Encode for Vec<u8> {
fn encode(&self, out: Encoder<'_>) {
out.encode_bytes(self.as_slice());
}
}
impl Encode for bool {
fn encode(&self, out: Encoder<'_>) {
out.encode_bool(*self);
}
}
impl Encode for Date {
fn encode(&self, out: Encoder<'_>) {
out.encode_date(*self);
}
}
impl Encode for Time {
fn encode(&self, out: Encoder<'_>) {
out.encode_time(*self);
}
}
impl Encode for DateTime {
fn encode(&self, out: Encoder<'_>) {
out.encode_datetime(*self);
}
}
impl Encode for DateTimeTz {
fn encode(&self, out: Encoder<'_>) {
out.encode_datetime_tz(*self);
}
}
#[cfg(feature = "json")]
impl Encode for serde_json::Value {
fn encode(&self, out: Encoder<'_>) {
out.encode_string(self.to_string());
}
}
#[cfg(feature = "uuid")]
impl Encode for uuid::Uuid {
fn encode(&self, out: Encoder<'_>) {
out.encode_uuid(*self.as_bytes());
}
}
impl Encode for ParamValue<'_> {
fn encode(&self, out: Encoder<'_>) {
match self {
ParamValue::Null => out.encode_null(),
ParamValue::I64(value) => out.encode_i64(*value),
ParamValue::U64(value) => out.encode_u64(*value),
ParamValue::F64(value) => out.encode_f64(*value),
ParamValue::Str(value) => out.encode_str(value.as_ref()),
ParamValue::Bytes(value) => out.encode_bytes(value.as_ref()),
}
}
}
impl<T> Encode for Option<T>
where
T: Encode,
{
fn encode(&self, out: Encoder<'_>) {
match self {
Some(value) => value.encode(out),
None => out.encode_null(),
}
}
}
impl Decode for Value {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
value.decode_owned()
}
}
impl Decode for String {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
Ok(value.decode_str()?.to_owned())
}
}
impl<T> Decode for Option<T>
where
T: Decode,
{
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
if value.is_null()? {
Ok(None)
} else {
T::decode(value).map(Some)
}
}
}
impl Decode for Vec<u8> {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
Ok(value.decode_bytes()?.to_vec())
}
}
impl Decode for f64 {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
value.decode_f64()
}
}
impl Decode for bool {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
value.decode_bool()
}
}
impl Decode for Date {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
if let Ok(Value::Date(value)) = value.decode_owned() {
return Ok(value);
}
decode_textual(value, parse_date, "date")
}
}
impl Decode for Time {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
if let Ok(Value::Time(value)) = value.decode_owned() {
return Ok(value);
}
decode_textual(value, parse_time, "time")
}
}
impl Decode for DateTime {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
if let Ok(Value::DateTime(value)) = value.decode_owned() {
return Ok(value);
}
decode_textual(value, parse_datetime, "datetime")
}
}
impl Decode for DateTimeTz {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
if let Ok(Value::DateTimeTz(value)) = value.decode_owned() {
return Ok(value);
}
decode_textual(value, parse_datetime_tz, "datetime with offset")
}
}
#[cfg(feature = "json")]
impl Decode for serde_json::Value {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
serde_json::from_str(value.decode_str()?).map_err(|err| Error::Unsupported(err.to_string()))
}
}
#[cfg(feature = "uuid")]
impl Decode for uuid::Uuid {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
if let Ok(Value::Uuid(bytes)) = value.decode_owned() {
return Ok(uuid::Uuid::from_bytes(bytes));
}
if let Ok(bytes) = value.decode_bytes() {
if let Ok(raw) = <[u8; 16]>::try_from(bytes) {
return Ok(uuid::Uuid::from_bytes(raw));
}
if let Ok(text) = std::str::from_utf8(bytes) {
return uuid::Uuid::parse_str(text)
.map_err(|err| Error::Unsupported(err.to_string()));
}
}
uuid::Uuid::parse_str(value.decode_str()?)
.map_err(|err| Error::Unsupported(err.to_string()))
}
}
fn bool_from_i64(value: i64) -> bool {
value != 0
}
fn parse_bool(value: &str) -> Result<bool> {
match value {
"1" => Ok(true),
"0" => Ok(false),
_ if value.eq_ignore_ascii_case("true") || value.eq_ignore_ascii_case("t") => Ok(true),
_ if value.eq_ignore_ascii_case("false") || value.eq_ignore_ascii_case("f") => Ok(false),
_ => Err(Error::Unsupported(format!("column is not bool: {value:?}"))),
}
}
fn decode_textual<T>(
value: &mut Decoder<'_>,
parse: impl Fn(&str) -> Option<T>,
kind: &'static str,
) -> Result<T> {
if let Ok(bytes) = value.decode_bytes() {
if let Ok(text) = std::str::from_utf8(bytes) {
if let Some(parsed) = parse(text) {
return Ok(parsed);
}
}
}
let text = value.decode_str()?;
parse(text).ok_or_else(|| Error::Unsupported(format!("column is not {kind}")))
}
pub(crate) fn parse_date(text: &str) -> Option<Date> {
let [year, month, day] = text.split('-').collect::<Vec<_>>().try_into().ok()?;
Some(Date {
year: year.parse().ok()?,
month: month.parse().ok()?,
day: day.parse().ok()?,
})
}
pub(crate) fn parse_time(text: &str) -> Option<Time> {
let (time, microsecond) = match text.split_once('.') {
Some((time, fraction)) => {
let digits = fraction.as_bytes();
if digits.is_empty() || digits.len() > 9 || !digits.iter().all(u8::is_ascii_digit) {
return None;
}
let fraction = if digits.len() > 6 {
&fraction[..6]
} else {
fraction
};
let mut micros = fraction.parse::<u32>().ok()?;
for _ in fraction.len()..6 {
micros *= 10;
}
(time, micros)
}
None => (text, 0),
};
let [hour, minute, second] = time.split(':').collect::<Vec<_>>().try_into().ok()?;
Some(Time {
hour: hour.parse().ok()?,
minute: minute.parse().ok()?,
second: second.parse().ok()?,
microsecond,
})
}
pub(crate) fn parse_datetime(text: &str) -> Option<DateTime> {
let (date, time) = text.split_once(' ').or_else(|| text.split_once('T'))?;
Some(DateTime {
date: parse_date(date)?,
time: parse_time(time)?,
})
}
pub(crate) fn parse_datetime_tz(text: &str) -> Option<DateTimeTz> {
let (datetime, offset_seconds) = parse_offset_datetime_parts(text)?;
Some(DateTimeTz {
datetime: parse_datetime(datetime)?,
offset_seconds,
})
}
fn parse_offset_datetime_parts(text: &str) -> Option<(&str, i32)> {
if let Some(datetime) = text.strip_suffix('Z') {
return Some((datetime, 0));
}
let split = text
.char_indices()
.rev()
.find(|(_, ch)| matches!(ch, '+' | '-'))?
.0;
let (datetime, offset) = text.split_at(split);
let sign = if offset.starts_with('-') { -1 } else { 1 };
let offset = &offset[1..];
let (hours, minutes) = offset.split_once(':')?;
let total = hours.parse::<i32>().ok()? * 3600 + minutes.parse::<i32>().ok()? * 60;
Some((datetime, sign * total))
}
#[cfg(feature = "chrono")]
impl Encode for chrono::NaiveDate {
fn encode(&self, out: Encoder<'_>) {
out.encode_date(Date {
year: chrono::Datelike::year(self),
month: chrono::Datelike::month(self) as u8,
day: chrono::Datelike::day(self) as u8,
});
}
}
#[cfg(feature = "chrono")]
impl Encode for chrono::NaiveTime {
fn encode(&self, out: Encoder<'_>) {
out.encode_time(Time {
hour: chrono::Timelike::hour(self) as u8,
minute: chrono::Timelike::minute(self) as u8,
second: chrono::Timelike::second(self) as u8,
microsecond: self.nanosecond() / 1_000,
});
}
}
#[cfg(feature = "chrono")]
impl Encode for chrono::NaiveDateTime {
fn encode(&self, out: Encoder<'_>) {
out.encode_datetime(DateTime {
date: Date {
year: chrono::Datelike::year(self),
month: chrono::Datelike::month(self) as u8,
day: chrono::Datelike::day(self) as u8,
},
time: Time {
hour: chrono::Timelike::hour(self) as u8,
minute: chrono::Timelike::minute(self) as u8,
second: chrono::Timelike::second(self) as u8,
microsecond: self.nanosecond() / 1_000,
},
});
}
}
#[cfg(feature = "chrono")]
impl<Tz> Encode for chrono::DateTime<Tz>
where
Tz: chrono::TimeZone,
Tz::Offset: ::core::fmt::Display,
{
fn encode(&self, out: Encoder<'_>) {
let local = self.naive_local();
out.encode_datetime_tz(DateTimeTz {
datetime: DateTime {
date: Date {
year: chrono::Datelike::year(&local),
month: chrono::Datelike::month(&local) as u8,
day: chrono::Datelike::day(&local) as u8,
},
time: Time {
hour: chrono::Timelike::hour(&local) as u8,
minute: chrono::Timelike::minute(&local) as u8,
second: chrono::Timelike::second(&local) as u8,
microsecond: local.nanosecond() / 1_000,
},
},
offset_seconds: self.offset().fix().local_minus_utc(),
});
}
}
#[cfg(feature = "chrono")]
impl Decode for chrono::NaiveDate {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
let date = Date::decode(value)?;
chrono::NaiveDate::from_ymd_opt(date.year, date.month.into(), date.day.into())
.ok_or_else(|| Error::Unsupported("invalid date".into()))
}
}
#[cfg(feature = "chrono")]
impl Decode for chrono::NaiveTime {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
let time = Time::decode(value)?;
chrono::NaiveTime::from_hms_micro_opt(
time.hour.into(),
time.minute.into(),
time.second.into(),
time.microsecond,
)
.ok_or_else(|| Error::Unsupported("invalid time".into()))
}
}
#[cfg(feature = "chrono")]
impl Decode for chrono::NaiveDateTime {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
if let Ok(Value::DateTimeTz(value)) = value.decode_owned() {
let date = chrono::NaiveDate::from_ymd_opt(
value.datetime.date.year,
value.datetime.date.month.into(),
value.datetime.date.day.into(),
)
.ok_or_else(|| Error::Unsupported("invalid date".into()))?;
let time = chrono::NaiveTime::from_hms_micro_opt(
value.datetime.time.hour.into(),
value.datetime.time.minute.into(),
value.datetime.time.second.into(),
value.datetime.time.microsecond,
)
.ok_or_else(|| Error::Unsupported("invalid time".into()))?;
return Ok(chrono::NaiveDateTime::new(date, time));
}
let datetime = DateTime::decode(value)?;
let date = chrono::NaiveDate::from_ymd_opt(
datetime.date.year,
datetime.date.month.into(),
datetime.date.day.into(),
)
.ok_or_else(|| Error::Unsupported("invalid date".into()))?;
let time = chrono::NaiveTime::from_hms_micro_opt(
datetime.time.hour.into(),
datetime.time.minute.into(),
datetime.time.second.into(),
datetime.time.microsecond,
)
.ok_or_else(|| Error::Unsupported("invalid time".into()))?;
Ok(chrono::NaiveDateTime::new(date, time))
}
}
#[cfg(feature = "chrono")]
impl Decode for chrono::DateTime<chrono::Utc> {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
let value = chrono::DateTime::<chrono::FixedOffset>::decode(value)?;
Ok(value.with_timezone(&chrono::Utc))
}
}
#[cfg(feature = "chrono")]
impl Decode for chrono::DateTime<chrono::FixedOffset> {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
if let Ok(Value::DateTime(value)) = value.decode_owned() {
let date = chrono::NaiveDate::from_ymd_opt(
value.date.year,
value.date.month.into(),
value.date.day.into(),
)
.ok_or_else(|| Error::Unsupported("invalid date".into()))?;
let time = chrono::NaiveTime::from_hms_micro_opt(
value.time.hour.into(),
value.time.minute.into(),
value.time.second.into(),
value.time.microsecond,
)
.ok_or_else(|| Error::Unsupported("invalid time".into()))?;
let offset = chrono::FixedOffset::east_opt(0)
.ok_or_else(|| Error::Unsupported("invalid datetime offset".into()))?;
return Ok(chrono::DateTime::from_naive_utc_and_offset(
chrono::NaiveDateTime::new(date, time),
offset,
));
}
let datetime = DateTimeTz::decode(value)?;
let date = chrono::NaiveDate::from_ymd_opt(
datetime.datetime.date.year,
datetime.datetime.date.month.into(),
datetime.datetime.date.day.into(),
)
.ok_or_else(|| Error::Unsupported("invalid date".into()))?;
let time = chrono::NaiveTime::from_hms_micro_opt(
datetime.datetime.time.hour.into(),
datetime.datetime.time.minute.into(),
datetime.datetime.time.second.into(),
datetime.datetime.time.microsecond,
)
.ok_or_else(|| Error::Unsupported("invalid time".into()))?;
let offset = chrono::FixedOffset::east_opt(datetime.offset_seconds)
.ok_or_else(|| Error::Unsupported("invalid datetime offset".into()))?;
Ok(chrono::DateTime::from_naive_utc_and_offset(
chrono::NaiveDateTime::new(date, time)
- chrono::TimeDelta::seconds(i64::from(datetime.offset_seconds)),
offset,
))
}
}
#[cfg(feature = "time")]
impl Encode for time::Date {
fn encode(&self, out: Encoder<'_>) {
out.encode_date(Date {
year: self.year(),
month: u8::from(self.month()),
day: self.day(),
});
}
}
#[cfg(feature = "time")]
impl Encode for time::Time {
fn encode(&self, out: Encoder<'_>) {
out.encode_time(Time {
hour: self.hour(),
minute: self.minute(),
second: self.second(),
microsecond: self.microsecond(),
});
}
}
#[cfg(feature = "time")]
impl Encode for time::PrimitiveDateTime {
fn encode(&self, out: Encoder<'_>) {
out.encode_datetime(DateTime {
date: Date {
year: self.year(),
month: u8::from(self.month()),
day: self.day(),
},
time: Time {
hour: self.hour(),
minute: self.minute(),
second: self.second(),
microsecond: self.microsecond(),
},
});
}
}
#[cfg(feature = "time")]
impl Encode for time::OffsetDateTime {
fn encode(&self, out: Encoder<'_>) {
out.encode_datetime_tz(DateTimeTz {
datetime: DateTime {
date: Date {
year: self.year(),
month: u8::from(self.month()),
day: self.day(),
},
time: Time {
hour: self.hour(),
minute: self.minute(),
second: self.second(),
microsecond: self.microsecond(),
},
},
offset_seconds: self.offset().whole_seconds(),
});
}
}
#[cfg(feature = "time")]
impl Decode for time::Date {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
let date = Date::decode(value)?;
let month =
time::Month::try_from(date.month).map_err(|err| Error::Unsupported(err.to_string()))?;
time::Date::from_calendar_date(date.year, month, date.day)
.map_err(|err| Error::Unsupported(err.to_string()))
}
}
#[cfg(feature = "time")]
impl Decode for time::Time {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
let time = Time::decode(value)?;
time::Time::from_hms_micro(time.hour, time.minute, time.second, time.microsecond)
.map_err(|err| Error::Unsupported(err.to_string()))
}
}
#[cfg(feature = "time")]
impl Decode for time::PrimitiveDateTime {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
let datetime = DateTime::decode(value)?;
Ok(time::PrimitiveDateTime::new(
time::Date::decode(&mut Decoder::value(&Value::Date(datetime.date)))?,
time::Time::decode(&mut Decoder::value(&Value::Time(datetime.time)))?,
))
}
}
#[cfg(feature = "time")]
impl Decode for time::OffsetDateTime {
fn decode(value: &mut Decoder<'_>) -> Result<Self> {
let datetime = DateTimeTz::decode(value)?;
let date = time::Date::from_calendar_date(
datetime.datetime.date.year,
time::Month::try_from(datetime.datetime.date.month)
.map_err(|err| Error::Unsupported(err.to_string()))?,
datetime.datetime.date.day,
)
.map_err(|err| Error::Unsupported(err.to_string()))?;
let time = time::Time::from_hms_micro(
datetime.datetime.time.hour,
datetime.datetime.time.minute,
datetime.datetime.time.second,
datetime.datetime.time.microsecond,
)
.map_err(|err| Error::Unsupported(err.to_string()))?;
let offset = time::UtcOffset::from_whole_seconds(datetime.offset_seconds)
.map_err(|err| Error::Unsupported(err.to_string()))?;
Ok(time::PrimitiveDateTime::new(date, time).assume_offset(offset))
}
}
macro_rules! impl_scalar_from_row {
($($ty:ty),* $(,)?) => {
$(
impl FromRow for $ty {
fn from_row(row: &Row) -> Result<Self> {
if row.values.len() != 1 {
return Err(Error::Unsupported(format!(
"expected one column for scalar row, got {}",
row.values.len()
)));
}
row.get(0)
}
fn from_row_ref(row: &RowRef<'_>) -> Result<Self> {
if row.columns().len() != 1 {
return Err(Error::Unsupported(format!(
"expected one column for scalar row, got {}",
row.columns().len()
)));
}
decode_row_column::<$ty>(row, 0)
}
}
)*
};
}
impl_scalar_from_row!(
Value,
String,
Vec<u8>,
i64,
u64,
f64,
bool,
Date,
Time,
DateTime,
DateTimeTz
);
#[cfg(feature = "json")]
impl_scalar_from_row!(serde_json::Value);
#[cfg(feature = "uuid")]
impl_scalar_from_row!(uuid::Uuid);
#[cfg(feature = "chrono")]
impl_scalar_from_row!(
chrono::NaiveDate,
chrono::NaiveTime,
chrono::NaiveDateTime,
chrono::DateTime<chrono::Utc>,
chrono::DateTime<chrono::FixedOffset>,
);
#[cfg(feature = "time")]
impl_scalar_from_row!(
time::Date,
time::Time,
time::PrimitiveDateTime,
time::OffsetDateTime,
);
macro_rules! impl_tuple_from_row {
($len:expr, $($idx:tt => $name:ident),+ $(,)?) => {
impl<$($name),+> FromRow for ($($name,)+)
where
$($name: Decode,)+
{
fn from_row(row: &Row) -> Result<Self> {
if row.values.len() != $len {
return Err(Error::Unsupported(format!(
"expected {} columns for tuple row, got {}",
$len,
row.values.len()
)));
}
Ok(($(row.get::<$name>($idx)?,)+))
}
fn from_row_ref(row: &RowRef<'_>) -> Result<Self> {
if row.columns().len() != $len {
return Err(Error::Unsupported(format!(
"expected {} columns for tuple row, got {}",
$len,
row.columns().len()
)));
}
Ok(($(decode_row_column::<$name>(row, $idx)?,)+))
}
}
};
}
impl_tuple_from_row!(1, 0 => A);
impl_tuple_from_row!(2, 0 => A, 1 => B);
impl_tuple_from_row!(3, 0 => A, 1 => B, 2 => C);
impl_tuple_from_row!(4, 0 => A, 1 => B, 2 => C, 3 => D);
impl_tuple_from_row!(5, 0 => A, 1 => B, 2 => C, 3 => D, 4 => E);
impl_tuple_from_row!(6, 0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F);
impl_tuple_from_row!(7, 0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G);
impl_tuple_from_row!(8, 0 => A, 1 => B, 2 => C, 3 => D, 4 => E, 5 => F, 6 => G, 7 => H);
impl<'r> FromColumnRef<'r> for i64 {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
row.get_i64(index)
}
}
impl<'r> FromColumnRef<'r> for u64 {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
row.get_u64(index)
}
}
impl<'r> FromColumnRef<'r> for f64 {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
row.get_f64(index)
}
}
impl<'r> FromColumnRef<'r> for bool {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
if row.is_null(index)? {
return Err(Error::Unsupported("column is null".into()));
}
match row.get_i64(index) {
Ok(value) => Ok(bool_from_i64(value)),
Err(_) => parse_bool(row.get_str(index)?),
}
}
}
impl<'r> FromColumnRef<'r> for String {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
Ok(row.get_str(index)?.to_owned())
}
}
impl<'r> FromColumnRef<'r> for Vec<u8> {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
Ok(row.get_bytes(index)?.to_vec())
}
}
impl<'r> FromColumnRef<'r> for Value {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
Ok(row.to_owned()?.values[index].clone())
}
}
impl<'r> FromColumnRef<'r> for Date {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
row.get_date(index)
}
}
impl<'r> FromColumnRef<'r> for Time {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
row.get_time_value(index)
}
}
impl<'r> FromColumnRef<'r> for DateTime {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
row.get_datetime(index)
}
}
impl<'r> FromColumnRef<'r> for DateTimeTz {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
row.get_datetimetz(index)
}
}
#[cfg(feature = "uuid")]
impl<'r> FromColumnRef<'r> for uuid::Uuid {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
match row.get_uuid_bytes(index) {
Ok(bytes) => Ok(uuid::Uuid::from_bytes(bytes)),
Err(_) => uuid::Uuid::parse_str(row.get_str(index)?)
.map_err(|err| Error::Unsupported(err.to_string())),
}
}
}
#[cfg(feature = "chrono")]
impl<'r> FromColumnRef<'r> for chrono::NaiveDate {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
chrono::NaiveDate::decode(&mut Decoder::row_column(row, index))
}
}
#[cfg(feature = "chrono")]
impl<'r> FromColumnRef<'r> for chrono::NaiveTime {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
chrono::NaiveTime::decode(&mut Decoder::row_column(row, index))
}
}
#[cfg(feature = "chrono")]
impl<'r> FromColumnRef<'r> for chrono::NaiveDateTime {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
chrono::NaiveDateTime::decode(&mut Decoder::row_column(row, index))
}
}
#[cfg(feature = "chrono")]
impl<'r> FromColumnRef<'r> for chrono::DateTime<chrono::Utc> {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
chrono::DateTime::<chrono::Utc>::decode(&mut Decoder::row_column(row, index))
}
}
#[cfg(feature = "chrono")]
impl<'r> FromColumnRef<'r> for chrono::DateTime<chrono::FixedOffset> {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
chrono::DateTime::<chrono::FixedOffset>::decode(&mut Decoder::row_column(row, index))
}
}
#[cfg(feature = "time")]
impl<'r> FromColumnRef<'r> for time::Date {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
time::Date::decode(&mut Decoder::row_column(row, index))
}
}
#[cfg(feature = "time")]
impl<'r> FromColumnRef<'r> for time::Time {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
time::Time::decode(&mut Decoder::row_column(row, index))
}
}
#[cfg(feature = "time")]
impl<'r> FromColumnRef<'r> for time::PrimitiveDateTime {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
time::PrimitiveDateTime::decode(&mut Decoder::row_column(row, index))
}
}
#[cfg(feature = "time")]
impl<'r> FromColumnRef<'r> for time::OffsetDateTime {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
time::OffsetDateTime::decode(&mut Decoder::row_column(row, index))
}
}
impl<'r> FromColumnRef<'r> for &'r str {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
row.get_str(index)
}
}
impl<'r> FromColumnRef<'r> for &'r [u8] {
fn from_column_ref(row: &'r RowRef<'r>, index: usize) -> Result<Self> {
row.get_bytes(index)
}
}