use crate::constants::type_;
use bytes::{BufMut, BytesMut};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum BindError {
#[error("to many arguments given")]
TooManyArgumentsBound,
#[error("missing argument")]
TooFewArgumentsBound,
#[error("try from int")]
TryFromInt(#[from] std::num::TryFromIntError),
}
const _: () = {
assert!(size_of::<BindError>() <= 8);
};
pub type BindResult<T> = Result<T, BindError>;
pub struct Writer<'a>(&'a mut BytesMut);
impl<'a> Writer<'a> {
#[allow(unused)]
pub(crate) fn new(w: &'a mut BytesMut) -> Self {
Writer(w)
}
#[inline]
pub fn put_u8(&mut self, v: u8) {
self.0.put_u8(v);
}
#[inline]
pub fn put_u16(&mut self, v: u16) {
self.0.put_u16_le(v);
}
#[inline]
pub fn put_u24(&mut self, v: u32) {
self.0.put_u8((v & 0xFF) as u8);
self.0.put_u8(((v >> 8) & 0xFF) as u8);
self.0.put_u8(((v >> 16) & 0xFF) as u8);
}
#[inline]
pub fn put_u32(&mut self, v: u32) {
self.0.put_u32_le(v);
}
#[inline]
pub fn put_u64(&mut self, v: u64) {
self.0.put_u64_le(v);
}
#[inline]
pub fn put_i8(&mut self, v: i8) {
self.0.put_i8(v);
}
#[inline]
pub fn put_i16(&mut self, v: i16) {
self.0.put_i16_le(v);
}
#[inline]
pub fn put_i32(&mut self, v: i32) {
self.0.put_i32_le(v);
}
#[inline]
pub fn put_i64(&mut self, v: i64) {
self.0.put_i64_le(v);
}
#[inline]
pub fn put_f32(&mut self, v: f32) {
self.0.put_f32_le(v);
}
#[inline]
pub fn put_f64(&mut self, v: f64) {
self.0.put_f64_le(v);
}
#[inline]
pub fn put_lenenc(&mut self, v: u64) {
if v < 0xFB {
self.put_u8(v as u8);
} else if v <= 0xFFFF {
self.put_u8(0xFC);
self.put_u16(v as u16);
} else if v <= 0xFFFFFF {
self.put_u8(0xFD);
self.put_u24(v as u32);
} else {
self.put_u8(0xFE);
self.put_u64(v);
}
}
pub fn put_slice(&mut self, src: &[u8]) {
self.0.put_slice(src);
}
}
pub trait Bind {
const UNSIGNED: bool = false;
const TYPE: u8;
fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool>;
}
impl Bind for u8 {
const UNSIGNED: bool = true;
const TYPE: u8 = type_::TINY;
#[inline]
fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
writer.put_u8(*self);
Ok(true)
}
}
impl Bind for i8 {
const TYPE: u8 = type_::TINY;
#[inline]
fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
writer.put_i8(*self);
Ok(true)
}
}
impl Bind for u16 {
const UNSIGNED: bool = true;
const TYPE: u8 = type_::SHORT;
#[inline]
fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
writer.put_u16(*self);
Ok(true)
}
}
impl Bind for i16 {
const TYPE: u8 = type_::SHORT;
#[inline]
fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
writer.put_i16(*self);
Ok(true)
}
}
impl Bind for u32 {
const UNSIGNED: bool = true;
const TYPE: u8 = type_::LONG;
#[inline]
fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
writer.put_u32(*self);
Ok(true)
}
}
impl Bind for i32 {
const TYPE: u8 = type_::LONG;
#[inline]
fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
writer.put_i32(*self);
Ok(true)
}
}
impl Bind for u64 {
const UNSIGNED: bool = true;
const TYPE: u8 = type_::LONG_LONG;
#[inline]
fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
writer.put_u64(*self);
Ok(true)
}
}
impl Bind for i64 {
const TYPE: u8 = type_::LONG_LONG;
#[inline]
fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
writer.put_i64(*self);
Ok(true)
}
}
impl Bind for f32 {
const TYPE: u8 = type_::FLOAT;
#[inline]
fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
writer.put_f32(*self);
Ok(true)
}
}
impl Bind for f64 {
const TYPE: u8 = type_::DOUBLE;
#[inline]
fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
writer.put_f64(*self);
Ok(true)
}
}
impl Bind for bool {
const UNSIGNED: bool = true;
const TYPE: u8 = type_::TINY;
#[inline]
fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
writer.put_u8(*self as u8);
Ok(true)
}
}
impl Bind for String {
const TYPE: u8 = type_::STRING;
#[inline]
fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
writer.put_lenenc(self.len() as u64);
writer.put_slice(self.as_bytes());
Ok(true)
}
}
impl Bind for str {
const TYPE: u8 = type_::STRING;
#[inline]
fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
writer.put_lenenc(self.len() as u64);
writer.put_slice(self.as_bytes());
Ok(true)
}
}
impl Bind for Vec<u8> {
const TYPE: u8 = type_::BLOB;
#[inline]
fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
writer.put_lenenc(self.len() as u64);
writer.put_slice(self);
Ok(true)
}
}
impl Bind for [u8] {
const TYPE: u8 = type_::BLOB;
#[inline]
fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
writer.put_lenenc(self.len() as u64);
writer.put_slice(self);
Ok(true)
}
}
impl<T: Bind> Bind for Option<T> {
const TYPE: u8 = T::TYPE;
const UNSIGNED: bool = T::UNSIGNED;
#[inline]
fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
match self {
Some(v) => v.bind(writer),
None => Ok(false),
}
}
}
impl<T: Bind + ?Sized> Bind for &T {
const TYPE: u8 = T::TYPE;
const UNSIGNED: bool = T::UNSIGNED;
#[inline]
fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
(*self).bind(writer)
}
}
pub trait ListBind {
type T: Bind + ?Sized;
fn single(&self) -> &Self::T;
fn get(&self, idx: usize) -> &Self::T;
fn list_length(&self) -> Option<usize>;
}
impl<T: Bind + ?Sized> ListBind for T {
type T = T;
#[inline]
fn single(&self) -> &Self::T {
self
}
#[inline]
fn get(&self, _: usize) -> &Self::T {
panic!("Singular")
}
#[inline]
fn list_length(&self) -> Option<usize> {
None
}
}
pub struct List<'a, T> {
inner: &'a [T],
}
impl<'a, T: Bind> ListBind for List<'a, T> {
type T = T;
#[inline]
fn single(&self) -> &Self::T {
panic!("List")
}
#[inline]
fn get(&self, idx: usize) -> &Self::T {
&self.inner[idx]
}
#[inline]
fn list_length(&self) -> Option<usize> {
Some(self.inner.len())
}
}
impl<'a, T: Bind> ListBind for &List<'a, T> {
type T = T;
#[inline]
fn single(&self) -> &Self::T {
panic!("List")
}
#[inline]
fn get(&self, idx: usize) -> &Self::T {
&self.inner[idx]
}
#[inline]
fn list_length(&self) -> Option<usize> {
Some(self.inner.len())
}
}
#[cfg(feature = "list_hack")]
pub fn list<'a, T: Bind>(v: &'a [T]) -> List<'a, T> {
List { inner: v }
}
#[cfg(not(feature = "list_hack"))]
pub fn list<'a, T: Bind>(_: &'a [T]) -> std::convert::Infallible {
panic!("The list_hack feature is not")
}