use std::{
io::{self, Write},
iter::FromIterator,
num::{
NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU16, NonZeroU32,
NonZeroU64, NonZeroU8, NonZeroUsize,
},
};
#[derive(Debug, PartialEq, Clone)]
pub struct Query {
buf: Vec<u8>,
param_cnt: usize,
q_window: usize,
}
impl From<String> for Query {
fn from(q: String) -> Self {
Self::new_string(q)
}
}
impl<'a> From<&'a str> for Query {
fn from(q: &'a str) -> Self {
Self::new(q)
}
}
impl Query {
pub fn new(query: &str) -> Self {
Self::_new(query.to_owned())
}
pub fn new_string(query: String) -> Self {
Self::_new(query)
}
fn _new(query: String) -> Self {
let l = query.len();
Self {
buf: query.into_bytes(),
param_cnt: 0,
q_window: l,
}
}
pub fn query_str(&self) -> &str {
unsafe { core::str::from_utf8_unchecked(&self.buf[..self.q_window]) }
}
pub fn push_param(&mut self, param: impl SQParam) -> &mut Self {
self.param_cnt += param.append_param(&mut self.buf);
self
}
pub fn param_cnt(&self) -> usize {
self.param_cnt
}
#[inline(always)]
pub(crate) fn write_packet(&self, buf: &mut impl Write) -> io::Result<()> {
let mut query_window_buffer = itoa::Buffer::new();
let query_window_str = query_window_buffer.format(self.q_window);
let total_packet_size = query_window_str.len() + 1 + self.buf.len();
let mut total_packet_size_buffer = itoa::Buffer::new();
let total_packet_size_str = total_packet_size_buffer.format(total_packet_size);
buf.write_all(b"S")?;
buf.write_all(total_packet_size_str.as_bytes())?;
buf.write_all(b"\n")?;
buf.write_all(query_window_str.as_bytes())?;
buf.write_all(b"\n")?;
buf.write_all(&self.buf)?;
Ok(())
}
#[inline(always)]
pub fn debug_encode_packet(&self) -> Vec<u8> {
let mut v = vec![];
self.write_packet(&mut v).unwrap();
v
}
}
pub struct Pipeline {
cnt: usize,
buf: Vec<u8>,
}
impl Pipeline {
pub const fn new() -> Self {
Self {
cnt: 0,
buf: Vec::new(),
}
}
pub(crate) fn buf(&self) -> &[u8] {
&self.buf
}
pub fn query_count(&self) -> usize {
self.cnt
}
pub fn push_owned(&mut self, q: Query) {
self.push(&q);
}
pub fn push(&mut self, q: &Query) {
self.buf
.extend(itoa::Buffer::new().format(q.q_window).as_bytes());
self.buf.push(b'\n');
self.buf.extend(
itoa::Buffer::new()
.format(q.buf.len() - q.q_window)
.as_bytes(),
);
self.buf.push(b'\n');
self.buf.extend(&q.buf);
self.cnt += 1;
}
pub fn add(mut self, q: &Query) -> Self {
self.push(q);
self
}
}
impl<Q: AsRef<Query>, I> From<I> for Pipeline
where
I: Iterator<Item = Q>,
{
fn from(iter: I) -> Self {
let mut pipeline = Pipeline::new();
iter.into_iter().for_each(|q| pipeline.push(q.as_ref()));
pipeline
}
}
impl<Q: AsRef<Query>> Extend<Q> for Pipeline {
fn extend<T: IntoIterator<Item = Q>>(&mut self, iter: T) {
iter.into_iter().for_each(|q| self.push(q.as_ref()))
}
}
impl<Q: AsRef<Query>> FromIterator<Q> for Pipeline {
fn from_iter<T: IntoIterator<Item = Q>>(iter: T) -> Self {
let mut pipe = Pipeline::new();
iter.into_iter().for_each(|q| pipe.push(q.as_ref()));
pipe
}
}
impl AsRef<Query> for Query {
fn as_ref(&self) -> &Query {
self
}
}
pub trait SQParam {
fn append_param(&self, q: &mut Vec<u8>) -> usize;
}
impl<T> SQParam for Option<T>
where
T: SQParam,
{
fn append_param(&self, buf: &mut Vec<u8>) -> usize {
match self {
None => {
buf.push(0);
1
}
Some(e) => e.append_param(buf),
}
}
}
impl<'a, T: SQParam> SQParam for &'a T {
fn append_param(&self, q: &mut Vec<u8>) -> usize {
T::append_param(self, q)
}
}
pub struct Null;
impl SQParam for Null {
fn append_param(&self, buf: &mut Vec<u8>) -> usize {
buf.push(0);
1
}
}
impl SQParam for bool {
fn append_param(&self, buf: &mut Vec<u8>) -> usize {
let a = [1, *self as u8];
buf.extend(a);
1
}
}
macro_rules! imp_number {
($($code:literal => $($ty:ty as $base:ty),*),* $(,)?) => {
$($(impl SQParam for $ty { fn append_param(&self, b: &mut Vec<u8>) -> usize {
let mut buf = ::itoa::Buffer::new();
let str = buf.format(<$base>::from(*self));
b.push($code); b.extend(str.as_bytes()); b.push(b'\n');
1
} })*)*
}
}
macro_rules! imp_terminated_str_type {
($($code:literal => $($ty:ty),*),* $(,)?) => {
$($(impl SQParam for $ty { fn append_param(&self, buf: &mut Vec<u8>) -> usize { buf.push($code); buf.extend(self.to_string().as_bytes()); buf.push(b'\n'); 1} })*)*
}
}
imp_number!(
2 => u8 as u8, NonZeroU8 as u8, u16 as u16, NonZeroU16 as u16, u32 as u32, NonZeroU32 as u32, u64 as u64, NonZeroU64 as u64, usize as usize, NonZeroUsize as usize,
3 => i8 as i8, NonZeroI8 as i8, i16 as i16, NonZeroI16 as i16, i32 as i32, NonZeroI32 as i32, i64 as i64, NonZeroI64 as i64, isize as isize, NonZeroIsize as isize,
);
imp_terminated_str_type!(
4 => f32, f64
);
impl<'a> SQParam for &'a [u8] {
fn append_param(&self, buf: &mut Vec<u8>) -> usize {
buf.push(5);
pushlen!(buf, self.len());
buf.extend(*self);
1
}
}
impl<const N: usize> SQParam for [u8; N] {
fn append_param(&self, buf: &mut Vec<u8>) -> usize {
buf.push(5);
pushlen!(buf, self.len());
buf.extend(self);
1
}
}
impl SQParam for Vec<u8> {
fn append_param(&self, buf: &mut Vec<u8>) -> usize {
buf.push(5);
pushlen!(buf, self.len());
buf.extend(self);
1
}
}
impl<'a> SQParam for &'a str {
fn append_param(&self, buf: &mut Vec<u8>) -> usize {
buf.push(6);
pushlen!(buf, self.len());
buf.extend(self.as_bytes());
1
}
}
impl SQParam for String {
fn append_param(&self, buf: &mut Vec<u8>) -> usize {
self.as_str().append_param(buf)
}
}
const LIST_SYM_OPEN: u8 = 0x07;
const LIST_SYM_CLOSE: u8 = ']' as u8;
#[derive(Debug, PartialEq, Clone)]
pub struct QList<'a, T: SQParam> {
l: &'a [T],
}
impl<'a, T: SQParam> QList<'a, T> {
pub fn new(l: &'a [T]) -> Self {
Self { l }
}
}
impl<'a, T: SQParam> SQParam for QList<'a, T> {
fn append_param(&self, q: &mut Vec<u8>) -> usize {
q.push(LIST_SYM_OPEN);
for param in self.l {
param.append_param(q);
}
q.push(LIST_SYM_CLOSE);
1
}
}
#[test]
fn list_param() {
let data = vec!["hello", "giant", "world"];
let list = QList::new(&data);
let q = query!(
"insert into apps.social(?, ?, ?)",
"username",
"password",
list
);
assert_eq!(q.param_cnt(), 3);
dbg!(String::from_utf8(q.debug_encode_packet())).unwrap();
}