2023-10-22 20:45:48 +02:00
use std ::convert ::Infallible ;
2023-10-22 15:26:03 +02:00
use axum ::{
http ::{ header ::InvalidHeaderValue , HeaderValue , StatusCode , Uri } ,
response ::{ IntoResponse , IntoResponseParts , ResponseParts } ,
} ;
use crate ::headers ;
2023-10-22 20:45:48 +02:00
#[ cfg(feature = " serde " ) ]
pub mod serde ;
2023-10-22 15:26:03 +02:00
const HX_SWAP_INNER_HTML : & str = " innerHTML " ;
const HX_SWAP_OUTER_HTML : & str = " outerHTML " ;
const HX_SWAP_BEFORE_BEGIN : & str = " beforebegin " ;
const HX_SWAP_AFTER_BEGIN : & str = " afterbegin " ;
const HX_SWAP_BEFORE_END : & str = " beforeend " ;
const HX_SWAP_AFTER_END : & str = " afterend " ;
const HX_SWAP_DELETE : & str = " delete " ;
const HX_SWAP_NONE : & str = " none " ;
/// The `HX-Location` header.
///
2023-10-22 20:45:48 +02:00
/// This response header can be used to trigger a client side redirection without reloading the whole page.
2023-10-22 15:26:03 +02:00
///
2023-10-22 20:45:48 +02:00
/// Will fail if the supplied Uri contains characters that are not visible ASCII (32-127).
#[ derive(Debug, Clone) ]
2023-10-23 20:43:03 +02:00
pub struct HxLocation ( pub Uri ) ;
2023-10-22 15:26:03 +02:00
impl IntoResponseParts for HxLocation {
type Error = HxError ;
fn into_response_parts ( self , mut res : ResponseParts ) -> Result < ResponseParts , Self ::Error > {
2023-10-22 20:45:48 +02:00
res . headers_mut ( ) . insert (
headers ::HX_LOCATION ,
HeaderValue ::from_maybe_shared ( self . 0. to_string ( ) ) ? ,
) ;
2023-10-22 15:26:03 +02:00
Ok ( res )
}
}
/// The `HX-Push-Url` header.
///
/// Pushes a new url into the history stack.
///
/// Will fail if the supplied Uri contains characters that are not visible ASCII (32-127).
#[ derive(Debug, Clone) ]
2023-10-23 20:43:03 +02:00
pub struct HxPushUrl ( pub Uri ) ;
2023-10-22 15:26:03 +02:00
impl IntoResponseParts for HxPushUrl {
type Error = HxError ;
fn into_response_parts ( self , mut res : ResponseParts ) -> Result < ResponseParts , Self ::Error > {
res . headers_mut ( ) . insert (
headers ::HX_PUSH_URL ,
HeaderValue ::from_maybe_shared ( self . 0. to_string ( ) ) ? ,
) ;
Ok ( res )
}
}
/// The `HX-Redirect` header.
///
/// Can be used to do a client-side redirect to a new location.
///
/// Will fail if the supplied Uri contains characters that are not visible ASCII (32-127).
#[ derive(Debug, Clone) ]
2023-10-23 20:43:03 +02:00
pub struct HxRedirect ( pub Uri ) ;
2023-10-22 15:26:03 +02:00
impl IntoResponseParts for HxRedirect {
type Error = HxError ;
fn into_response_parts ( self , mut res : ResponseParts ) -> Result < ResponseParts , Self ::Error > {
res . headers_mut ( ) . insert (
headers ::HX_REDIRECT ,
HeaderValue ::from_maybe_shared ( self . 0. to_string ( ) ) ? ,
) ;
Ok ( res )
}
}
/// The `HX-Refresh`header.
///
/// If set to `true` the client-side will do a full refresh of the page.
///
/// This responder will never fail.
#[ derive(Debug, Copy, Clone) ]
2023-10-23 20:43:03 +02:00
pub struct HxRefresh ( pub bool ) ;
2023-10-22 15:26:03 +02:00
impl IntoResponseParts for HxRefresh {
type Error = Infallible ;
fn into_response_parts ( self , mut res : ResponseParts ) -> Result < ResponseParts , Self ::Error > {
res . headers_mut ( ) . insert (
headers ::HX_REFRESH ,
match self . 0 {
true = > HeaderValue ::from_static ( " true " ) ,
false = > HeaderValue ::from_static ( " false " ) ,
} ,
) ;
Ok ( res )
}
}
/// The `HX-Replace-Url` header.
///
/// Replaces the currelt URL in the location bar.
///
/// Will fail if the supplied Uri contains characters that are not visible ASCII (32-127).
#[ derive(Debug, Clone) ]
2023-10-23 20:43:03 +02:00
pub struct HxReplaceUrl ( pub Uri ) ;
2023-10-22 15:26:03 +02:00
impl IntoResponseParts for HxReplaceUrl {
type Error = HxError ;
fn into_response_parts ( self , mut res : ResponseParts ) -> Result < ResponseParts , Self ::Error > {
res . headers_mut ( ) . insert (
headers ::HX_REPLACE_URL ,
HeaderValue ::from_maybe_shared ( self . 0. to_string ( ) ) ? ,
) ;
Ok ( res )
}
}
/// The `HX-Reswap` header.
///
/// Allows you to specidy how the response will be swapped.
///
/// This responder will never fail.
#[ derive(Debug, Copy, Clone) ]
2023-10-23 20:43:03 +02:00
pub struct HxReswap ( pub SwapOption ) ;
2023-10-22 15:26:03 +02:00
impl IntoResponseParts for HxReswap {
type Error = Infallible ;
fn into_response_parts ( self , mut res : ResponseParts ) -> Result < ResponseParts , Self ::Error > {
res . headers_mut ( ) . insert ( headers ::HX_RESWAP , self . 0. into ( ) ) ;
Ok ( res )
}
}
/// The `HX-Retarget` header.
///
/// A CSS selector that updates the target of the content update to a different element on the page.
///
/// Will fail if the supplied String contains characters that are not visible ASCII (32-127).
#[ derive(Debug, Clone) ]
2023-10-23 20:43:03 +02:00
pub struct HxRetarget ( pub String ) ;
2023-10-22 15:26:03 +02:00
impl IntoResponseParts for HxRetarget {
type Error = HxError ;
fn into_response_parts ( self , mut res : ResponseParts ) -> Result < ResponseParts , Self ::Error > {
res . headers_mut ( ) . insert (
headers ::HX_RETARGET ,
HeaderValue ::from_maybe_shared ( self . 0 ) ? ,
) ;
Ok ( res )
}
}
/// The `HX-Reselect` header.
///
/// A CSS selector that allows you to choose which part of the response is used to be swapped in. Overrides an existing hx-select on the triggering element.
///
/// Will fail if the supplied String contains characters that are not visible ASCII (32-127).
#[ derive(Debug, Clone) ]
2023-10-23 20:43:03 +02:00
pub struct HxReselect ( pub String ) ;
2023-10-22 15:26:03 +02:00
impl IntoResponseParts for HxReselect {
type Error = HxError ;
fn into_response_parts ( self , mut res : ResponseParts ) -> Result < ResponseParts , Self ::Error > {
res . headers_mut ( ) . insert (
headers ::HX_RESELECT ,
HeaderValue ::from_maybe_shared ( self . 0 ) ? ,
) ;
Ok ( res )
}
}
/// The `HX-Trigger` header.
///
/// Allows you to trigger client-side events.
///
2023-10-22 20:45:48 +02:00
/// Will fail if the supplied events contain or produce characters that are not visible ASCII (32-127) when serializing to json.
2023-10-22 15:26:03 +02:00
#[ derive(Debug, Clone) ]
2023-10-23 20:43:03 +02:00
pub struct HxResponseTrigger ( pub Vec < String > ) ;
2023-10-22 15:26:03 +02:00
2023-10-23 20:43:03 +02:00
impl IntoResponseParts for HxResponseTrigger {
2023-10-22 15:26:03 +02:00
type Error = HxError ;
fn into_response_parts ( self , mut res : ResponseParts ) -> Result < ResponseParts , Self ::Error > {
2023-10-22 20:45:48 +02:00
res . headers_mut ( ) . insert (
headers ::HX_TRIGGER ,
HeaderValue ::from_maybe_shared (
self . 0
. into_iter ( )
. reduce ( | acc , e | acc + " , " + & e )
. unwrap_or_default ( ) ,
) ? ,
) ;
2023-10-22 15:26:03 +02:00
Ok ( res )
}
}
/// The `HX-Trigger-After-Settle` header.
///
/// Allows you to trigger client-side events after the settle step.
///
2023-10-22 20:45:48 +02:00
/// Will fail if the supplied events contain or produce characters that are not visible ASCII (32-127) when serializing to json.
2023-10-22 15:26:03 +02:00
#[ derive(Debug, Clone) ]
2023-10-23 20:43:03 +02:00
pub struct HxResponseTriggerAfterSettle ( pub Vec < String > ) ;
2023-10-22 15:26:03 +02:00
2023-10-23 20:43:03 +02:00
impl IntoResponseParts for HxResponseTriggerAfterSettle {
2023-10-22 15:26:03 +02:00
type Error = HxError ;
fn into_response_parts ( self , mut res : ResponseParts ) -> Result < ResponseParts , Self ::Error > {
res . headers_mut ( ) . insert (
headers ::HX_TRIGGER_AFTER_SETTLE ,
2023-10-22 20:45:48 +02:00
HeaderValue ::from_maybe_shared (
self . 0
. into_iter ( )
. reduce ( | acc , e | acc + " , " + & e )
. unwrap_or_default ( ) ,
) ? ,
2023-10-22 15:26:03 +02:00
) ;
Ok ( res )
}
}
/// The `HX-Trigger-After-Swap` header.
///
/// Allows you to trigger client-side events after the swap step.
///
2023-10-22 20:45:48 +02:00
/// Will fail if the supplied events contain or produce characters that are not visible ASCII (32-127) when serializing to json.
2023-10-22 15:26:03 +02:00
#[ derive(Debug, Clone) ]
2023-10-23 20:43:03 +02:00
pub struct HxResponseTriggerAfterSwap ( pub Vec < String > ) ;
2023-10-22 15:26:03 +02:00
2023-10-23 20:43:03 +02:00
impl IntoResponseParts for HxResponseTriggerAfterSwap {
2023-10-22 15:26:03 +02:00
type Error = HxError ;
fn into_response_parts ( self , mut res : ResponseParts ) -> Result < ResponseParts , Self ::Error > {
res . headers_mut ( ) . insert (
headers ::HX_TRIGGER_AFTER_SWAP ,
2023-10-22 20:45:48 +02:00
HeaderValue ::from_maybe_shared (
self . 0
. into_iter ( )
. reduce ( | acc , e | acc + " , " + & e )
. unwrap_or_default ( ) ,
) ? ,
2023-10-22 15:26:03 +02:00
) ;
Ok ( res )
}
}
/// Values of the `hx-swap` attribute.
2023-10-22 20:45:48 +02:00
// serde::Serialize is implemented in responders/serde.rs
2023-10-22 15:26:03 +02:00
#[ derive(Debug, Copy, Clone) ]
pub enum SwapOption {
/// Replace the inner html of the target element.
InnerHtml ,
/// Replace the entire target element with the response.
OuterHtml ,
/// Insert the response before the target element.
BeforeBegin ,
/// Insert the response before the first child of the target element.
AfterBegin ,
/// Insert the response after the last child of the target element
BeforeEnd ,
/// Insert the response after the target element
AfterEnd ,
/// Deletes the target element regardless of the response
Delete ,
/// Does not append content from response (out of band items will still be processed).
None ,
}
impl From < SwapOption > for HeaderValue {
fn from ( value : SwapOption ) -> Self {
match value {
SwapOption ::InnerHtml = > HeaderValue ::from_static ( HX_SWAP_INNER_HTML ) ,
SwapOption ::OuterHtml = > HeaderValue ::from_static ( HX_SWAP_OUTER_HTML ) ,
SwapOption ::BeforeBegin = > HeaderValue ::from_static ( HX_SWAP_BEFORE_BEGIN ) ,
SwapOption ::AfterBegin = > HeaderValue ::from_static ( HX_SWAP_AFTER_BEGIN ) ,
SwapOption ::BeforeEnd = > HeaderValue ::from_static ( HX_SWAP_BEFORE_END ) ,
SwapOption ::AfterEnd = > HeaderValue ::from_static ( HX_SWAP_AFTER_END ) ,
SwapOption ::Delete = > HeaderValue ::from_static ( HX_SWAP_DELETE ) ,
SwapOption ::None = > HeaderValue ::from_static ( HX_SWAP_NONE ) ,
}
}
}
pub enum HxError {
InvalidHeaderValue ( InvalidHeaderValue ) ,
2023-10-22 20:45:48 +02:00
#[ cfg(feature = " serde " ) ]
2023-10-22 15:26:03 +02:00
Serialization ( serde_json ::Error ) ,
}
impl From < InvalidHeaderValue > for HxError {
fn from ( value : InvalidHeaderValue ) -> Self {
Self ::InvalidHeaderValue ( value )
}
}
2023-10-22 20:45:48 +02:00
#[ cfg(feature = " serde " ) ]
2023-10-22 15:26:03 +02:00
impl From < serde_json ::Error > for HxError {
fn from ( value : serde_json ::Error ) -> Self {
Self ::Serialization ( value )
}
}
impl IntoResponse for HxError {
fn into_response ( self ) -> axum ::response ::Response {
match self {
Self ::InvalidHeaderValue ( _ ) = > {
( StatusCode ::INTERNAL_SERVER_ERROR , " invalid header value " ) . into_response ( )
}
2023-10-22 20:45:48 +02:00
#[ cfg(feature = " serde " ) ]
2023-10-22 15:26:03 +02:00
Self ::Serialization ( _ ) = > (
StatusCode ::INTERNAL_SERVER_ERROR ,
" failed to serialize event " ,
)
. into_response ( ) ,
}
}
}