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 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
//! Interface to the [Compute KV Store][blog].
//!
//! [blog]: https://www.fastly.com/blog/introducing-the-compute-edge-kv-store-global-persistent-storage-for-compute-functions
use crate::Body;
pub use self::handle::KVStoreError;
use self::handle::StoreHandle;
use self::handle::PendingObjectStoreDeleteHandle;
use self::handle::PendingObjectStoreInsertHandle;
use self::handle::PendingObjectStoreLookupHandle;
// TODO ACF 2022-10-10: this module is temporarily public for the large kv preview.
#[doc(hidden)]
pub mod handle;
/// A [Compute KV Store][blog].
///
/// Keys in the KV Store must follow the following rules:
///
/// * Keys can contain any sequence of valid Unicode characters, of length 1-1024 bytes when
/// UTF-8 encoded.
/// * Keys cannot contain Carriage Return or Line Feed characters.
/// * Keys cannot start with `.well-known/acme-challenge/`.
/// * Keys cannot be named `.` or `..`.
///
/// [blog]: https://www.fastly.com/blog/introducing-the-compute-edge-kv-store-global-persistent-storage-for-compute-functions
pub struct KVStore {
handle: StoreHandle,
}
impl KVStore {
// TODO ACF 2022-10-10: temporary method to support the large kv preview
#[doc(hidden)]
pub fn as_handle(&self) -> &StoreHandle {
&self.handle
}
/// Open the KV Store with the given name.
///
/// If there is no store by that name, this returns `Ok(None)`.
pub fn open(name: &str) -> Result<Option<Self>, KVStoreError> {
match StoreHandle::open(name)? {
Some(handle) => Ok(Some(Self { handle })),
None => Ok(None),
}
}
/// Look up a value in the KV Store.
///
/// Returns `Ok(Some(Body))` if the value is found, and `Ok(None)` if the key was not found.
pub fn lookup(&self, key: &str) -> Result<Option<Body>, KVStoreError> {
Ok(self
.handle
.lookup(key.as_bytes())?
.map(|body_handle| body_handle.into()))
}
/// Initiate async lookup of a value in the KV Store.
///
/// Returns `Ok(Some(PendingObjectStoreLookupHandle))` lookup creation is successful.
pub fn lookup_async(
&self,
key: &str,
) -> Result<Option<PendingObjectStoreLookupHandle>, KVStoreError> {
Ok(self.handle.lookup_async(key.as_bytes())?)
}
/// Look up a value in the KV Store.
///
/// Returns `Ok(Some(Body))` if the value is found, and `Ok(None)` if the key was not found.
pub fn pending_lookup_wait(
&self,
pending_request_handle: PendingObjectStoreLookupHandle,
) -> Result<Option<Body>, KVStoreError> {
Ok(self
.handle
.pending_lookup_wait(pending_request_handle)?
.map(|body_handle| body_handle.into()))
}
/// Look up a value in the KV Store, and return it as a byte vector.
///
/// Returns `Ok(Some(Vec<u8>))` if the value is found, and `Ok(None)` if the key was not found.
pub fn lookup_bytes(&self, key: &str) -> Result<Option<Vec<u8>>, KVStoreError> {
Ok(self.lookup(key)?.map(|body| body.into_bytes()))
}
/// Look up a value in the KV Store, and return it as a string.
///
/// Returns `Ok(Some(String))` if the value is found, and `Ok(None)` if the key was not found.
///
/// # Panics
///
/// Panics if the value is not a valid UTF-8 string.
pub fn lookup_str(&self, key: &str) -> Result<Option<String>, KVStoreError> {
Ok(self.lookup(key)?.map(|body| body.into_string()))
}
/// Insert a value into the KV Store.
///
/// If the store already contained a value for this key, it will be overwritten.
///
/// The value may be provided as any type that can be converted to [`Body`], such as `&[u8]`,
/// `Vec<u8>`, `&str`, or `String`.
///
/// # Value sizes
///
/// The size of the value must be known when calling this method. In practice, that means that
/// if a [`Body`] value contains an external request or response, it must be encoded with
/// `Content-Length` rather than `Transfer-Encoding: chunked`.
///
/// For the moment, this method will return `StoreError::Unexpected(FastlyStatus::INVAL)` if the
/// value size is not known. This will be replaced by a more specific error value in a future
/// release.
pub fn insert(&mut self, key: &str, value: impl Into<Body>) -> Result<(), KVStoreError> {
self.handle.insert(key, value.into().into_handle())
}
/// Initiate async insert of a value into the KV Store.
///
/// Returns `Ok(Some(PendingObjectStoreInsertHandle))` if insert creation is successful.
pub fn insert_async(
&self,
key: &str,
value: impl Into<Body>,
) -> Result<Option<PendingObjectStoreInsertHandle>, KVStoreError> {
Ok(self.handle.insert_async(key, value.into().into_handle())?)
}
/// Insert a value in the KV Store.
///
/// Returns `Ok(())` if the value is found, and `KVStoreError` if the key was not found.
pub fn pending_insert_wait(
&self,
pending_insert_handle: PendingObjectStoreInsertHandle,
) -> Result<(), KVStoreError> {
self.handle.pending_insert_wait(pending_insert_handle)?;
Ok(())
}
/// Delete a value in the KV Store.
///
/// Returns `Ok(())` if delete is successful.
pub fn delete(&self, key: &str) -> Result<(), KVStoreError> {
Ok(self.pending_delete_wait(self.handle.delete_async(key)?)?)
}
/// Initiate async delete of a key the KV Store.
///
/// Returns `Ok(PendingObjectStoreDeleteHandle)` if delete creation is successful.
pub fn delete_async(&self, key: &str) -> Result<PendingObjectStoreDeleteHandle, KVStoreError> {
Ok(self.handle.delete_async(key)?)
}
/// Delete a value in the KV Store.
///
/// Returns `Ok(())` if the key is deleted, and `KVStoreError` if the key was not found.
pub fn pending_delete_wait(
&self,
pending_delete_handle: PendingObjectStoreDeleteHandle,
) -> Result<(), KVStoreError> {
self.handle.pending_delete_wait(pending_delete_handle)?;
Ok(())
}
}