Use enum for route resolution
This commit is contained in:
parent
abbd1d9fea
commit
7fdf451470
2 changed files with 53 additions and 18 deletions
|
@ -38,6 +38,8 @@ pub struct Route {
|
|||
pub path: String,
|
||||
pub hidden: bool,
|
||||
pub description: Option<String>,
|
||||
pub min_args: Option<usize>,
|
||||
pub max_args: Option<usize>,
|
||||
}
|
||||
|
||||
impl FromStr for Route {
|
||||
|
@ -48,6 +50,8 @@ impl FromStr for Route {
|
|||
path: s.to_string(),
|
||||
hidden: false,
|
||||
description: None,
|
||||
min_args: None,
|
||||
max_args: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +72,8 @@ impl<'de> Deserialize<'de> for Route {
|
|||
Path,
|
||||
Hidden,
|
||||
Description,
|
||||
MinArgs,
|
||||
MaxArgs,
|
||||
}
|
||||
|
||||
struct RouteVisitor;
|
||||
|
@ -83,7 +89,7 @@ impl<'de> Deserialize<'de> for Route {
|
|||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
// This is infalliable
|
||||
// This is infallable
|
||||
Ok(Self::Value::from_str(path).unwrap())
|
||||
}
|
||||
|
||||
|
@ -94,6 +100,8 @@ impl<'de> Deserialize<'de> for Route {
|
|||
let mut path = None;
|
||||
let mut hidden = None;
|
||||
let mut description = None;
|
||||
let mut min_args = None;
|
||||
let mut max_args = None;
|
||||
|
||||
while let Some(key) = map.next_key()? {
|
||||
match key {
|
||||
|
@ -115,6 +123,18 @@ impl<'de> Deserialize<'de> for Route {
|
|||
}
|
||||
description = Some(map.next_value()?);
|
||||
}
|
||||
Field::MinArgs => {
|
||||
if min_args.is_some() {
|
||||
return Err(de::Error::duplicate_field("min_args"));
|
||||
}
|
||||
min_args = Some(map.next_value()?);
|
||||
}
|
||||
Field::MaxArgs => {
|
||||
if max_args.is_some() {
|
||||
return Err(de::Error::duplicate_field("max_args"));
|
||||
}
|
||||
max_args = Some(map.next_value()?);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,6 +144,8 @@ impl<'de> Deserialize<'de> for Route {
|
|||
path,
|
||||
hidden: hidden.unwrap_or_default(),
|
||||
description,
|
||||
min_args,
|
||||
max_args,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -337,7 +359,7 @@ mod route {
|
|||
fn serialize() {
|
||||
assert_eq!(
|
||||
&to_string(&Route::from_str("hello world").unwrap()).unwrap(),
|
||||
"---\nroute_type: External\npath: hello world\nhidden: false\ndescription: ~"
|
||||
"---\nroute_type: External\npath: hello world\nhidden: false\ndescription: ~\nmin_args: ~\nmax_args: ~"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ pub async fn hop(
|
|||
let data = data.read().unwrap();
|
||||
|
||||
match resolve_hop(&query.to, &data.routes, &data.default_route) {
|
||||
(Some(path), args) => {
|
||||
RouteResolution::Resolved { route: path, args } => {
|
||||
let resolved_template = match path {
|
||||
ConfigRoute {
|
||||
route_type: RouteType::Internal,
|
||||
|
@ -126,10 +126,16 @@ pub async fn hop(
|
|||
}
|
||||
}
|
||||
}
|
||||
(None, _) => HttpResponse::NotFound().body("not found"),
|
||||
RouteResolution::Unresolved => HttpResponse::NotFound().body("not found"),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum RouteResolution<'a> {
|
||||
Resolved { route: &'a Route, args: String },
|
||||
Unresolved,
|
||||
}
|
||||
|
||||
/// Attempts to resolve the provided string into its route and its arguments.
|
||||
/// If a default route was provided, then this will consider that route before
|
||||
/// failing to resolve a route.
|
||||
|
@ -140,21 +146,20 @@ fn resolve_hop<'a>(
|
|||
query: &str,
|
||||
routes: &'a HashMap<String, Route>,
|
||||
default_route: &Option<String>,
|
||||
) -> (Option<&'a Route>, String) {
|
||||
) -> RouteResolution<'a> {
|
||||
let mut split_args = query.split_ascii_whitespace().peekable();
|
||||
let command = match split_args.peek() {
|
||||
Some(command) => command,
|
||||
None => {
|
||||
debug!("Found empty query, returning no route.");
|
||||
return (None, String::new());
|
||||
return RouteResolution::Unresolved;
|
||||
}
|
||||
};
|
||||
|
||||
match (routes.get(*command), default_route) {
|
||||
// Found a route
|
||||
(Some(resolved), _) => (
|
||||
Some(resolved),
|
||||
match split_args.next() {
|
||||
(Some(resolved), _) => {
|
||||
let args = match split_args.next() {
|
||||
// Discard the first result, we found the route using the first arg
|
||||
Some(_) => {
|
||||
let args = split_args.collect::<Vec<&str>>().join(" ");
|
||||
|
@ -165,21 +170,26 @@ fn resolve_hop<'a>(
|
|||
debug!("Resolved {} with no args", resolved);
|
||||
String::new()
|
||||
}
|
||||
},
|
||||
),
|
||||
};
|
||||
|
||||
RouteResolution::Resolved {
|
||||
route: resolved,
|
||||
args,
|
||||
}
|
||||
}
|
||||
// Unable to find route, but had a default route
|
||||
(None, Some(route)) => {
|
||||
let args = split_args.collect::<Vec<&str>>().join(" ");
|
||||
debug!("Using default route {} with args {}", route, args);
|
||||
match routes.get(route) {
|
||||
Some(v) => (Some(v), args),
|
||||
None => (None, String::new()),
|
||||
Some(route) => RouteResolution::Resolved { route, args },
|
||||
None => RouteResolution::Unresolved,
|
||||
}
|
||||
}
|
||||
// No default route and no match
|
||||
(None, None) => {
|
||||
debug!("Failed to resolve route!");
|
||||
(None, String::new())
|
||||
RouteResolution::Unresolved
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -211,15 +221,18 @@ mod resolve_hop {
|
|||
fn generate_route_result<'a>(
|
||||
keyword: &'a Route,
|
||||
args: &str,
|
||||
) -> (Option<&'a Route>, String) {
|
||||
(Some(keyword), String::from(args))
|
||||
) -> RouteResolution<'a> {
|
||||
RouteResolution::Resolved {
|
||||
route: keyword,
|
||||
args: String::from(args),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_routes_no_default_yields_failed_hop() {
|
||||
assert_eq!(
|
||||
resolve_hop("hello world", &HashMap::new(), &None),
|
||||
(None, String::new())
|
||||
RouteResolution::Unresolved
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -231,7 +244,7 @@ mod resolve_hop {
|
|||
&HashMap::new(),
|
||||
&Some(String::from("google"))
|
||||
),
|
||||
(None, String::new())
|
||||
RouteResolution::Unresolved
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue