Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ any of your responses.

| Header | Responder | Value |
|---------------------------|---------------------|-------------------------------------|
| `HX-Location` | `HxLocation` | `axum::http::Uri` |
| `HX-Push-Url` | `HxPushUrl` | `axum::http::Uri` |
| `HX-Redirect` | `HxRedirect` | `axum::http::Uri` |
| `HX-Location` | `HxLocation` | `String` |
| `HX-Push-Url` | `HxPushUrl` | `String` |
| `HX-Redirect` | `HxRedirect` | `String` |
| `HX-Refresh` | `HxRefresh` | `bool` |
| `HX-Replace-Url` | `HxReplaceUrl` | `axum::http::Uri` |
| `HX-Replace-Url` | `HxReplaceUrl` | `String` |
| `HX-Reswap` | `HxReswap` | `axum_htmx::responders::SwapOption` |
| `HX-Retarget` | `HxRetarget` | `String` |
| `HX-Reselect` | `HxReselect` | `String` |
Expand Down
58 changes: 17 additions & 41 deletions src/responders.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//! Axum responses for htmx response headers.

use std::{convert::Infallible, str::FromStr};
use std::convert::Infallible;

use axum_core::response::{IntoResponseParts, ResponseParts};
use http::{HeaderValue, Uri};
use http::HeaderValue;

use crate::{headers, HxError};

Expand Down Expand Up @@ -32,32 +32,24 @@ const HX_SWAP_NONE: &str = "none";
///
/// See <https://htmx.org/headers/hx-push-url/> for more information.
#[derive(Debug, Clone)]
pub struct HxPushUrl(pub Uri);
pub struct HxPushUrl(pub String);

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())?,
HeaderValue::from_maybe_shared(self.0)?,
);

Ok(res)
}
}

impl From<Uri> for HxPushUrl {
fn from(uri: Uri) -> Self {
Self(uri)
}
}

impl<'a> TryFrom<&'a str> for HxPushUrl {
type Error = <Uri as FromStr>::Err;

fn try_from(value: &'a str) -> Result<Self, Self::Error> {
Ok(Self(value.parse()?))
impl<'a> From<&'a str> for HxPushUrl {
fn from(value: &'a str) -> Self {
Self(value.to_string())
}
}

Expand All @@ -68,32 +60,24 @@ impl<'a> TryFrom<&'a str> for HxPushUrl {
/// Will fail if the supplied Uri contains characters that are not visible ASCII
/// (32-127).
#[derive(Debug, Clone)]
pub struct HxRedirect(pub Uri);
pub struct HxRedirect(pub String);

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())?,
HeaderValue::from_maybe_shared(self.0)?,
);

Ok(res)
}
}

impl From<Uri> for HxRedirect {
fn from(uri: Uri) -> Self {
Self(uri)
}
}

impl<'a> TryFrom<&'a str> for HxRedirect {
type Error = <Uri as FromStr>::Err;

fn try_from(value: &'a str) -> Result<Self, Self::Error> {
Ok(Self(value.parse()?))
impl<'a> From<&'a str> for HxRedirect {
fn from(value: &'a str) -> Self {
Self(value.to_string())
}
}

Expand Down Expand Up @@ -137,32 +121,24 @@ impl IntoResponseParts for HxRefresh {
///
/// See <https://htmx.org/headers/hx-replace-url/> for more information.
#[derive(Debug, Clone)]
pub struct HxReplaceUrl(pub Uri);
pub struct HxReplaceUrl(pub String);

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())?,
HeaderValue::from_maybe_shared(self.0)?,
);

Ok(res)
}
}

impl From<Uri> for HxReplaceUrl {
fn from(uri: Uri) -> Self {
Self(uri)
}
}

