use crate::abi;
use bytes::BytesMut;
use fastly_shared::FastlyStatus;
#[derive(Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct ConfigStoreHandle {
handle: u32,
}
impl ConfigStoreHandle {
#[cfg_attr(
not(target_env = "p1"),
deprecated(
since = "0.11.6",
note = "This code will need to be updated for wasip2."
)
)]
pub const INVALID: Self = ConfigStoreHandle {
handle: fastly_shared::INVALID_CONFIG_STORE_HANDLE,
};
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:?}"),
})
}
pub fn get(&self, key: &str, initial_buf_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(initial_buf_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,
)
};
let status = match status {
FastlyStatus::BUFLEN if nwritten != 0 => {
buf.resize(nwritten, 0);
nwritten = 0;
unsafe {
abi::fastly_config_store::get(
self.as_u32(),
key.as_ptr(),
key.len(),
buf.as_mut_ptr(),
buf.capacity(),
&mut nwritten,
)
}
}
s => s,
};
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(Vec::from(buf)).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:?}"),
}
}
pub fn contains(&self, key: &str) -> Result<bool, LookupError> {
let mut buf = BytesMut::with_capacity(0);
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 {
FastlyStatus::OK | FastlyStatus::BUFLEN => Ok(true),
FastlyStatus::NONE => Ok(false),
FastlyStatus::ERROR => Err(LookupError::Other),
FastlyStatus::BADF => Err(LookupError::ConfigStoreInvalid),
FastlyStatus::INVAL => Err(LookupError::KeyInvalid),
FastlyStatus::UNSUPPORTED => Err(LookupError::KeyTooLong),
FastlyStatus::LIMITEXCEEDED => Err(LookupError::TooManyLookups),
e => panic!("fastly_config_store::get returned an unrecognized result; please report this as a bug: {e:?}"),
}
}
#[cfg_attr(
not(target_env = "p1"),
deprecated(
since = "0.11.6",
note = "This code will need to be updated for wasip2."
)
)]
pub fn is_valid(&self) -> bool {
!self.is_invalid()
}
#[cfg_attr(
not(target_env = "p1"),
deprecated(
since = "0.11.6",
note = "This code will need to be updated for wasip2."
)
)]
pub fn is_invalid(&self) -> bool {
self.handle == Self::INVALID.handle
}
pub(crate) fn as_u32(&self) -> u32 {
self.handle
}
pub(crate) fn as_u32_mut(&mut self) -> &mut u32 {
&mut self.handle
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum OpenError {
#[error("config store could not be found")]
ConfigStoreDoesNotExist,
#[error("config store names cannot be empty")]
NameEmpty,
#[error("config store name too long")]
NameTooLong,
#[error("invalid config store name")]
NameInvalid,
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum LookupError {
#[error("invalid config store")]
ConfigStoreInvalid,
#[error("invalid config store key")]
KeyInvalid,
#[error("config store keys must be shorter than 256 characters")]
KeyTooLong,
#[error("config store value was longer than the given buffer")]
ValueTooLong,
#[error("Too many lookups have been performed")]
TooManyLookups,
#[error("config store lookup failed")]
Other,
}