use std::{
borrow::{Borrow, Cow},
convert::TryFrom,
error::Error,
fmt,
ops::Deref,
};
use encoding_rs::WINDOWS_1252;
use memchr::memchr;
#[repr(transparent)]
#[derive(Ord, PartialOrd, Eq, PartialEq)]
pub struct Latin1String {
inner: Box<[u8]>,
}
impl Latin1String {
pub unsafe fn new(inner: Box<[u8]>) -> Self {
Self { inner }
}
pub fn encode(string: &str) -> Cow<Latin1Str> {
let (res, _enc, _has_replaced_chars) = WINDOWS_1252.encode(string);
match res {
Cow::Owned(o) => Cow::Owned(Self {
inner: o.into_boxed_slice(),
}),
Cow::Borrowed(b) => Cow::Borrowed(unsafe { Latin1Str::from_bytes_unchecked(b) }),
}
}
}
impl Borrow<Latin1Str> for Latin1String {
fn borrow(&self) -> &Latin1Str {
unsafe { Latin1Str::from_bytes_unchecked(&self.inner) }
}
}
impl Deref for Latin1String {
type Target = Latin1Str;
fn deref(&self) -> &Self::Target {
self.borrow()
}
}
impl From<Cow<'_, Latin1Str>> for Latin1String {
fn from(cow: Cow<'_, Latin1Str>) -> Self {
cow.into_owned()
}
}
impl From<&Latin1Str> for Latin1String {
fn from(src: &Latin1Str) -> Latin1String {
src.to_owned()
}
}
#[repr(transparent)]
#[derive(PartialEq, PartialOrd, Eq, Ord)]
pub struct Latin1Str {
#[allow(dead_code)]
inner: [u8],
}
#[cfg(feature = "serde-derives")]
impl serde::Serialize for Latin1Str {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.decode().as_ref())
}
}
impl fmt::Debug for &'_ Latin1Str {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.decode().fmt(f)
}
}
impl ToOwned for Latin1Str {
type Owned = Latin1String;
fn to_owned(&self) -> Self::Owned {
Latin1String {
inner: self.as_bytes().into(),
}
}
}
impl Latin1Str {
pub(super) fn new(bytes: &[u8]) -> &Self {
let text = if let Some(index) = memchr(0x00, bytes) {
bytes.split_at(index).0
} else {
bytes
};
unsafe { Self::from_bytes_unchecked(text) }
}
pub unsafe fn from_bytes_unchecked(text: &[u8]) -> &Self {
&*(text as *const [u8] as *const Latin1Str)
}
pub fn as_bytes(&self) -> &[u8] {
&self.inner
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn req_buf_len(&self) -> usize {
self.inner.len() / 4 + 1
}
pub fn decode(&self) -> Cow<str> {
WINDOWS_1252.decode(self.as_bytes()).0
}
}
pub trait Context {
type String;
type I64;
type XML;
}
pub trait ValueMapperMut<TI, TO>
where
TI: Context,
TO: Context,
{
fn map_string(&mut self, from: &TI::String) -> TO::String;
fn map_i64(&mut self, from: &TI::I64) -> TO::I64;
fn map_xml(&mut self, from: &TI::XML) -> TO::XML;
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "serde-derives", derive(serde::Serialize))]
#[cfg_attr(feature = "serde-derives", serde(untagged))]
pub enum Value<T: Context> {
Nothing,
Integer(i32),
Float(f32),
Text(T::String),
Boolean(bool),
BigInt(T::I64),
VarChar(T::XML),
}
impl<T: Context> Clone for Value<T>
where
T::String: Clone,
T::XML: Clone,
T::I64: Clone,
{
fn clone(&self) -> Self {
match self {
Value::Nothing => Value::Nothing,
Value::Integer(v) => Value::Integer(*v),
Value::Float(v) => Value::Float(*v),
Value::Text(v) => Value::Text(v.clone()),
Value::Boolean(v) => Value::Boolean(*v),
Value::BigInt(v) => Value::BigInt(v.clone()),
Value::VarChar(v) => Value::VarChar(v.clone()),
}
}
}
impl<T: Context> Copy for Value<T>
where
T::String: Copy,
T::XML: Copy,
T::I64: Copy,
{
}
impl<T: Context> Value<T> {
pub fn map<O, M>(&self, mapper: &mut M) -> Value<O>
where
O: Context,
M: ValueMapperMut<T, O>,
{
match self {
Value::Nothing => Value::Nothing,
Value::Integer(v) => Value::Integer(*v),
Value::Float(v) => Value::Float(*v),
Value::Text(v) => Value::Text(mapper.map_string(v)),
Value::Boolean(v) => Value::Boolean(*v),
Value::BigInt(v) => Value::BigInt(mapper.map_i64(v)),
Value::VarChar(v) => Value::VarChar(mapper.map_xml(v)),
}
}
pub fn into_opt_integer(self) -> Option<i32> {
if let Self::Integer(value) = self {
Some(value)
} else {
None
}
}
pub fn into_opt_float(self) -> Option<f32> {
if let Self::Float(value) = self {
Some(value)
} else {
None
}
}
pub fn into_opt_text(self) -> Option<T::String> {
if let Self::Text(value) = self {
Some(value)
} else {
None
}
}
pub fn into_opt_boolean(self) -> Option<bool> {
if let Self::Boolean(value) = self {
Some(value)
} else {
None
}
}
pub fn into_opt_big_int(self) -> Option<T::I64> {
if let Self::BigInt(value) = self {
Some(value)
} else {
None
}
}
pub fn into_opt_varchar(self) -> Option<T::XML> {
if let Self::VarChar(value) = self {
Some(value)
} else {
None
}
}
}
impl<T: Context> From<&Value<T>> for ValueType {
fn from(val: &Value<T>) -> Self {
match val {
Value::Nothing => ValueType::Nothing,
Value::Integer(_) => ValueType::Integer,
Value::Float(_) => ValueType::Float,
Value::Text(_) => ValueType::Text,
Value::Boolean(_) => ValueType::Boolean,
Value::BigInt(_) => ValueType::BigInt,
Value::VarChar(_) => ValueType::VarChar,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde-derives", derive(serde::Serialize))]
pub enum ValueType {
Nothing,
Integer,
Float,
Text,
Boolean,
BigInt,
VarChar,
}
impl ValueType {
pub fn static_name(&self) -> &'static str {
match self {
ValueType::Nothing => "NULL",
ValueType::Integer => "INTEGER",
ValueType::Float => "FLOAT",
ValueType::Text => "TEXT",
ValueType::Boolean => "BOOLEAN",
ValueType::BigInt => "BIGINT",
ValueType::VarChar => "VARCHAR",
}
}
}
impl fmt::Display for ValueType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.static_name())
}
}
impl From<ValueType> for u8 {
fn from(value_type: ValueType) -> u8 {
match value_type {
ValueType::Nothing => 0,
ValueType::Integer => 1,
ValueType::Float => 3,
ValueType::Text => 4,
ValueType::Boolean => 5,
ValueType::BigInt => 6,
ValueType::VarChar => 8,
}
}
}
impl From<ValueType> for u32 {
fn from(value_type: ValueType) -> u32 {
u8::from(value_type).into()
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct UnknownValueType(u32);
impl UnknownValueType {
pub fn value(&self) -> u32 {
self.0
}
}
impl Error for UnknownValueType {}
impl fmt::Display for UnknownValueType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Unknown FDB value type {}", self.0)
}
}
impl TryFrom<u32> for ValueType {
type Error = UnknownValueType;
fn try_from(value_type: u32) -> Result<ValueType, Self::Error> {
match value_type {
0 => Ok(ValueType::Nothing),
1 => Ok(ValueType::Integer),
3 => Ok(ValueType::Float),
4 => Ok(ValueType::Text),
5 => Ok(ValueType::Boolean),
6 => Ok(ValueType::BigInt),
8 => Ok(ValueType::VarChar),
_ => Err(UnknownValueType(value_type)),
}
}
}
#[cfg(test)]
mod tests {
use super::Latin1Str;
#[test]
fn test_latin1_req_bytes() {
assert_eq!(1, Latin1Str::new(b"a").req_buf_len());
assert_eq!(1, Latin1Str::new(b"ab").req_buf_len());
assert_eq!(1, Latin1Str::new(b"abc").req_buf_len());
assert_eq!(2, Latin1Str::new(b"abcd").req_buf_len());
assert_eq!(2, Latin1Str::new(b"abcde").req_buf_len());
assert_eq!(2, Latin1Str::new(b"abcdef").req_buf_len());
assert_eq!(2, Latin1Str::new(b"abcdefg").req_buf_len());
assert_eq!(3, Latin1Str::new(b"abcdefgh").req_buf_len());
}
}