diff --git a/src/config.rs b/src/config.rs index a4be7fa..791ca74 100644 --- a/src/config.rs +++ b/src/config.rs @@ -38,6 +38,8 @@ pub struct Route { pub path: String, pub hidden: bool, pub description: Option, + pub min_args: Option, + pub max_args: Option, } 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: ~" ); } } diff --git a/src/routes.rs b/src/routes.rs index edce7ba..4058aed 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -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, default_route: &Option, -) -> (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::>().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::>().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 ); }