This commit is contained in:
Ibraheem Ahmed 2022-01-27 15:30:53 +02:00 committed by GitHub
commit 2764c867ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 82 additions and 2 deletions

View File

@ -106,6 +106,7 @@ smallvec = "1.6.1"
socket2 = "0.4.0" socket2 = "0.4.0"
time = { version = "0.3", default-features = false, features = ["formatting"] } time = { version = "0.3", default-features = false, features = ["formatting"] }
url = "2.1" url = "2.1"
tokio = { version = "1.7.1", features = ["sync"] }
[dev-dependencies] [dev-dependencies]
actix-files = "0.6.0-beta.15" actix-files = "0.6.0-beta.15"

View File

@ -1,9 +1,11 @@
use std::{any::type_name, ops::Deref, sync::Arc}; use std::{any::type_name, cell::Cell, fmt, future::Future, ops::Deref, rc::Rc, sync::Arc};
use actix_http::Extensions; use actix_http::Extensions;
use actix_utils::future::{err, ok, Ready}; use actix_utils::future::{err, ok, Ready};
use futures_core::future::LocalBoxFuture; use futures_core::future::LocalBoxFuture;
use futures_util::FutureExt;
use serde::Serialize; use serde::Serialize;
use tokio::sync::OnceCell;
use crate::{ use crate::{
dev::Payload, error::ErrorInternalServerError, extract::FromRequest, request::HttpRequest, dev::Payload, error::ErrorInternalServerError, extract::FromRequest, request::HttpRequest,
@ -175,6 +177,83 @@ impl<T: ?Sized + 'static> DataFactory for Data<T> {
} }
} }
/// A lazy extractor for thread-local data.
pub struct LazyData<T> {
inner: Rc<LazyDataInner<T>>,
}
pub struct LazyDataInner<T> {
cell: OnceCell<T>,
fut: Cell<Option<LocalBoxFuture<'static, T>>>,
}
impl<T> Clone for LazyData<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<T: fmt::Debug> fmt::Debug for LazyData<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Lazy")
.field("cell", &self.inner.cell)
.field("fut", &"..")
.finish()
}
}
impl<T> LazyData<T> {
pub fn new<F, Fut>(init: F) -> LazyData<T>
where
F: FnOnce() -> Fut,
Fut: Future<Output = T> + 'static,
{
Self {
inner: Rc::new(LazyDataInner {
cell: OnceCell::new(),
fut: Cell::new(Some(init().boxed_local())),
}),
}
}
pub async fn get(&self) -> &T {
self.inner
.cell
.get_or_init(|| async move {
match self.inner.fut.take() {
Some(fut) => fut.await,
None => panic!("LazyData instance has previously been poisoned"),
}
})
.await
}
}
impl<T: 'static> FromRequest for LazyData<T> {
type Config = ();
type Error = Error;
type Future = Ready<Result<Self, Error>>;
#[inline]
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
if let Some(lazy) = req.app_data::<LazyData<T>>() {
ok(lazy.clone())
} else {
log::debug!(
"Failed to construct App-level LazyData extractor. \
Request path: {:?} (type: {})",
req.path(),
type_name::<T>(),
);
err(ErrorInternalServerError(
"App data is not configured, to configure use App::data()",
))
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -11,7 +11,7 @@ use crate::{
}; };
pub use crate::config::ServiceConfig; pub use crate::config::ServiceConfig;
pub use crate::data::Data; pub use crate::data::{Data, LazyData};
pub use crate::request::HttpRequest; pub use crate::request::HttpRequest;
pub use crate::request_data::ReqData; pub use crate::request_data::ReqData;
pub use crate::response::HttpResponse; pub use crate::response::HttpResponse;