impl<'a> TryFrom<&'a str> for HxReplaceUrl {
type Error = <Uri as FromStr>::Err;

fn try_from(value: &'a str) -> Result<Self, Self::Error> {
Ok(Self(value.parse()?))
impl<'a> From<&'a str> for HxReplaceUrl {
fn from(value: &'a str) -> Self {
Self(value.to_string())
}
}

Expand Down
75 changes: 18 additions & 57 deletions src/responders/location.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use std::str::FromStr;

use axum_core::response::{IntoResponseParts, ResponseParts};
use http::{HeaderValue, Uri};
use http::HeaderValue;

use crate::{headers, HxError};

Expand All @@ -12,58 +10,39 @@ use crate::{headers, HxError};
/// target on the page, you must enable the `serde` feature flag and specify
/// [`LocationOptions`].
///
/// Will fail if the supplied Uri contains characters that are not visible ASCII
/// Will fail if the supplied uri contains characters that are not visible ASCII
/// (32-127).
///
/// See <https://htmx.org/headers/hx-location/> for more information.
#[derive(Debug, Clone)]
pub struct HxLocation {
/// Uri of the new location.
pub uri: Uri,
pub uri: String,
/// Extra options.
#[cfg(feature = "serde")]
#[cfg_attr(feature = "unstable", doc(cfg(feature = "serde")))]
pub options: LocationOptions,
}

impl HxLocation {
/// Creates location from [`Uri`] without any options.
pub fn from_uri(uri: Uri) -> Self {
Self {
#[cfg(feature = "serde")]
options: LocationOptions::default(),
uri,
}
}

/// Creates location from [`Uri`] and options.
#[cfg(feature = "serde")]
#[cfg_attr(feature = "unstable", doc(cfg(feature = "serde")))]
pub fn from_uri_with_options(uri: Uri, options: LocationOptions) -> Self {
Self { uri, options }
}

/// Parses `uri` and sets it as location.
#[allow(clippy::should_implement_trait)]
pub fn from_str(uri: impl AsRef<str>) -> Result<Self, http::uri::InvalidUri> {
Ok(Self {
pub fn from_str(uri: impl AsRef<str>) -> Self {
Self {
#[cfg(feature = "serde")]
options: LocationOptions::default(),
uri: uri.as_ref().parse::<Uri>()?,
})
uri: uri.as_ref().to_string(),
}
}

/// Parses `uri` and sets it as location with additional options.
#[cfg(feature = "serde")]
#[cfg_attr(feature = "unstable", doc(cfg(feature = "serde")))]
pub fn from_str_with_options(
uri: impl AsRef<str>,
options: LocationOptions,
) -> Result<Self, http::uri::InvalidUri> {
Ok(Self {
pub fn from_str_with_options(uri: impl AsRef<str>, options: LocationOptions) -> Self {
Self {
options,
uri: uri.as_ref().parse::<Uri>()?,
})
uri: uri.as_ref().to_string(),
}
}

#[cfg(feature = "serde")]
Expand All @@ -88,34 +67,16 @@ impl HxLocation {
}
}

impl From<Uri> for HxLocation {
fn from(uri: Uri) -> Self {
Self::from_uri(uri)
}
}

#[cfg(feature = "serde")]
#[cfg_attr(feature = "unstable", doc(cfg(feature = "serde")))]
impl From<(Uri, LocationOptions)> for HxLocation {
fn from((uri, options): (Uri, LocationOptions)) -> Self {
Self::from_uri_with_options(uri, options)
}
}

impl<'a> TryFrom<&'a str> for HxLocation {
type Error = <Uri as FromStr>::Err;

fn try_from(uri: &'a str) -> Result<Self, Self::Error> {
impl<'a> From<&'a str> for HxLocation {
fn from(uri: &'a str) -> Self {
Self::from_str(uri)
}
}

#[cfg(feature = "serde")]
#[cfg_attr(feature = "unstable", doc(cfg(feature = "serde")))]
impl<'a> TryFrom<(&'a str, LocationOptions)> for HxLocation {
type Error = <Uri as FromStr>::Err;

fn try_from((uri, options): (&'a str, LocationOptions)) -> Result<Self, Self::Error> {
impl<'a> From<(&'a str, LocationOptions)> for HxLocation {
fn from((uri, options): (&'a str, LocationOptions)) -> Self {
Self::from_str_with_options(uri, options)
}
}
Expand Down Expand Up @@ -207,11 +168,11 @@ mod tests {
fn test_serialize_location() {
use crate::SwapOption;

let loc = HxLocation::try_from("/foo").unwrap();
let loc = HxLocation::from("/foo");
assert_eq!(loc.into_header_with_options().unwrap(), "/foo");

let loc = HxLocation::from_uri_with_options(
"/foo".parse().unwrap(),
let loc = HxLocation::from_str_with_options(
"/foo",
LocationOptions {
event: Some("click".into()),
swap: Some(SwapOption::InnerHtml),
Expand Down
Loading