use std::fmt;
use std::sync::Arc;
use crate::Result;
use crate::backend::Client;
use crate::dataset::{Data, Dataset, Datasets};
use crate::frontier::memory::InMemFrontier;
use crate::frontier::queue::Queue;
use crate::request::{Request, Response, ResponseExt};
pub struct Context<C> {
request: Request,
client: C,
queue: Queue,
datasets: Arc<Datasets>,
}
impl<C> Context<C> {
#[must_use]
pub fn new(request: Request, client: C) -> Self {
let queue = Queue::new(InMemFrontier::fifo()).child_of(&request);
Self::from_parts(request, client, queue, Arc::default())
}
pub(crate) const fn from_parts(
request: Request,
client: C,
queue: Queue,
datasets: Arc<Datasets>,
) -> Self {
Self {
request,
client,
queue,
datasets,
}
}
#[must_use]
pub fn with_queue(mut self, queue: &Queue) -> Self {
self.queue = queue.child_of(&self.request);
self
}
#[must_use]
pub fn with_dataset<T, D>(mut self, dataset: D) -> Self
where
T: Send + 'static,
D: Dataset<T>,
{
Arc::make_mut(&mut self.datasets).insert(dataset);
self
}
#[must_use]
pub const fn request(&self) -> &Request {
&self.request
}
pub const fn request_mut(&mut self) -> &mut Request {
&mut self.request
}
#[must_use]
pub const fn client(&self) -> &C {
&self.client
}
pub const fn client_mut(&mut self) -> &mut C {
&mut self.client
}
#[must_use]
pub const fn queue(&self) -> &Queue {
&self.queue
}
pub fn dataset<T: Send + 'static>(&self) -> Result<Data<T>> {
self.datasets.get()
}
#[must_use]
pub fn into_parts(self) -> (Request, C) {
(self.request, self.client)
}
pub async fn fetch(self) -> Result<Response>
where
C: Client,
{
let response = self.client.fetch(self.request).await?;
if let Some(uri) = response.final_uri() {
self.queue.rebase(uri.clone());
}
Ok(response)
}
}
impl<C> fmt::Debug for Context<C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Context")
.field("method", self.request.method())
.field("uri", self.request.uri())
.field("queue", &self.queue)
.finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::backend::Noop;
use crate::dataset::InMemDataset;
use crate::frontier::seed::Seed;
use crate::request::{Depth, RequestExt};
#[tokio::test]
async fn standalone_context() {
let request = Seed::get("https://example.com/").unwrap().into_request();
let cx = Context::new(request, Noop).with_dataset(InMemDataset::<u32>::new());
cx.dataset::<u32>().unwrap().write(1).await.unwrap();
assert!(cx.dataset::<String>().is_err());
cx.queue().follow("https://example.com/next").await.unwrap();
assert_eq!(cx.queue().size_hint(), Some(1));
assert_eq!(cx.request().depth(), Depth::ROOT);
let response = cx.fetch().await.unwrap();
assert!(response.status().is_success());
}
}