assembly_data/fdb/
common.rs

1//! # Types that are common to most FDB-APIs
2//!
3//! This crate module contains rustic representations/types for values that
4//! necessarily appear in most of the APIs in this crate.
5
6use std::{
7    borrow::{Borrow, Cow},
8    convert::TryFrom,
9    error::Error,
10    fmt,
11    ops::Deref,
12};
13
14use encoding_rs::WINDOWS_1252;
15use memchr::memchr;
16
17#[repr(transparent)]
18#[derive(Ord, PartialOrd, Eq, PartialEq)]
19/// An owned latin-1 encoded string
20pub struct Latin1String {
21    inner: Box<[u8]>,
22}
23
24impl Latin1String {
25    /// Create a new string
26    ///
27    /// ## Safety
28    ///
29    /// Must not contain null bytes
30    pub unsafe fn new(inner: Box<[u8]>) -> Self {
31        Self { inner }
32    }
33
34    /// Create a new instance from a rust string.
35    ///
36    /// **Note**: This encodes any unavailable unicode codepoints as their equivalent HTML-Entity.
37    /// This is an implementation detail of the `encoding_rs` crate and not really useful for this crate.
38    pub fn encode(string: &str) -> Cow<Latin1Str> {
39        let (res, _enc, _has_replaced_chars) = WINDOWS_1252.encode(string);
40        match res {
41            Cow::Owned(o) => Cow::Owned(Self {
42                inner: o.into_boxed_slice(),
43            }),
44            Cow::Borrowed(b) => Cow::Borrowed(unsafe { Latin1Str::from_bytes_unchecked(b) }),
45        }
46    }
47}
48
49impl Borrow<Latin1Str> for Latin1String {
50    fn borrow(&self) -> &Latin1Str {
51        unsafe { Latin1Str::from_bytes_unchecked(&self.inner) }
52    }
53}
54
55impl Deref for Latin1String {
56    type Target = Latin1Str;
57
58    fn deref(&self) -> &Self::Target {
59        self.borrow()
60    }
61}
62
63impl From<Cow<'_, Latin1Str>> for Latin1String {
64    fn from(cow: Cow<'_, Latin1Str>) -> Self {
65        cow.into_owned()
66    }
67}
68
69impl From<&Latin1Str> for Latin1String {
70    fn from(src: &Latin1Str) -> Latin1String {
71        src.to_owned()
72    }
73}
74
75#[repr(transparent)]
76#[derive(PartialEq, PartialOrd, Eq, Ord)]
77/// A borrowed latin-1 encoded string (like `&str`)
78pub struct Latin1Str {
79    #[allow(dead_code)]
80    inner: [u8],
81}
82
83#[cfg(feature = "serde-derives")]
84impl serde::Serialize for Latin1Str {
85    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
86    where
87        S: serde::Serializer,
88    {
89        serializer.serialize_str(self.decode().as_ref())
90    }
91}
92
93impl fmt::Debug for &'_ Latin1Str {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        self.decode().fmt(f)
96    }
97}
98
99impl ToOwned for Latin1Str {
100    type Owned = Latin1String;
101
102    fn to_owned(&self) -> Self::Owned {
103        Latin1String {
104            inner: self.as_bytes().into(),
105        }
106    }
107}
108
109impl Latin1Str {
110    /// Takes all bytes until before the first null byte or end of slice.
111    pub(super) fn new(bytes: &[u8]) -> &Self {
112        let text = if let Some(index) = memchr(0x00, bytes) {
113            bytes.split_at(index).0
114        } else {
115            bytes
116        };
117        unsafe { Self::from_bytes_unchecked(text) }
118    }
119
120    /// Turns some bytes into a Latin1Str slice
121    ///
122    /// ## Safety
123    ///
124    /// The byte slice may not contain any null bytes
125    pub unsafe fn from_bytes_unchecked(text: &[u8]) -> &Self {
126        &*(text as *const [u8] as *const Latin1Str)
127    }
128
129    /// Get the bytes of the string
130    pub fn as_bytes(&self) -> &[u8] {
131        &self.inner
132    }
133
134    /// Get the bytes of the string
135    pub fn len(&self) -> usize {
136        self.inner.len()
137    }
138
139    /// Check whether the str is empty
140    pub fn is_empty(&self) -> bool {
141        self.inner.is_empty()
142    }
143
144    /// Calculates the number of 4-byte units that are needed to store
145    /// this string with at least one null terminator.
146    pub fn req_buf_len(&self) -> usize {
147        self.inner.len() / 4 + 1
148    }
149
150    /// Decode the string
151    pub fn decode(&self) -> Cow<str> {
152        WINDOWS_1252.decode(self.as_bytes()).0
153    }
154}
155
156/// Type-Parameters to [`Value`]
157///
158/// This trait is used to parameterize `Value` to produce the concrete types
159/// that are used elsewhere in this crate.
160pub trait Context {
161    /// The type that holds a `ValueType::String`
162    type String;
163    /// The type that holds a `ValueType::BigInt`
164    type I64;
165    /// The type that holds a `ValueType::VarChar`
166    type XML;
167}
168
169/// Trait for mapping value from one context to another
170///
171/// This traits allows us to implement a generic [`Value::map`] function
172/// that works similar to three [`FnMut`] closures but can guarantee that
173/// only one of them ever borrows `Self` mutably at the same time.
174pub trait ValueMapperMut<TI, TO>
175where
176    TI: Context,
177    TO: Context,
178{
179    /// Called when mapping a string
180    fn map_string(&mut self, from: &TI::String) -> TO::String;
181    /// Called when mapping an i64
182    fn map_i64(&mut self, from: &TI::I64) -> TO::I64;
183    /// Called when mapping an XML value
184    fn map_xml(&mut self, from: &TI::XML) -> TO::XML;
185}
186
187/// A single field value in the database
188///
189/// This is a generic enum that is the template for all
190/// other `Field` types in this crate.
191#[derive(Debug, PartialEq)]
192#[cfg_attr(feature = "serde-derives", derive(serde::Serialize))]
193#[cfg_attr(feature = "serde-derives", serde(untagged))]
194pub enum Value<T: Context> {
195    /// The NULL value
196    Nothing,
197    /// A 32 bit integer
198    Integer(i32),
199    /// A 32 bit IEEE floating point number
200    Float(f32),
201    /// A string
202    Text(T::String),
203    /// A boolean
204    Boolean(bool),
205    /// A 64 bit integer
206    BigInt(T::I64),
207    /// A (XML?) string
208    VarChar(T::XML),
209}
210
211impl<T: Context> Clone for Value<T>
212where
213    T::String: Clone,
214    T::XML: Clone,
215    T::I64: Clone,
216{
217    fn clone(&self) -> Self {
218        match self {
219            Value::Nothing => Value::Nothing,
220            Value::Integer(v) => Value::Integer(*v),
221            Value::Float(v) => Value::Float(*v),
222            Value::Text(v) => Value::Text(v.clone()),
223            Value::Boolean(v) => Value::Boolean(*v),
224            Value::BigInt(v) => Value::BigInt(v.clone()),
225            Value::VarChar(v) => Value::VarChar(v.clone()),
226        }
227    }
228}
229
230impl<T: Context> Copy for Value<T>
231where
232    T::String: Copy,
233    T::XML: Copy,
234    T::I64: Copy,
235{
236}
237
238impl<T: Context> Value<T> {
239    /// Creates a value of a different context using the given mapper
240    pub fn map<O, M>(&self, mapper: &mut M) -> Value<O>
241    where
242        O: Context,
243        M: ValueMapperMut<T, O>,
244    {
245        match self {
246            Value::Nothing => Value::Nothing,
247            Value::Integer(v) => Value::Integer(*v),
248            Value::Float(v) => Value::Float(*v),
249            Value::Text(v) => Value::Text(mapper.map_string(v)),
250            Value::Boolean(v) => Value::Boolean(*v),
251            Value::BigInt(v) => Value::BigInt(mapper.map_i64(v)),
252            Value::VarChar(v) => Value::VarChar(mapper.map_xml(v)),
253        }
254    }
255
256    /// Returns `Some` with the value if the field contains an [`Value::Integer`].
257    pub fn into_opt_integer(self) -> Option<i32> {
258        if let Self::Integer(value) = self {
259            Some(value)
260        } else {
261            None
262        }
263    }
264
265    /// Returns `Some` with the value if the field contains a [`Value::Float`].
266    pub fn into_opt_float(self) -> Option<f32> {
267        if let Self::Float(value) = self {
268            Some(value)
269        } else {
270            None
271        }
272    }
273
274    /// Returns `Some` with the value if the field contains a [`Value::Text`].
275    pub fn into_opt_text(self) -> Option<T::String> {
276        if let Self::Text(value) = self {
277            Some(value)
278        } else {
279            None
280        }
281    }
282
283    /// Returns `Some` with the value if the field contains a [`Value::Boolean`].
284    pub fn into_opt_boolean(self) -> Option<bool> {
285        if let Self::Boolean(value) = self {
286            Some(value)
287        } else {
288            None
289        }
290    }
291
292    /// Returns `Some` with the value if the field contains a [`Value::BigInt`].
293    pub fn into_opt_big_int(self) -> Option<T::I64> {
294        if let Self::BigInt(value) = self {
295            Some(value)
296        } else {
297            None
298        }
299    }
300
301    /// Returns `Some` with the value if the field contains a [`Value::VarChar`].
302    pub fn into_opt_varchar(self) -> Option<T::XML> {
303        if let Self::VarChar(value) = self {
304            Some(value)
305        } else {
306            None
307        }
308    }
309}
310
311impl<T: Context> From<&Value<T>> for ValueType {
312    fn from(val: &Value<T>) -> Self {
313        match val {
314            Value::Nothing => ValueType::Nothing,
315            Value::Integer(_) => ValueType::Integer,
316            Value::Float(_) => ValueType::Float,
317            Value::Text(_) => ValueType::Text,
318            Value::Boolean(_) => ValueType::Boolean,
319            Value::BigInt(_) => ValueType::BigInt,
320            Value::VarChar(_) => ValueType::VarChar,
321        }
322    }
323}
324
325/// Value datatypes used in the database
326#[derive(Debug, Copy, Clone, PartialEq, Eq)]
327#[cfg_attr(feature = "serde-derives", derive(serde::Serialize))]
328pub enum ValueType {
329    /// The NULL value
330    Nothing,
331    /// A 32-bit signed integer
332    Integer,
333    /// A 32-bit IEEE floating point number
334    Float,
335    /// A long string
336    Text,
337    /// A boolean
338    Boolean,
339    /// A 64 bit integer
340    BigInt,
341    /// An (XML?) string
342    VarChar,
343}
344
345impl ValueType {
346    /// Get a static name for the type
347    pub fn static_name(&self) -> &'static str {
348        match self {
349            ValueType::Nothing => "NULL",
350            ValueType::Integer => "INTEGER",
351            ValueType::Float => "FLOAT",
352            ValueType::Text => "TEXT",
353            ValueType::Boolean => "BOOLEAN",
354            ValueType::BigInt => "BIGINT",
355            ValueType::VarChar => "VARCHAR",
356        }
357    }
358}
359
360impl fmt::Display for ValueType {
361    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
362        write!(f, "{}", self.static_name())
363    }
364}
365
366impl From<ValueType> for u8 {
367    fn from(value_type: ValueType) -> u8 {
368        match value_type {
369            ValueType::Nothing => 0,
370            ValueType::Integer => 1,
371            ValueType::Float => 3,
372            ValueType::Text => 4,
373            ValueType::Boolean => 5,
374            ValueType::BigInt => 6,
375            ValueType::VarChar => 8,
376        }
377    }
378}
379
380impl From<ValueType> for u32 {
381    fn from(value_type: ValueType) -> u32 {
382        u8::from(value_type).into()
383    }
384}
385
386/// This represents a value type that could not be parsed
387#[derive(Debug, PartialEq, Eq)]
388pub struct UnknownValueType(u32);
389
390impl UnknownValueType {
391    /// Get the value that could not be interpreted
392    pub fn value(&self) -> u32 {
393        self.0
394    }
395}
396
397impl Error for UnknownValueType {}
398impl fmt::Display for UnknownValueType {
399    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
400        write!(f, "Unknown FDB value type {}", self.0)
401    }
402}
403
404impl TryFrom<u32> for ValueType {
405    type Error = UnknownValueType;
406
407    fn try_from(value_type: u32) -> Result<ValueType, Self::Error> {
408        match value_type {
409            0 => Ok(ValueType::Nothing),
410            1 => Ok(ValueType::Integer),
411            3 => Ok(ValueType::Float),
412            4 => Ok(ValueType::Text),
413            5 => Ok(ValueType::Boolean),
414            6 => Ok(ValueType::BigInt),
415            8 => Ok(ValueType::VarChar),
416            _ => Err(UnknownValueType(value_type)),
417        }
418    }
419}
420
421#[cfg(test)]
422mod tests {
423    use super::Latin1Str;
424
425    #[test]
426    fn test_latin1_req_bytes() {
427        assert_eq!(1, Latin1Str::new(b"a").req_buf_len());
428        assert_eq!(1, Latin1Str::new(b"ab").req_buf_len());
429        assert_eq!(1, Latin1Str::new(b"abc").req_buf_len());
430        assert_eq!(2, Latin1Str::new(b"abcd").req_buf_len());
431        assert_eq!(2, Latin1Str::new(b"abcde").req_buf_len());
432        assert_eq!(2, Latin1Str::new(b"abcdef").req_buf_len());
433        assert_eq!(2, Latin1Str::new(b"abcdefg").req_buf_len());
434        assert_eq!(3, Latin1Str::new(b"abcdefgh").req_buf_len());
435    }
436}