use crate::{Error, Executor, Request, Response};
use http::header::{AsHeaderName, ToStrError};
use http::StatusCode;
use http::{Method, Uri, Version};
use std::any::Any;
use std::any::TypeId;
use std::cell::UnsafeCell;
use std::collections::HashMap;
use std::fmt::Display;
use std::net::SocketAddr;
use std::ops::{Deref, DerefMut};
use std::rc::Rc;
use std::str::FromStr;
use std::sync::Arc;
struct PublicScope;
pub struct Context<S>(Rc<UnsafeCell<Inner<S>>>);
pub struct SyncContext<S> {
pub exec: Executor,
pub remote_addr: SocketAddr,
state: S,
storage: HashMap<TypeId, Bucket>,
}
struct Inner<S> {
request: Request,
response: Response,
ctx: SyncContext<S>,
}
#[derive(Debug, Clone)]
struct Bucket(HashMap<String, Arc<dyn Any + Send + Sync>>);
#[derive(Debug, Clone)]
pub struct Variable<'a, T> {
name: &'a str,
value: Arc<T>,
}
impl<T> Deref for Variable<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&*self.value
}
}
impl<'a, T> Variable<'a, T> {
#[inline]
fn new(name: &'a str, value: Arc<T>) -> Self {
Self { name, value }
}
#[inline]
pub fn value(&self) -> Arc<T> {
self.value.clone()
}
}
impl Variable<'_, String> {
pub fn parse<T>(&self) -> Result<T, Error>
where
T: FromStr,
T::Err: Display,
{
self.deref().parse().map_err(|err| {
Error::new(
StatusCode::BAD_REQUEST,
format!(
"{}\ntype of variable `{}` should be {}",
err,
self.name,
std::any::type_name::<T>()
),
true,
)
})
}
}
impl Bucket {
pub fn new() -> Self {
Self(HashMap::new())
}
#[inline]
pub fn insert<'a, T: Any + Send + Sync>(
&mut self,
name: &'a str,
value: T,
) -> Option<Variable<'a, T>> {
self.0
.insert(name.to_string(), Arc::new(value))
.and_then(|value| value.downcast().ok())
.map(|value| Variable::new(name, value))
}
#[inline]
pub fn get<'a, T: Any + Send + Sync>(
&self,
name: &'a str,
) -> Option<Variable<'a, T>> {
self.0.get(name).and_then(|value| {
Some(Variable {
name,
value: value.clone().downcast().ok()?,
})
})
}
}
impl Default for Bucket {
fn default() -> Self {
Self::new()
}
}
impl<S> Context<S> {
pub(crate) fn new(
request: Request,
state: S,
exec: Executor,
remote_addr: SocketAddr,
) -> Self {
let inner = Inner {
request,
response: Response::new(),
ctx: SyncContext {
state,
exec,
storage: HashMap::new(),
remote_addr,
},
};
Self(Rc::new(UnsafeCell::new(inner)))
}
pub(crate) unsafe fn unsafe_clone(&self) -> Self {
Self(self.0.clone())
}
fn inner(&self) -> &Inner<S> {
unsafe { &*self.0.get() }
}
fn inner_mut(&mut self) -> &mut Inner<S> {
unsafe { &mut *self.0.get() }
}
#[inline]
pub fn req(&self) -> &Request {
&self.inner().request
}
#[inline]
pub fn resp(&self) -> &Response {
&self.inner().response
}
#[inline]
pub fn req_mut(&mut self) -> &mut Request {
&mut self.inner_mut().request
}
#[inline]
pub fn resp_mut(&mut self) -> &mut Response {
&mut self.inner_mut().response
}
pub fn uri(&self) -> &Uri {
&self.req().uri
}
pub fn method(&self) -> &Method {
&self.req().method
}
pub fn header(&self, name: impl AsHeaderName) -> Option<Result<&str, ToStrError>> {
self.req().headers.get(name).map(|value| value.to_str())
}
pub fn status(&self) -> StatusCode {
self.resp().status
}
pub fn version(&self) -> Version {
self.req().version
}
}
impl<S> SyncContext<S> {
pub fn store_scoped<'a, SC, T>(
&mut self,
_scope: SC,
name: &'a str,
value: T,
) -> Option<Variable<'a, T>>
where
SC: Any,
T: Any + Send + Sync,
{
let id = TypeId::of::<SC>();
match self.storage.get_mut(&id) {
Some(bucket) => bucket.insert(name, value),
None => {
let mut bucket = Bucket::default();
bucket.insert(name, value);
self.storage.insert(id, bucket);
None
}
}
}
pub fn store<'a, T>(&mut self, name: &'a str, value: T) -> Option<Variable<'a, T>>
where
T: Any + Send + Sync,
{
self.store_scoped(PublicScope, name, value)
}
pub fn load_scoped<'a, SC, T>(&self, name: &'a str) -> Option<Variable<'a, T>>
where
SC: Any,
T: Any + Send + Sync,
{
let id = TypeId::of::<SC>();
self.storage.get(&id).and_then(|bucket| bucket.get(name))
}
pub fn load<'a, T>(&self, name: &'a str) -> Option<Variable<'a, T>>
where
T: Any + Send + Sync,
{
self.load_scoped::<PublicScope, T>(name)
}
}
impl<S> Deref for SyncContext<S> {
type Target = S;
fn deref(&self) -> &Self::Target {
&self.state
}
}
impl<S> DerefMut for SyncContext<S> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.state
}
}
impl<S: Clone> Clone for SyncContext<S> {
fn clone(&self) -> Self {
Self {
state: self.state.clone(),
exec: self.exec.clone(),
storage: self.storage.clone(),
remote_addr: self.remote_addr,
}
}
}
impl<S> Deref for Context<S> {
type Target = SyncContext<S>;
fn deref(&self) -> &Self::Target {
&self.inner().ctx
}
}
impl<S> DerefMut for Context<S> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner_mut().ctx
}
}
#[cfg(all(test, feature = "runtime"))]
mod tests_with_runtime {
use crate::{App, Context, Request};
use http::{StatusCode, Version};
#[async_std::test]
async fn status_and_version() -> Result<(), Box<dyn std::error::Error>> {
let service = App::new(())
.end(|ctx| async move {
assert_eq!(Version::HTTP_11, ctx.version());
assert_eq!(StatusCode::OK, ctx.status());
Ok(())
})
.fake_service();
service.serve(Request::default()).await?;
Ok(())
}
#[derive(Clone)]
struct State {
data: usize,
}
#[async_std::test]
async fn state_mut() -> Result<(), Box<dyn std::error::Error>> {
let service = App::new(State { data: 1 })
.gate_fn(|mut ctx, next| async move {
ctx.data = 1;
next.await
})
.end(|ctx: Context<State>| async move {
assert_eq!(1, ctx.data);
Ok(())
})
.fake_service();
service.serve(Request::default()).await?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::{Bucket, Variable};
use http::StatusCode;
use std::sync::Arc;
#[test]
fn bucket() {
let mut bucket = Bucket::new();
assert!(bucket.get::<String>("id").is_none());
assert!(bucket.insert("id", "1".to_string()).is_none());
let id: i32 = bucket.get::<String>("id").unwrap().parse().unwrap();
assert_eq!(1, id);
assert_eq!(
1,
bucket
.insert("id", "2".to_string())
.unwrap()
.parse::<i32>()
.unwrap()
);
}
#[test]
fn variable() {
assert_eq!(
1,
Variable::new("id", Arc::new("1".to_string()))
.parse::<i32>()
.unwrap()
);
let result = Variable::new("id", Arc::new("x".to_string())).parse::<usize>();
assert!(result.is_err());
let status = result.unwrap_err();
assert_eq!(StatusCode::BAD_REQUEST, status.status_code);
assert!(status
.message
.ends_with("type of variable `id` should be usize"));
}
}