#![allow(non_snake_case)]
use dioxus_core::Element;
use std::iter::FlatMap;
use std::slice::Iter;
use std::{fmt::Display, str::FromStr};
#[derive(Debug, PartialEq)]
pub struct RouteParseError<E: Display> {
pub attempted_routes: Vec<E>,
}
impl<E: Display> Display for RouteParseError<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Route did not match:\nAttempted Matches:\n")?;
for (i, route) in self.attempted_routes.iter().enumerate() {
writeln!(f, "{}) {route}", i + 1)?;
}
Ok(())
}
}
#[rustversion::attr(
since(1.78.0),
diagnostic::on_unimplemented(
message = "`FromQuery` is not implemented for `{Self}`",
label = "spread query",
note = "FromQuery is automatically implemented for types that implement `From<&str>`. You need to either implement From<&str> or implement FromQuery manually."
)
)]
pub trait FromQuery {
fn from_query(query: &str) -> Self;
}
impl<T: for<'a> From<&'a str>> FromQuery for T {
fn from_query(query: &str) -> Self {
T::from(query)
}
}
#[rustversion::attr(
since(1.78.0),
diagnostic::on_unimplemented(
message = "`FromQueryArgument` is not implemented for `{Self}`",
label = "query argument",
note = "FromQueryArgument is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromQueryArgument manually."
)
)]
pub trait FromQueryArgument<P = ()>: Default {
type Err;
fn from_query_argument(argument: &str) -> Result<Self, Self::Err>;
}
impl<T: Default + FromStr> FromQueryArgument for T
where
<T as FromStr>::Err: Display,
{
type Err = <T as FromStr>::Err;
fn from_query_argument(argument: &str) -> Result<Self, Self::Err> {
match T::from_str(argument) {
Ok(result) => Ok(result),
Err(err) => {
tracing::error!("Failed to parse query argument: {}", err);
Err(err)
}
}
}
}
pub struct OptionMarker;
impl<T: Default + FromStr> FromQueryArgument<OptionMarker> for Option<T>
where
<T as FromStr>::Err: Display,
{
type Err = <T as FromStr>::Err;
fn from_query_argument(argument: &str) -> Result<Self, Self::Err> {
match T::from_str(argument) {
Ok(result) => Ok(Some(result)),
Err(err) => {
tracing::error!("Failed to parse query argument: {}", err);
Err(err)
}
}
}
}
pub trait ToQueryArgument<T = ()> {
fn display_query_argument(
&self,
query_name: &str,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result;
}
impl<T> ToQueryArgument for T
where
T: Display,
{
fn display_query_argument(
&self,
query_name: &str,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
write!(f, "{}={}", query_name, self)
}
}
impl<T: Display> ToQueryArgument<OptionMarker> for Option<T> {
fn display_query_argument(
&self,
query_name: &str,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
if let Some(value) = self {
write!(f, "{}={}", query_name, value)
} else {
Ok(())
}
}
}
pub struct DisplayQueryArgument<'a, T, M = ()> {
query_name: &'a str,
value: &'a T,
_marker: std::marker::PhantomData<M>,
}
impl<'a, T, M> DisplayQueryArgument<'a, T, M> {
pub fn new(query_name: &'a str, value: &'a T) -> Self {
Self {
query_name,
value,
_marker: std::marker::PhantomData,
}
}
}
impl<T: ToQueryArgument<M>, M> Display for DisplayQueryArgument<'_, T, M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.value.display_query_argument(self.query_name, f)
}
}
#[rustversion::attr(
since(1.78.0),
diagnostic::on_unimplemented(
message = "`FromHashFragment` is not implemented for `{Self}`",
label = "hash fragment",
note = "FromHashFragment is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromHashFragment manually."
)
)]
pub trait FromHashFragment {
fn from_hash_fragment(hash: &str) -> Self;
}
impl<T> FromHashFragment for T
where
T: FromStr + Default,
T::Err: std::fmt::Display,
{
fn from_hash_fragment(hash: &str) -> Self {
match T::from_str(hash) {
Ok(value) => value,
Err(err) => {
tracing::error!("Failed to parse hash fragment: {}", err);
Default::default()
}
}
}
}
#[rustversion::attr(
since(1.78.0),
diagnostic::on_unimplemented(
message = "`FromRouteSegment` is not implemented for `{Self}`",
label = "route segment",
note = "FromRouteSegment is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromRouteSegment manually."
)
)]
pub trait FromRouteSegment: Sized {
type Err;
fn from_route_segment(route: &str) -> Result<Self, Self::Err>;
}
impl<T: FromStr> FromRouteSegment for T
where
<T as FromStr>::Err: Display,
{
type Err = <T as FromStr>::Err;
fn from_route_segment(route: &str) -> Result<Self, Self::Err> {
T::from_str(route)
}
}
#[test]
fn full_circle() {
let route = "testing 1234 hello world";
assert_eq!(String::from_route_segment(route).unwrap(), route);
}
pub trait ToRouteSegments {
fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
}
impl<I, T: Display> ToRouteSegments for I
where
for<'a> &'a I: IntoIterator<Item = &'a T>,
{
fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for segment in self {
write!(f, "/")?;
let segment = segment.to_string();
let encoded =
percent_encoding::utf8_percent_encode(&segment, crate::query_sets::PATH_ASCII_SET);
write!(f, "{}", encoded)?;
}
Ok(())
}
}
#[test]
fn to_route_segments() {
struct DisplaysRoute;
impl Display for DisplaysRoute {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let segments = vec!["hello", "world"];
segments.display_route_segments(f)
}
}
assert_eq!(DisplaysRoute.to_string(), "/hello/world");
}
#[rustversion::attr(
since(1.78.0),
diagnostic::on_unimplemented(
message = "`FromRouteSegments` is not implemented for `{Self}`",
label = "spread route segments",
note = "FromRouteSegments is automatically implemented for types that implement `FromIterator` with an `Item` type that implements `Display`. You need to either implement FromIterator or implement FromRouteSegments manually."
)
)]
pub trait FromRouteSegments: Sized {
type Err: std::fmt::Display;
fn from_route_segments(segments: &[&str]) -> Result<Self, Self::Err>;
}
impl<I: std::iter::FromIterator<String>> FromRouteSegments for I {
type Err = <String as FromRouteSegment>::Err;
fn from_route_segments(segments: &[&str]) -> Result<Self, Self::Err> {
segments
.iter()
.map(|s| String::from_route_segment(s))
.collect()
}
}
type SiteMapFlattened<'a> = FlatMap<
Iter<'a, SiteMapSegment>,
Vec<Vec<SegmentType>>,
fn(&SiteMapSegment) -> Vec<Vec<SegmentType>>,
>;
#[rustversion::attr(
since(1.78.0),
diagnostic::on_unimplemented(
message = "`Routable` is not implemented for `{Self}`",
label = "Route",
note = "Routable should generally be derived using the `#[derive(Routable)]` macro."
)
)]
pub trait Routable: FromStr<Err: Display> + Display + Clone + 'static {
const SITE_MAP: &'static [SiteMapSegment];
fn render(&self, level: usize) -> Element;
fn is_child_of(&self, other: &Self) -> bool {
let self_str = self.to_string();
let self_str = self_str
.split_once('#')
.map(|(route, _)| route)
.unwrap_or(&self_str);
let self_str = self_str
.split_once('?')
.map(|(route, _)| route)
.unwrap_or(self_str);
let self_str = self_str.trim_end_matches('/');
let other_str = other.to_string();
let other_str = other_str
.split_once('#')
.map(|(route, _)| route)
.unwrap_or(&other_str);
let other_str = other_str
.split_once('?')
.map(|(route, _)| route)
.unwrap_or(other_str);
let other_str = other_str.trim_end_matches('/');
let mut self_segments = self_str.split('/');
let mut other_segments = other_str.split('/');
loop {
match (self_segments.next(), other_segments.next()) {
(None, Some(_)) | (None, None) => {
return false;
}
(Some(self_seg), Some(other_seg)) => {
if self_seg != other_seg {
return false;
}
}
(Some(_), None) => break,
}
}
true
}
fn parent(&self) -> Option<Self> {
let as_str = self.to_string();
let (route_and_query, _) = as_str.split_once('#').unwrap_or((&as_str, ""));
let (route, _) = route_and_query
.split_once('?')
.unwrap_or((route_and_query, ""));
let route = route.trim_end_matches('/');
let segments = route.split_inclusive('/');
let segment_count = segments.clone().count();
let new_route: String = segments.take(segment_count.saturating_sub(1)).collect();
Self::from_str(&new_route).ok()
}
fn flatten_site_map<'a>() -> SiteMapFlattened<'a> {
Self::SITE_MAP.iter().flat_map(SiteMapSegment::flatten)
}
fn static_routes() -> Vec<Self> {
Self::flatten_site_map()
.filter_map(|segments| {
let mut route = String::new();
for segment in segments.iter() {
match segment {
SegmentType::Static(s) => {
route.push('/');
route.push_str(s)
}
SegmentType::Child => {}
_ => return None,
}
}
route.parse().ok()
})
.collect()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SiteMapSegment {
pub segment_type: SegmentType,
pub children: &'static [SiteMapSegment],
}
impl SiteMapSegment {
pub fn flatten(&self) -> Vec<Vec<SegmentType>> {
let mut routes = Vec::new();
self.flatten_inner(&mut routes, Vec::new());
routes
}
fn flatten_inner(&self, routes: &mut Vec<Vec<SegmentType>>, current: Vec<SegmentType>) {
let mut current = current;
current.push(self.segment_type.clone());
if self.children.is_empty() {
routes.push(current);
} else {
for child in self.children {
child.flatten_inner(routes, current.clone());
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum SegmentType {
Static(&'static str),
Dynamic(&'static str),
CatchAll(&'static str),
Child,
}
impl SegmentType {
pub fn to_static(&self) -> Option<&'static str> {
match self {
SegmentType::Static(s) => Some(*s),
_ => None,
}
}
pub fn to_dynamic(&self) -> Option<&'static str> {
match self {
SegmentType::Dynamic(s) => Some(*s),
_ => None,
}
}
pub fn to_catch_all(&self) -> Option<&'static str> {
match self {
SegmentType::CatchAll(s) => Some(*s),
_ => None,
}
}
pub fn to_child(&self) -> Option<()> {
match self {
SegmentType::Child => Some(()),
_ => None,
}
}
}
impl Display for SegmentType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self {
SegmentType::Static(s) => write!(f, "/{}", s),
SegmentType::Child => Ok(()),
SegmentType::Dynamic(s) => write!(f, "/:{}", s),
SegmentType::CatchAll(s) => write!(f, "/:..{}", s),
}
}
}