use std::borrow::Cow;
use quex::{
Date as QuexDate, DateTime as QuexDateTime, DateTimeTz as QuexDateTimeTz, Encode,
EncodeTarget, Encoder, Time as QuexTime,
};
use crate::{
lower::Data,
span::{Span, TextSource, TextSpan},
};
#[derive(Debug, Clone, Copy)]
pub enum Param {
Null,
Bool(Option<bool>),
Float(Option<f32>),
Double(Option<f64>),
Int(Option<i32>),
BigInt(Option<i64>),
UInt(Option<u32>),
UBigInt(Option<u64>),
Text(Option<TextSpan>),
Blob(Option<Span>),
}
pub fn encode_param(value: &(impl Encode + ?Sized), data: &mut Vec<u8>) -> Param {
struct ParamEncoder<'a> {
data: &'a mut Vec<u8>,
param: Option<Param>,
}
impl ParamEncoder<'_> {
fn set(&mut self, param: Param) {
assert!(
self.param.is_none(),
"Encode must write exactly one SQL parameter"
);
self.param = Some(param);
}
}
impl EncodeTarget for ParamEncoder<'_> {
fn encode_param(&mut self, value: quex::ParamValue<'_>) {
match value {
quex::ParamValue::Null => self.encode_null(),
quex::ParamValue::I64(value) => self.encode_i64(value),
quex::ParamValue::U64(value) => self.encode_u64(value),
quex::ParamValue::F64(value) => self.encode_f64(value),
quex::ParamValue::Str(value) => self.encode_str(value.as_ref()),
quex::ParamValue::Bytes(value) => self.encode_bytes(value.as_ref()),
}
}
fn encode_null(&mut self) {
self.set(Param::Null);
}
fn encode_i64(&mut self, value: i64) {
self.set(Param::BigInt(Some(value)));
}
fn encode_u64(&mut self, value: u64) {
self.set(Param::UBigInt(Some(value)));
}
fn encode_f64(&mut self, value: f64) {
self.set(Param::Double(Some(value)));
}
fn encode_bool(&mut self, value: bool) {
self.set(Param::Bool(Some(value)));
}
fn encode_date(&mut self, value: QuexDate) {
let encoded = format!("{:04}-{:02}-{:02}", value.year, value.month, value.day);
self.encode_string(encoded);
}
fn encode_time(&mut self, value: QuexTime) {
let mut encoded = format!("{:02}:{:02}:{:02}", value.hour, value.minute, value.second);
if value.microsecond != 0 {
let mut micros = format!("{:06}", value.microsecond);
while micros.ends_with('0') {
micros.pop();
}
encoded.push('.');
encoded.push_str(µs);
}
self.encode_string(encoded);
}
fn encode_datetime(&mut self, value: QuexDateTime) {
let mut encoded = format!(
"{:04}-{:02}-{:02} {:02}:{:02}:{:02}",
value.date.year,
value.date.month,
value.date.day,
value.time.hour,
value.time.minute,
value.time.second
);
if value.time.microsecond != 0 {
let mut micros = format!("{:06}", value.time.microsecond);
while micros.ends_with('0') {
micros.pop();
}
encoded.push('.');
encoded.push_str(µs);
}
self.encode_string(encoded);
}
fn encode_datetime_tz(&mut self, value: QuexDateTimeTz) {
let sign = if value.offset_seconds < 0 { '-' } else { '+' };
let offset_seconds = value.offset_seconds.abs();
let hours = offset_seconds / 3600;
let minutes = (offset_seconds % 3600) / 60;
let mut encoded = format!(
"{:04}-{:02}-{:02}T{:02}:{:02}:{:02}",
value.datetime.date.year,
value.datetime.date.month,
value.datetime.date.day,
value.datetime.time.hour,
value.datetime.time.minute,
value.datetime.time.second
);
if value.datetime.time.microsecond != 0 {
let mut micros = format!("{:06}", value.datetime.time.microsecond);
while micros.ends_with('0') {
micros.pop();
}
encoded.push('.');
encoded.push_str(µs);
}
encoded.push(sign);
encoded.push_str(&format!("{hours:02}:{minutes:02}"));
self.encode_string(encoded);
}
fn encode_uuid(&mut self, value: [u8; 16]) {
#[cfg(feature = "uuid")]
let encoded = uuid::Uuid::from_bytes(value).hyphenated().to_string();
#[cfg(not(feature = "uuid"))]
let encoded = {
let mut encoded = String::with_capacity(32);
for byte in value {
use std::fmt::Write as _;
let _ = write!(&mut encoded, "{byte:02x}");
}
encoded
};
self.encode_string(encoded);
}
fn encode_str(&mut self, value: &str) {
let span = self.data.intern_text(value);
self.set(Param::Text(Some(span)));
}
fn encode_string(&mut self, value: String) {
let span = self.data.intern_text(&value);
self.set(Param::Text(Some(span)));
}
fn encode_bytes(&mut self, value: &[u8]) {
let span = self.data.intern_blob(value);
self.set(Param::Blob(Some(span)));
}
fn encode_bytes_owned(&mut self, value: Vec<u8>) {
let span = self.data.intern_blob(&value);
self.set(Param::Blob(Some(span)));
}
}
let mut encoder = ParamEncoder { data, param: None };
value.encode(Encoder::new(&mut encoder));
encoder
.param
.expect("Encode implementations must write exactly one SQL parameter")
}
impl Param {
pub fn into_param_value<'a>(self, data: &'a Vec<u8>) -> ParamValue<'a> {
match self {
Param::Null => ParamValue::Text(None),
Param::Bool(b) => ParamValue::Bool(b),
Param::Float(f) => ParamValue::Float(f),
Param::Double(d) => ParamValue::Double(d),
Param::Int(i) => ParamValue::Int(i),
Param::BigInt(b) => ParamValue::BigInt(b),
Param::Text(s) => ParamValue::Text(match s {
Some(span) => match span.0 {
TextSource::StaticText(s) => Some(Cow::Borrowed(s)),
TextSource::Text(_) => Some(Cow::Borrowed(data.text(span))),
},
None => None,
}),
Param::Blob(s) => ParamValue::Blob(match s {
Some(s) => Some(Cow::Borrowed(data.blob(s))),
None => None,
}),
Param::UInt(value) => ParamValue::UInt(value),
Param::UBigInt(value) => ParamValue::UBigInt(value),
}
}
}
#[derive(Debug, Clone)]
pub enum ParamValue<'a> {
Bool(Option<bool>),
Float(Option<f32>),
Double(Option<f64>),
Int(Option<i32>),
UInt(Option<u32>),
UBigInt(Option<u64>),
BigInt(Option<i64>),
Text(Option<Cow<'a, str>>),
Blob(Option<Cow<'a, [u8]>>),
}
macro_rules! from_param_value {
($sql:ty, $t:ty) => {
paste::paste! {
impl<'v> From<$t> for ParamValue<'v> {
fn from(value: $t) -> Self {
ParamValue::$sql(Some(value))
}
}
impl<'v> From<Option<$t>> for ParamValue<'v> {
fn from(value: Option<$t>) -> Self {
ParamValue::$sql(value)
}
}
}
};
}
from_param_value!(Bool, bool);
from_param_value!(Float, f32);
from_param_value!(Double, f64);
from_param_value!(Int, i32);
from_param_value!(BigInt, i64);
from_param_value!(UInt, u32);
from_param_value!(UBigInt, u64);
impl<'v> From<&'v str> for ParamValue<'v> {
fn from(value: &'v str) -> Self {
ParamValue::Text(Some(Cow::Borrowed(value)))
}
}
impl<'v> From<Option<&'v str>> for ParamValue<'v> {
fn from(value: Option<&'v str>) -> Self {
ParamValue::Text(value.map(Cow::Borrowed))
}
}
impl<'v> From<&'v [u8]> for ParamValue<'v> {
fn from(value: &'v [u8]) -> Self {
ParamValue::Blob(Some(Cow::Borrowed(value)))
}
}
impl<'v> From<Option<&'v [u8]>> for ParamValue<'v> {
fn from(value: Option<&'v [u8]>) -> Self {
ParamValue::Blob(value.map(Cow::Borrowed))
}
}
impl<'v> From<Cow<'v, str>> for ParamValue<'v> {
fn from(value: Cow<'v, str>) -> Self {
ParamValue::Text(Some(value))
}
}
impl<'v> From<Option<Cow<'v, str>>> for ParamValue<'v> {
fn from(value: Option<Cow<'v, str>>) -> Self {
ParamValue::Text(value)
}
}
impl<'v> From<Cow<'v, [u8]>> for ParamValue<'v> {
fn from(value: Cow<'v, [u8]>) -> Self {
ParamValue::Blob(Some(value))
}
}
impl<'v> From<Option<Cow<'v, [u8]>>> for ParamValue<'v> {
fn from(value: Option<Cow<'v, [u8]>>) -> Self {
ParamValue::Blob(value)
}
}
impl<'a> ParamValue<'a> {
pub fn new<T>(value: T) -> Self
where
T: Into<ParamValue<'a>>,
{
value.into()
}
pub fn into_param(self, data: &mut Vec<u8>) -> Param {
match self {
ParamValue::Bool(value) => Param::Bool(value),
ParamValue::Float(value) => Param::Float(value),
ParamValue::Double(value) => Param::Double(value),
ParamValue::Int(value) => Param::Int(value),
ParamValue::BigInt(value) => Param::BigInt(value),
ParamValue::Text(text) => Param::Text(text.map(|t| data.intern_text(t.as_ref()))),
ParamValue::Blob(blob) => Param::Blob(blob.map(|b| data.intern_blob(b.as_ref()))),
ParamValue::UInt(v) => Param::UInt(v),
ParamValue::UBigInt(v) => Param::UBigInt(v),
}
}
}