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
//! Low-level Compute Config Store interfaces.
use crate::abi;
use bytes::BytesMut;
use fastly_shared::FastlyStatus;
/// A low-level interface to Config Store.
///
/// Methods here are typically exposed as the `try_*` APIs on
/// [`crate::config_store::ConfigStore`] and should not be needed directly.
/// Additionally, the [`get()`][`Self::get()`] method allows the caller to configure the
/// size of the buffer used to receive lookup results from the host. The size of this buffer
/// is typically managed by APIs exposed in [`crate::config_store::ConfigStore`].
#[derive(Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct ConfigStoreHandle {
handle: u32,
}
impl ConfigStoreHandle {
/// An invalid handle.
pub const INVALID: Self = ConfigStoreHandle {
handle: fastly_shared::INVALID_CONFIG_STORE_HANDLE,
};
/// Acquire a handle to an Config Store.
///
/// If a handle could not be acquired, an [`OpenError`] will be returned.
pub fn open(name: &str) -> Result<Self, OpenError> {
use OpenError::*;
let mut handle = Self::INVALID;
unsafe { abi::fastly_config_store::open(name.as_ptr(), name.len(), handle.as_u32_mut()) }
.result()
.map(|_| handle)
.map_err(|status| match status {
FastlyStatus::NONE => NameEmpty,
FastlyStatus::UNSUPPORTED => NameTooLong,
FastlyStatus::INVAL => NameInvalid,
FastlyStatus::BADF => ConfigStoreDoesNotExist,
e => panic!("fastly_config_store::open returned an unrecognized result; please report this as a bug: {e:?}"),
})
}
/// Lookup a value in this config store.
///
/// If successful, this function returns `Ok(Some(_))` if an entry was found, or `Ok(None)` if
/// no entry with the given key was found. If the lookup failed, a [`LookupError`] will be
/// returned.
pub fn get(&self, key: &str, max_len: usize) -> Result<Option<String>, LookupError> {
if self.is_invalid() {
panic!("cannot lookup value with invalid config store handle");
}
let mut buf = BytesMut::with_capacity(max_len);
let mut nwritten = 0;
let status = unsafe {
abi::fastly_config_store::get(
self.as_u32(),
key.as_ptr(),
key.len(),
buf.as_mut_ptr(),
buf.capacity(),
&mut nwritten,
)
};
match status.result().map(|_| nwritten) {
Ok(nwritten) => {
assert!(
nwritten <= buf.capacity(),
"fastly_config_store::get wrote too many bytes"
);
unsafe {
buf.set_len(nwritten);
}
Ok(Some(
String::from_utf8(buf.to_vec()).expect("host returns valid UTF-8"),
))
}
Err(FastlyStatus::NONE) => Ok(None),
Err(FastlyStatus::ERROR) => Err(LookupError::Other),
Err(FastlyStatus::BADF) => Err(LookupError::ConfigStoreInvalid),
Err(FastlyStatus::INVAL) => Err(LookupError::KeyInvalid),
Err(FastlyStatus::UNSUPPORTED) => Err(LookupError::KeyTooLong),
Err(FastlyStatus::BUFLEN) => Err(LookupError::ValueTooLong),
Err(FastlyStatus::LIMITEXCEEDED) => Err(LookupError::TooManyLookups),
Err(e) => panic!("fastly_config_store::get returned an unrecognized result; please report this as a bug: {e:?}"),
}
}
/// Return true if the config store contains an entry with the given key.
pub fn contains(&self, key: &str) -> Result<bool, LookupError> {
use LookupError::*;
match self.get(key, 0) {
Ok(Some(_)) | Err(ValueTooLong) => Ok(true),
Ok(None) => Ok(false),
Err(e) => Err(e),
}
}
/// Return true if the [`crate::config_store::ConfigStore`] handle is valid.
pub fn is_valid(&self) -> bool {
!self.is_invalid()
}
/// Return true if the [`crate::config_store::ConfigStore`] handle is invalid.
pub fn is_invalid(&self) -> bool {
self.handle == Self::INVALID.handle
}
/// 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(crate) fn as_u32(&self) -> u32 {
self.handle
}
/// Get a mutable reference to the underlying `u32` 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(crate) fn as_u32_mut(&mut self) -> &mut u32 {
&mut self.handle
}
}
/// Errors thrown when a config store could not be opened.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum OpenError {
/// No config store with this name was found.
#[error("config store could not be found")]
ConfigStoreDoesNotExist,
/// A config store name was empty.
#[error("config store names cannot be empty")]
NameEmpty,
/// A config store name was too long.
#[error("config store name too long")]
NameTooLong,
/// A config store name was invalid.
#[error("invalid config store name")]
NameInvalid,
}
/// Errors thrown when a config store lookup failed.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum LookupError {
/// The config store handle used for a lookup was invalid.
#[error("invalid config store")]
ConfigStoreInvalid,
/// The config store key provided was invalid.
#[error("invalid config store key")]
KeyInvalid,
/// The config store key provided was too long.
#[error("config store keys must be shorter than 256 characters")]
KeyTooLong,
/// The config store value was too long for the provided buffer length.
#[error("config store value was longer than the given buffer")]
ValueTooLong,
/// Too many lookups have been performed
#[error("Too many lookups have been performed")]
TooManyLookups,
/// A config store lookup failed for some other reason.
#[error("config store lookup failed")]
Other,
}