use crate::prelude::*;
use http_body_util::Full;
use hyper::Response;
use hyper::body::Bytes;
use std::path::PathBuf;
use std::pin::Pin;
use tokio::fs;
use tokio::fs::create_dir_all;
pub struct RecordReplayFileStore {
path: PathBuf,
records: RecordReplayRecords,
}
impl RecordReplayFileStore {
pub async fn from<Str: Into<String>>(path: Str) -> Result<Self, CinemaError> {
let path = PathBuf::from(path.into());
let records = match fs::read_to_string(&path).await {
Ok(content) => {
toml::from_str(&content).unwrap()
}
Err(_) => Default::default(),
};
Ok(RecordReplayFileStore { path, records })
}
}
impl RecordReplayStore for RecordReplayFileStore {
fn handle(
&mut self,
payload: ProxyHandlerPayload<'_>,
) -> Result<Option<Response<Full<Bytes>>>, CinemaError> {
let record = self.records.requests.iter().find(|req| {
req.method == payload.request.method
&& req.uri == payload.request.uri
&& req.body == payload.request.body
});
match record {
Some(record) => {
let response = &record.response;
let mut builder = Response::builder().status(response.status);
if let Some(headers) = &response.headers {
for (name, value) in headers.iter() {
builder = builder.header(name, value);
}
}
let body = Bytes::from(response.body.clone());
let response = builder
.body(Full::new(body))
.map_err(|err| CinemaError::RestoreResponseBody(err))?;
return Ok(Some(response));
}
None => Ok(None),
}
}
fn commit<'a>(&'a self) -> Pin<Box<dyn Future<Output = Result<(), CinemaError>> + Send + 'a>> {
Box::pin(async {
if let Some(parent) = self.path.parent() {
if !parent.exists() {
create_dir_all(parent).await.unwrap();
}
}
let _ = fs::write(
&self.path,
toml::to_string(&self.records).map_err(|err| CinemaError::Serialize(err, ""))?,
)
.await;
Ok(())
})
}
fn record(&mut self, payload: RecordReplayRecord) -> Result<(), CinemaError> {
self.records.requests.push(payload);
Ok(())
}
}