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
use futures::future;
use std::ops::{Deref, DerefMut};

use crate::{body::Body, configuration::Store, Extract, Response, RouteMatch};

/// An HTTP request.
///
/// A convenient alias for the `http::Request` type, using Tide's `Body`.
pub type Request = http::Request<Body>;

/// A value that can be computed on-demand from a request.
pub trait Compute: 'static + Sync + Send + Clone + Sized {
    /// Compute the value directly from the given request.
    fn compute_fresh(req: &mut Request) -> Self;

    /// Compute the value, or return a copy if it has already been computed for this request.
    fn compute(req: &mut Request) -> Self {
        if req.extensions().get::<ComputedMarker<Self>>().is_none() {
            let t = Self::compute_fresh(req);
            req.extensions_mut().insert(ComputedMarker(t));
        }

        req.extensions()
            .get::<ComputedMarker<Self>>()
            .unwrap()
            .0
            .clone()
    }
}

/// A private marker to ensure that computed values cannot be accessed directly through `extensions`
struct ComputedMarker<T>(T);

/// An extractor for computed values.
///
/// Endpoints can use this extractor to automatically compute values for a request, and re-use cached
/// results if those values have been computed previously (e.g. in some middleware).
#[derive(Clone)]
pub struct Computed<T>(pub T);

impl<T> Deref for Computed<T> {
    type Target = T;
    fn deref(&self) -> &T {
        &self.0
    }
}

impl<T> DerefMut for Computed<T> {
    fn deref_mut(&mut self) -> &mut T {
        &mut self.0
    }
}

impl<Data: 'static, T: Compute> Extract<Data> for Computed<T> {
    type Fut = future::Ready<Result<Self, Response>>;
    fn extract(
        data: &mut Data,
        req: &mut Request,
        params: &Option<RouteMatch<'_>>,
        store: &Store,
    ) -> Self::Fut {
        future::ok(Computed(T::compute(req)))
    }
}