assembly_fdb_core/value/
mod.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    convert::TryFrom,
8    error::Error,
9    fmt::{self, Debug},
10};
11
12pub mod file;
13pub mod mem;
14pub mod owned;
15
16/// Type-Parameters to [`Value`]
17///
18/// This trait is used to parameterize `Value` to produce the concrete types
19/// that are used elsewhere in this crate.
20pub trait Context {
21    /// The type that holds a `ValueType::String`
22    type String;
23    /// The type that holds a `ValueType::BigInt`
24    type I64;
25    /// The type that holds a `ValueType::VarChar`
26    type XML;
27}
28
29/// Trait for mapping value from one context to another
30///
31/// This traits allows us to implement a generic [`Value::map`] function
32/// that works similar to three [`FnMut`] closures but can guarantee that
33/// only one of them ever borrows `Self` mutably at the same time.
34pub trait ValueMapperMut<TI, TO>
35where
36    TI: Context,
37    TO: Context,
38{
39    /// Called when mapping a string
40    fn map_string(&mut self, from: &TI::String) -> TO::String;
41    /// Called when mapping an i64
42    fn map_i64(&mut self, from: &TI::I64) -> TO::I64;
43    /// Called when mapping an XML value
44    fn map_xml(&mut self, from: &TI::XML) -> TO::XML;
45}
46
47/// A single field value in the database
48///
49/// This is a generic enum that is the template for all
50/// other `Field` types in this crate.
51#[derive(Debug)]
52#[cfg_attr(feature = "serde", derive(serde::Serialize))]
53#[cfg_attr(feature = "serde", serde(untagged))]
54pub enum Value<T: Context> {
55    /// The NULL value
56    Nothing,
57    /// A 32 bit integer
58    Integer(i32),
59    /// A 32 bit IEEE floating point number
60    Float(f32),
61    /// A string
62    Text(T::String),
63    /// A boolean
64    Boolean(bool),
65    /// A 64 bit integer
66    BigInt(T::I64),
67    /// A (XML?) string
68    VarChar(T::XML),
69}
70
71impl<C: Context> fmt::Display for Value<C>
72where
73    C::I64: Debug,
74    C::String: Debug,
75    C::XML: Debug,
76{
77    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78        match self {
79            Self::Nothing => write!(f, "NULL"),
80            Self::Integer(i) => write!(f, "{}", i),
81            Self::Float(v) => write!(f, "{}", v),
82            Self::Text(t) => write!(f, "{:?}", t),
83            Self::Boolean(b) => write!(f, "{}", b),
84            Self::BigInt(i) => write!(f, "{:?}", i),
85            Self::VarChar(v) => write!(f, "{:?}", v),
86        }
87    }
88}
89
90impl<C1: Context, C2: Context> PartialEq<Value<C1>> for Value<C2>
91where
92    C1::I64: PartialEq<C2::I64>,
93    C1::String: PartialEq<C2::String>,
94    C1::XML: PartialEq<C2::XML>,
95{
96    fn eq(&self, other: &Value<C1>) -> bool {
97        match other {
98            Value::Nothing => matches!(self, Self::Nothing),
99            Value::Integer(x) => matches!(self, Self::Integer(y) if x == y),
100            Value::Float(x) => matches!(self, Self::Float(y) if x == y),
101            Value::Text(x) => matches!(self, Self::Text(y) if x == y),
102            Value::Boolean(x) => matches!(self, Self::Boolean(y) if x == y),
103            Value::BigInt(x) => matches!(self, Self::BigInt(y) if x == y),
104            Value::VarChar(x) => matches!(self, Self::VarChar(y) if x == y),
105        }
106    }
107}
108
109impl<T: Context> Clone for Value<T>
110where
111    T::String: Clone,
112    T::XML: Clone,
113    T::I64: Clone,
114{
115    fn clone(&self) -> Self {
116        match self {
117            Value::Nothing => Value::Nothing,
118            Value::Integer(v) => Value::Integer(*v),
119            Value::Float(v) => Value::Float(*v),
120            Value::Text(v) => Value::Text(v.clone()),
121            Value::Boolean(v) => Value::Boolean(*v),
122            Value::BigInt(v) => Value::BigInt(v.clone()),
123            Value::VarChar(v) => Value::VarChar(v.clone()),
124        }
125    }
126}
127
128impl<T: Context> Copy for Value<T>
129where
130    T::String: Copy,
131    T::XML: Copy,
132    T::I64: Copy,
133{
134}
135
136impl<T: Context> Value<T> {
137    /// Creates a value of a different context using the given mapper
138    pub fn map<O, M>(&self, mapper: &mut M) -> Value<O>
139    where
140        O: Context,
141        M: ValueMapperMut<T, O>,
142    {
143        match self {
144            Value::Nothing => Value::Nothing,
145            Value::Integer(v) => Value::Integer(*v),
146            Value::Float(v) => Value::Float(*v),
147            Value::Text(v) => Value::Text(mapper.map_string(v)),
148            Value::Boolean(v) => Value::Boolean(*v),
149            Value::BigInt(v) => Value::BigInt(mapper.map_i64(v)),
150            Value::VarChar(v) => Value::VarChar(mapper.map_xml(v)),
151        }
152    }
153
154    /// Returns `Some` with the value if the field contains an [`Value::Integer`].
155    pub fn into_opt_integer(self) -> Option<i32> {
156        if let Self::Integer(value) = self {
157            Some(value)
158        } else {
159            None
160        }
161    }
162
163    /// Returns `Some` with the value if the field contains a [`Value::Float`].
164    pub fn into_opt_float(self) -> Option<f32> {
165        if let Self::Float(value) = self {
166            Some(value)
167        } else {
168            None
169        }
170    }
171
172    /// Returns `Some` with the value if the field contains a [`Value::Text`].
173    pub fn into_opt_text(self) -> Option<T::String> {
174        if let Self::Text(value) = self {
175            Some(value)
176        } else {
177            None
178        }
179    }
180
181    /// Returns `Some` with the value if the field contains a [`Value::Boolean`].
182    pub fn into_opt_boolean(self) -> Option<bool> {
183        if let Self::Boolean(value) = self {
184            Some(value)
185        } else {
186            None
187        }
188    }
189
190    /// Returns `Some` with the value if the field contains a [`Value::BigInt`].
191    pub fn into_opt_big_int(self) -> Option<T::I64> {
192        if let Self::BigInt(value) = self {
193            Some(value)
194        } else {
195            None
196        }
197    }
198
199    /// Returns `Some` with the value if the field contains a [`Value::VarChar`].
200    pub fn into_opt_varchar(self) -> Option<T::XML> {
201        if let Self::VarChar(value) = self {
202            Some(value)
203        } else {
204            None
205        }
206    }
207}
208
209impl<T: Context> From<&Value<T>> for ValueType {
210    fn from(val: &Value<T>) -> Self {
211        match val {
212            Value::Nothing => ValueType::Nothing,
213            Value::Integer(_) => ValueType::Integer,
214            Value::Float(_) => ValueType::Float,
215            Value::Text(_) => ValueType::Text,
216            Value::Boolean(_) => ValueType::Boolean,
217            Value::BigInt(_) => ValueType::BigInt,
218            Value::VarChar(_) => ValueType::VarChar,
219        }
220    }
221}
222
223/// Value datatypes used in the database
224#[derive(Debug, Copy, Clone, PartialEq, Eq)]
225#[cfg_attr(feature = "serde", derive(serde::Serialize))]
226pub enum ValueType {
227    /// The NULL value
228    Nothing,
229    /// A 32-bit signed integer
230    Integer,
231    /// A 32-bit IEEE floating point number
232    Float,
233    /// A long string
234    Text,
235    /// A boolean
236    Boolean,
237    /// A 64 bit integer
238    BigInt,
239    /// An (XML?) string
240    VarChar,
241}
242
243impl ValueType {
244    /// Get a static name for the type
245    pub fn static_name(&self) -> &'static str {
246        match self {
247            ValueType::Nothing => "NULL",
248            ValueType::Integer => "INTEGER",
249            ValueType::Float => "FLOAT",
250            ValueType::Text => "TEXT",
251            ValueType::Boolean => "BOOLEAN",
252            ValueType::BigInt => "BIGINT",
253            ValueType::VarChar => "VARCHAR",
254        }
255    }
256
257    /// Get the canonical SQLite name of this data type
258    pub fn to_sqlite_type(self) -> &'static str {
259        match self {
260            ValueType::Nothing => "BLOB_NONE",
261            ValueType::Integer => "INT32",
262            ValueType::Float => "REAL",
263            ValueType::Text => "TEXT4",
264            ValueType::Boolean => "INT_BOOL",
265            ValueType::BigInt => "INT64",
266            ValueType::VarChar => "TEXT_XML",
267        }
268    }
269
270    /// Take an SQLite column declaration type and guess the ValueType
271    ///
272    /// This function does a proper round-trip with `to_sqlite_type`
273    ///
274    /// ```
275    /// use assembly_fdb_core::value::ValueType;
276    ///
277    /// fn round_trip(v: ValueType) -> Option<ValueType> {
278    ///     ValueType::from_sqlite_type(v.to_sqlite_type())
279    /// }
280    ///
281    /// // Check whether the round-trip works
282    /// assert_eq!(round_trip(ValueType::Nothing), Some(ValueType::Nothing));
283    /// assert_eq!(round_trip(ValueType::Integer), Some(ValueType::Integer));
284    /// assert_eq!(round_trip(ValueType::Float), Some(ValueType::Float));
285    /// assert_eq!(round_trip(ValueType::Text), Some(ValueType::Text));
286    /// assert_eq!(round_trip(ValueType::Boolean), Some(ValueType::Boolean));
287    /// assert_eq!(round_trip(ValueType::BigInt), Some(ValueType::BigInt));
288    /// assert_eq!(round_trip(ValueType::VarChar), Some(ValueType::VarChar));
289    ///
290    /// // Check whether lcdr's names work
291    /// assert_eq!(ValueType::from_sqlite_type("none"), Some(ValueType::Nothing));
292    /// assert_eq!(ValueType::from_sqlite_type("int32"), Some(ValueType::Integer));
293    /// assert_eq!(ValueType::from_sqlite_type("real"), Some(ValueType::Float));
294    /// assert_eq!(ValueType::from_sqlite_type("text_4"), Some(ValueType::Text));
295    /// assert_eq!(ValueType::from_sqlite_type("int_bool"), Some(ValueType::Boolean));
296    /// assert_eq!(ValueType::from_sqlite_type("int64"), Some(ValueType::BigInt));
297    /// assert_eq!(ValueType::from_sqlite_type("text_8"), Some(ValueType::VarChar));
298    /// ```
299    pub fn from_sqlite_type(decl_type: &str) -> Option<Self> {
300        match decl_type {
301            "BLOB_NONE" | "blob_none" | "none" | "NULL" => Some(ValueType::Nothing),
302            "INT32" | "int32" | "TINYINT" | "SMALLINT" => Some(ValueType::Integer),
303            "real" | "REAL" | "FLOAT" => Some(ValueType::Float),
304            "TEXT4" | "text_4" | "TEXT" => Some(ValueType::Text),
305            "BIT" | "INT_BOOL" | "int_bool" => Some(ValueType::Boolean),
306            "INT64" | "int64" | "BIGINT" | "INTEGER" => Some(ValueType::BigInt),
307            "XML" | "TEXT_XML" | "xml" | "text_8" | "text_xml" | "VARCHAR" => {
308                Some(ValueType::VarChar)
309            }
310            _ => None,
311        }
312    }
313}
314
315impl fmt::Display for ValueType {
316    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317        write!(f, "{}", self.static_name())
318    }
319}
320
321impl From<ValueType> for u8 {
322    fn from(value_type: ValueType) -> u8 {
323        match value_type {
324            ValueType::Nothing => 0,
325            ValueType::Integer => 1,
326            ValueType::Float => 3,
327            ValueType::Text => 4,
328            ValueType::Boolean => 5,
329            ValueType::BigInt => 6,
330            ValueType::VarChar => 8,
331        }
332    }
333}
334
335impl From<ValueType> for u32 {
336    fn from(value_type: ValueType) -> u32 {
337        u8::from(value_type).into()
338    }
339}
340
341/// This represents a value type that could not be parsed
342#[derive(Debug, PartialEq, Eq)]
343pub struct UnknownValueType(u32);
344
345impl UnknownValueType {
346    /// Get the value that could not be interpreted
347    pub fn value(&self) -> u32 {
348        self.0
349    }
350}
351
352impl Error for UnknownValueType {}
353impl fmt::Display for UnknownValueType {
354    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
355        write!(f, "Unknown FDB value type {}", self.0)
356    }
357}
358
359impl TryFrom<u32> for ValueType {
360    type Error = UnknownValueType;
361
362    fn try_from(value_type: u32) -> Result<ValueType, Self::Error> {
363        match value_type {
364            0 => Ok(ValueType::Nothing),
365            1 => Ok(ValueType::Integer),
366            3 => Ok(ValueType::Float),
367            4 => Ok(ValueType::Text),
368            5 => Ok(ValueType::Boolean),
369            6 => Ok(ValueType::BigInt),
370            8 => Ok(ValueType::VarChar),
371            _ => Err(UnknownValueType(value_type)),
372        }
373    }
374}