use crate::{
cache::core::{
handle, handle::ReplaceOptions as HandleReplaceOptions, CacheError, CacheKey, Found,
FoundInner, WriteOptions,
},
convert::{ToHeaderName, ToHeaderValue},
handle::RequestHandle,
http::body::StreamingBody,
};
use bytes::Bytes;
use fastly_sys::fastly_cache::CacheLookupState;
use http::{HeaderName, HeaderValue};
use std::{sync::Arc, time::Duration};
#[derive(Clone, Debug, Default)]
pub enum ReplaceStrategy {
#[default]
Immediate,
ImmediateForceMiss,
Wait,
}
#[derive(Debug, Default)]
struct ReplaceOptions {
request_headers: Option<RequestHandle>,
replace_strategy: ReplaceStrategy,
service: Option<String>,
always_use_requested_range: bool,
}
impl ReplaceOptions {
fn as_handle_options(&self) -> HandleReplaceOptions<'_> {
HandleReplaceOptions {
request_headers: self.request_headers.as_ref(),
replace_strategy: self.replace_strategy.clone(),
service: self.service.clone(),
always_use_requested_range: self.always_use_requested_range,
}
}
}
pub struct ReplaceBuilder {
key: CacheKey,
options: ReplaceOptions,
}
pub fn replace(key: CacheKey) -> ReplaceBuilder {
ReplaceBuilder {
key,
options: ReplaceOptions::default(),
}
}
impl ReplaceBuilder {
pub fn begin(self) -> Result<Replace, CacheError> {
let replace_handle = handle::replace(self.key, &self.options.as_handle_options())?;
let handle = Arc::new(replace_handle);
let existing_object = if handle.get_state().contains(CacheLookupState::FOUND) {
let found = Found {
inner: FoundInner::ExistingObject(handle.clone()),
};
Some(found)
} else {
None
};
Ok(Replace {
handle,
existing_object,
options: WriteOptions::default(),
})
}
#[doc = include_str!("../../../docs/snippets/cache-headers.md")]
pub fn header(self, name: impl ToHeaderName, value: impl ToHeaderValue) -> Self {
self.header_values(name.into_owned(), Some(&value.into_owned()))
}
#[doc = include_str!("../../../docs/snippets/cache-headers.md")]
pub fn header_values<'a>(
mut self,
name: impl ToHeaderName,
values: impl IntoIterator<Item = &'a HeaderValue>,
) -> Self {
self.options
.request_headers
.get_or_insert_with(RequestHandle::new)
.set_header_values(&name.into_owned(), values);
self
}
pub fn replace_strategy(mut self, strategy: ReplaceStrategy) -> Self {
self.options.replace_strategy = strategy;
self
}
#[doc=include_str!("../../../docs/snippets/always-use-requested-range.md")]
pub fn always_use_requested_range(mut self) -> Self {
self.options.always_use_requested_range = true;
self
}
}
pub struct Replace {
handle: Arc<handle::CacheReplaceHandle>,
existing_object: Option<Found>,
options: WriteOptions,
}
impl Replace {
pub fn execute(self, max_age: Duration) -> Result<StreamingBody, CacheError> {
let Replace {
handle,
mut options,
existing_object,
} = self;
options.max_age = max_age;
drop(existing_object);
let body_handle = Arc::into_inner(handle)
.unwrap()
.replace_insert(&options.as_handle_options())?;
Ok(body_handle.into())
}
pub fn deliver_node_max_age(mut self, duration: Duration) -> Self {
self.options.edge_max_age = Some(duration);
self
}
pub fn existing_object(&self) -> Option<&Found> {
self.existing_object.as_ref()
}
#[doc = include_str!("../../../docs/snippets/cache-headers.md")]
pub fn header(self, name: impl ToHeaderName, value: impl ToHeaderValue) -> Self {
self.header_values(name.into_owned(), Some(&value.into_owned()))
}
#[doc = include_str!("../../../docs/snippets/cache-headers.md")]
pub fn header_values<'a>(
mut self,
name: impl ToHeaderName,
values: impl IntoIterator<Item = &'a HeaderValue>,
) -> Self {
self.options
.request_headers
.get_or_insert_with(RequestHandle::new)
.set_header_values(&name.into_owned(), values);
self
}
pub fn initial_age(mut self, age: Duration) -> Self {
self.options.initial_age = Some(age);
self
}
#[doc = include_str!("../../../docs/snippets/cache-insert-known-length.md")]
pub fn known_length(mut self, length: u64) -> Self {
self.options.length = Some(length);
self
}
#[doc = include_str!("../../../docs/snippets/cache-insert-sensitive-data.md")]
pub fn sensitive_data(mut self, is_sensitive_data: bool) -> Self {
self.options.sensitive_data = is_sensitive_data;
self
}
#[doc = include_str!("../../../docs/snippets/cache-swr.md")]
pub fn stale_while_revalidate(mut self, duration: Duration) -> Self {
self.options.stale_while_revalidate = Some(duration);
self
}
#[doc = include_str!("../../../docs/snippets/cache-insert-surrogate-keys.md")]
pub fn surrogate_keys<'a>(mut self, keys: impl IntoIterator<Item = &'a str>) -> Self {
self.options.surrogate_keys(keys);
self
}
pub fn user_metadata(mut self, user_metadata: Bytes) -> Self {
self.options.user_metadata = Some(user_metadata);
self
}
#[doc = include_str!("../../../docs/snippets/cache-headers.md")]
pub fn vary_by<'a>(mut self, headers: impl IntoIterator<Item = &'a HeaderName>) -> Self {
self.options.vary_by(headers);
self
}
}