#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
pub enum RouteMethod {
Get,
Post,
Put,
Patch,
Delete,
Head,
Options,
}
impl RouteMethod {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Get => "GET",
Self::Post => "POST",
Self::Put => "PUT",
Self::Patch => "PATCH",
Self::Delete => "DELETE",
Self::Head => "HEAD",
Self::Options => "OPTIONS",
}
}
#[must_use]
pub const fn as_routing_fn(self) -> &'static str {
match self {
Self::Get => "get",
Self::Post => "post",
Self::Put => "put",
Self::Patch => "patch",
Self::Delete => "delete",
Self::Head => "head",
Self::Options => "options",
}
}
}
impl std::fmt::Display for RouteMethod {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct RouteDescriptor {
pub method: RouteMethod,
pub path: &'static str,
pub name: &'static str,
pub handler: &'static str,
#[cfg_attr(feature = "serde", serde(default))]
pub pages: &'static [&'static str],
#[cfg_attr(feature = "serde", serde(default))]
pub action_fields: &'static [super::field_metadata::FieldShape],
#[cfg_attr(feature = "serde", serde(default))]
pub action_type: &'static str,
#[cfg_attr(feature = "serde", serde(default))]
pub query_fields: &'static [super::field_metadata::FieldShape],
#[cfg_attr(feature = "serde", serde(default))]
pub query_type: &'static str,
#[cfg_attr(feature = "serde", serde(default))]
pub query_array: bool,
#[cfg_attr(feature = "serde", serde(default))]
pub query_string_fields: &'static [super::field_metadata::FieldShape],
#[cfg_attr(feature = "serde", serde(default))]
pub query_string_type: &'static str,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ResourceAction {
Index,
Create,
Store,
Show,
Edit,
Update,
Destroy,
}
#[allow(dead_code)] impl ResourceAction {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Index => "index",
Self::Create => "create",
Self::Store => "store",
Self::Show => "show",
Self::Edit => "edit",
Self::Update => "update",
Self::Destroy => "destroy",
}
}
#[must_use]
pub const fn method(self) -> RouteMethod {
match self {
Self::Index | Self::Create | Self::Show | Self::Edit => RouteMethod::Get,
Self::Store => RouteMethod::Post,
Self::Update => RouteMethod::Put,
Self::Destroy => RouteMethod::Delete,
}
}
#[must_use]
pub fn path_suffix(self, param: &str) -> String {
match self {
Self::Index | Self::Store => String::new(),
Self::Create => "/new".to_string(),
Self::Show | Self::Update | Self::Destroy => format!("/{{{param}}}"),
Self::Edit => format!("/{{{param}}}/edit"),
}
}
}
impl std::fmt::Display for ResourceAction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[must_use]
#[allow(dead_code)] pub fn parse_resource_action(name: &str) -> Option<ResourceAction> {
match name {
"index" => Some(ResourceAction::Index),
"create" => Some(ResourceAction::Create),
"store" => Some(ResourceAction::Store),
"show" => Some(ResourceAction::Show),
"edit" => Some(ResourceAction::Edit),
"update" => Some(ResourceAction::Update),
"destroy" => Some(ResourceAction::Destroy),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn route_method_display() {
assert_eq!(RouteMethod::Get.to_string(), "GET");
assert_eq!(RouteMethod::Post.to_string(), "POST");
assert_eq!(RouteMethod::Delete.to_string(), "DELETE");
}
#[test]
#[cfg(feature = "serde")]
fn route_descriptor_serializes_to_json() {
let descriptor = RouteDescriptor {
method: RouteMethod::Get,
path: "/links/{link}",
name: "links.show",
handler: "LinksController::show",
pages: &["Show"],
action_fields: &[],
action_type: "",
query_fields: &[],
query_type: "",
query_array: false,
query_string_fields: &[],
query_string_type: "",
};
let json = serde_json::to_string(&descriptor).unwrap();
assert!(
json.contains("\"method\":\"get\""),
"method lowercase: {json}"
);
assert!(json.contains("\"path\":\"/links/{link}\""), "path: {json}");
assert!(json.contains("\"name\":\"links.show\""), "name: {json}");
assert!(
json.contains("\"handler\":\"LinksController::show\""),
"handler: {json}"
);
assert!(
json.contains("\"pages\":[\"Show\"]"),
"route→page edge serialized: {json}"
);
}
#[test]
fn route_method_routing_fn() {
assert_eq!(RouteMethod::Get.as_routing_fn(), "get");
assert_eq!(RouteMethod::Patch.as_routing_fn(), "patch");
}
#[test]
fn resource_action_method_mapping() {
assert_eq!(ResourceAction::Index.method(), RouteMethod::Get);
assert_eq!(ResourceAction::Store.method(), RouteMethod::Post);
assert_eq!(ResourceAction::Update.method(), RouteMethod::Put);
assert_eq!(ResourceAction::Destroy.method(), RouteMethod::Delete);
}
#[test]
fn parse_known_actions() {
assert_eq!(parse_resource_action("index"), Some(ResourceAction::Index));
assert_eq!(
parse_resource_action("destroy"),
Some(ResourceAction::Destroy)
);
assert_eq!(parse_resource_action("unknown"), None);
}
}