indradb/
util.rs

1//! Utility functions. These are public because they may be useful for crates
2//! that implement Datastore.
3
4use std::collections::hash_map::DefaultHasher;
5use std::hash::{Hash, Hasher};
6use std::io::{Cursor, Read, Result as IoResult, Write};
7use std::str;
8
9use crate::errors::{Result, ValidationError, ValidationResult};
10use crate::models;
11
12use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
13use once_cell::sync::Lazy;
14use uuid::v1::{Context, Timestamp};
15use uuid::Uuid;
16
17const NODE_ID: [u8; 6] = [0, 0, 0, 0, 0, 0];
18
19static CONTEXT: Lazy<Context> = Lazy::new(|| Context::new(0));
20
21/// A byte-serializable value, frequently employed in the keys of key/value
22/// store.
23pub enum Component<'a> {
24    /// A UUID.
25    Uuid(Uuid),
26    /// A fixed length string.
27    FixedLengthString(&'a str),
28    /// An identifier.
29    Identifier(models::Identifier),
30    /// A JSON value.
31    Json(&'a models::Json),
32}
33
34impl Component<'_> {
35    /// Gets the length of the component. This isn't called `len` to avoid a
36    /// clippy warning.
37    pub fn byte_len(&self) -> usize {
38        match *self {
39            Component::Uuid(_) => 16,
40            Component::FixedLengthString(s) => s.len(),
41            Component::Identifier(t) => t.0.len() + 1,
42            Component::Json(_) => 8,
43        }
44    }
45
46    /// Writes a component into a cursor of bytes.
47    pub fn write(&self, cursor: &mut Cursor<Vec<u8>>) -> IoResult<()> {
48        match *self {
49            Component::Uuid(uuid) => cursor.write_all(uuid.as_bytes()),
50            Component::FixedLengthString(s) => cursor.write_all(s.as_bytes()),
51            Component::Identifier(i) => {
52                cursor.write_all(&[i.0.len() as u8])?;
53                cursor.write_all(i.0.as_bytes())
54            }
55            Component::Json(json) => {
56                let mut hasher = DefaultHasher::new();
57                json.hash(&mut hasher);
58                let hash = hasher.finish();
59                cursor.write_u64::<BigEndian>(hash)
60            }
61        }
62    }
63}
64
65// Serializes component(s) into bytes.
66///
67/// # Arguments
68/// * `components`: The components to serialize to bytes.
69pub fn build(components: &[Component]) -> Vec<u8> {
70    let len = components.iter().fold(0, |len, component| len + component.byte_len());
71    let mut cursor: Cursor<Vec<u8>> = Cursor::new(Vec::with_capacity(len));
72    for component in components {
73        component
74            .write(&mut cursor)
75            .expect("failed to write bytes to in-memory buffer");
76    }
77
78    cursor.into_inner()
79}
80
81/// Reads a UUID from bytes.
82///
83/// # Arguments
84/// * `cursor`: The bytes to read from.
85pub fn read_uuid<T: AsRef<[u8]>>(cursor: &mut Cursor<T>) -> Result<Uuid> {
86    let mut buf: [u8; 16] = [0; 16];
87    cursor.read_exact(&mut buf)?;
88    let uuid = Uuid::from_slice(&buf).unwrap();
89    Ok(uuid)
90}
91
92/// Reads an identifier from bytes.
93///
94/// # Arguments
95/// * `cursor`: The bytes to read from.
96///
97/// # Safety
98/// This is used for reading in datastores that already checked the validity
99/// of the data at write-time. Re-validation is skipped in the interest of
100/// performance.
101pub unsafe fn read_identifier<T: AsRef<[u8]>>(cursor: &mut Cursor<T>) -> Result<models::Identifier> {
102    let t_len = {
103        let mut buf: [u8; 1] = [0; 1];
104        cursor.read_exact(&mut buf)?;
105        buf[0] as usize
106    };
107
108    let mut buf = vec![0u8; t_len];
109    cursor.read_exact(&mut buf)?;
110    let s = str::from_utf8_unchecked(&buf).to_string();
111    Ok(models::Identifier::new_unchecked(s))
112}
113
114/// Reads a fixed-length string from bytes.
115///
116/// # Arguments
117/// * `cursor`: The bytes to read from.
118pub fn read_fixed_length_string<T: AsRef<[u8]>>(cursor: &mut Cursor<T>) -> Result<String> {
119    let mut buf = String::new();
120    cursor.read_to_string(&mut buf)?;
121    Ok(buf)
122}
123
124/// Reads a `u64` from bytes.
125///
126/// # Arguments
127/// * `cursor`: The bytes to read from.
128pub fn read_u64<T: AsRef<[u8]>>(cursor: &mut Cursor<T>) -> Result<u64> {
129    let i = cursor.read_u64::<BigEndian>()?;
130    Ok(i)
131}
132
133/// Generates a UUID v1. This utility method uses a shared context and node ID
134/// to help ensure generated UUIDs are unique.
135pub fn generate_uuid_v1() -> Uuid {
136    Uuid::new_v1(Timestamp::now(&*CONTEXT), &NODE_ID)
137}
138
139/// Gets the next UUID that would occur after the given one.
140///
141/// # Arguments
142/// * `uuid`: The input UUID.
143///
144/// # Errors
145/// Returns a `ValidationError` if the input UUID is the great possible value
146/// (i.e., FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF)
147pub fn next_uuid(uuid: Uuid) -> ValidationResult<Uuid> {
148    let mut bytes = *uuid.as_bytes();
149
150    for i in (0..16).rev() {
151        if bytes[i] < 255 {
152            bytes[i] += 1;
153            return Ok(Uuid::from_slice(&bytes[..]).unwrap());
154        } else {
155            bytes[i] = 0;
156        }
157    }
158
159    Err(ValidationError::CannotIncrementUuid)
160}
161
162/// Extracts vertices from the last query output value, or `None`.
163///
164/// # Arguments
165/// * `output`: The query output.
166pub fn extract_vertices(mut output: Vec<models::QueryOutputValue>) -> Option<Vec<models::Vertex>> {
167    if let Some(models::QueryOutputValue::Vertices(vertices)) = output.pop() {
168        Some(vertices)
169    } else {
170        None
171    }
172}
173
174/// Extracts edges from the last query output value, or `None`.
175///
176/// # Arguments
177/// * `output`: The query output.
178pub fn extract_edges(mut output: Vec<models::QueryOutputValue>) -> Option<Vec<models::Edge>> {
179    if let Some(models::QueryOutputValue::Edges(edges)) = output.pop() {
180        Some(edges)
181    } else {
182        None
183    }
184}
185
186/// Extracts a count from the last query output value, or `None`.
187///
188/// # Arguments
189/// * `output`: The query output.
190pub fn extract_count(mut output: Vec<models::QueryOutputValue>) -> Option<u64> {
191    if let Some(models::QueryOutputValue::Count(count)) = output.pop() {
192        Some(count)
193    } else {
194        None
195    }
196}
197
198/// Extracts vertex properties from the last query output value, or `None`.
199///
200/// # Arguments
201/// * `output`: The query output.
202pub fn extract_vertex_properties(mut output: Vec<models::QueryOutputValue>) -> Option<Vec<models::VertexProperties>> {
203    if let Some(models::QueryOutputValue::VertexProperties(props)) = output.pop() {
204        Some(props)
205    } else {
206        None
207    }
208}
209
210/// Extracts edge properties from the last query output value, or `None`.
211///
212/// # Arguments
213/// * `output`: The query output.
214pub fn extract_edge_properties(mut output: Vec<models::QueryOutputValue>) -> Option<Vec<models::EdgeProperties>> {
215    if let Some(models::QueryOutputValue::EdgeProperties(props)) = output.pop() {
216        Some(props)
217    } else {
218        None
219    }
220}
221
222#[cfg(test)]
223mod tests {
224    use super::{
225        extract_count, extract_edge_properties, extract_edges, extract_vertex_properties, extract_vertices,
226        generate_uuid_v1, next_uuid,
227    };
228    use core::str::FromStr;
229    use uuid::Uuid;
230
231    #[test]
232    fn should_generate_new_uuid_v1() {
233        let first = generate_uuid_v1();
234        let second = generate_uuid_v1();
235        assert_ne!(first, second);
236    }
237
238    #[test]
239    fn should_generate_next_uuid() {
240        let result = next_uuid(Uuid::from_str("16151dea-a538-4bf1-9559-851e256cf139").unwrap());
241        assert!(result.is_ok());
242        assert_eq!(
243            result.unwrap(),
244            Uuid::from_str("16151dea-a538-4bf1-9559-851e256cf13a").unwrap()
245        );
246
247        let from_uuid = Uuid::from_str("ffffffff-ffff-ffff-ffff-ffffffffffff").unwrap();
248        assert!(next_uuid(from_uuid).is_err());
249    }
250
251    #[test]
252    fn should_not_extract_vertices_on_empty() {
253        assert_eq!(extract_vertices(vec![]), None);
254    }
255
256    #[test]
257    fn should_not_extract_edges_on_empty() {
258        assert_eq!(extract_edges(vec![]), None);
259    }
260
261    #[test]
262    fn should_not_extract_count_on_empty() {
263        assert_eq!(extract_count(vec![]), None);
264    }
265
266    #[test]
267    fn should_not_extract_vertex_properties_on_empty() {
268        assert_eq!(extract_vertex_properties(vec![]), None);
269    }
270
271    #[test]
272    fn should_not_extract_edge_properties_on_empty() {
273        assert_eq!(extract_edge_properties(vec![]), None);
274    }
275}