1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
//! Safe abstractions around the Object Store FFI.
use fastly_shared::{FastlyStatus, INVALID_BODY_HANDLE, INVALID_OBJECT_STORE_HANDLE};
use fastly_sys::fastly_object_store as sys;

use crate::handle::BodyHandle;

/// Errors that can arise during Object Store operations.
///
/// This type is marked as non-exhaustive because more variants will be added over time.
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum ObjectStoreError {
    /// The key provided for this operation was not valid.
    #[error("Invalid Object Store key")]
    InvalidKey,
    /// The Object Store handle provided for this operation was not valid.
    #[error("Invalid Object Store handle")]
    InvalidObjectStoreHandle,
    /// No Object Store by this name exists.
    #[error("Object Store {0:?} not found")]
    ObjectStoreNotFound(String),
    /// Some unexpected error occurred.
    #[error("Unexpected Object Store error: {0:?}")]
    Unexpected(FastlyStatus),
}

impl From<FastlyStatus> for ObjectStoreError {
    fn from(st: FastlyStatus) -> Self {
        ObjectStoreError::Unexpected(st)
    }
}

/// A handle to a key-value store that is open for lookups and inserting.
#[derive(Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct StoreHandle {
    handle: u32,
}

impl StoreHandle {
    /// Get the underlying representation of the handle.
    ///
    /// This should only be used when calling the raw ABI directly, and care should be taken not to
    /// reuse or alias handle values.
    pub fn as_u32(&self) -> u32 {
        self.handle
    }

    /// Open a handle to the Object Store with the given name.
    pub fn open(name: &str) -> Result<Option<StoreHandle>, ObjectStoreError> {
        let mut store_handle_out = INVALID_OBJECT_STORE_HANDLE;
        let status = unsafe { sys::open(name.as_ptr(), name.len(), &mut store_handle_out) };
        status.result().map_err(|st| match st {
            FastlyStatus::INVAL => ObjectStoreError::ObjectStoreNotFound(name.to_owned()),
            _ => st.into(),
        })?;
        if store_handle_out == INVALID_OBJECT_STORE_HANDLE {
            Ok(None)
        } else {
            Ok(Some(StoreHandle {
                handle: store_handle_out,
            }))
        }
    }

    /// Look up a value in the Object Store.
    ///
    /// Returns `Ok(Some(BodyHandle))` if a value is found, and `Ok(None)` if the key was not
    /// found or is expired.
    pub fn lookup(&self, key: impl AsRef<[u8]>) -> Result<Option<BodyHandle>, ObjectStoreError> {
        let mut body_handle_out = INVALID_BODY_HANDLE;
        let key = key.as_ref();
        let status =
            unsafe { sys::lookup(self.as_u32(), key.as_ptr(), key.len(), &mut body_handle_out) };
        status.result().map_err(|st| match st {
            FastlyStatus::BADF => ObjectStoreError::InvalidObjectStoreHandle,
            FastlyStatus::INVAL => ObjectStoreError::InvalidKey,
            _ => st.into(),
        })?;
        if body_handle_out == INVALID_BODY_HANDLE {
            Ok(None)
        } else {
            Ok(Some(unsafe { BodyHandle::from_u32(body_handle_out) }))
        }
    }

    /// Insert a value into the Object Store.
    ///
    /// If the Object Store already contains a value for this key, it will be overwritten.
    pub fn insert(
        &mut self,
        key: impl AsRef<str>,
        value: BodyHandle,
    ) -> Result<(), ObjectStoreError> {
        let key = key.as_ref();
        let status =
            unsafe { sys::insert(self.as_u32(), key.as_ptr(), key.len(), value.into_u32()) };
        status.result().map_err(|st| match st {
            FastlyStatus::BADF => ObjectStoreError::InvalidObjectStoreHandle,
            FastlyStatus::INVAL => ObjectStoreError::InvalidKey,
            _ => st.into(),
        })?;
        Ok(())
    }
}