mirror of
https://git.proxmox.com/git/proxmox
synced 2025-05-01 18:39:52 +00:00
macro: add wildcard matching to router macro
router!{ /path/{parameter}*: { methods... } } Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
8036941977
commit
b82b14d947
@ -41,6 +41,7 @@ pub fn router_macro(input: TokenStream) -> Result<TokenStream, Error> {
|
|||||||
pub enum SubRoute {
|
pub enum SubRoute {
|
||||||
Directories(HashMap<LitStr, Router>),
|
Directories(HashMap<LitStr, Router>),
|
||||||
Parameter(LitStr, Box<Router>),
|
Parameter(LitStr, Box<Router>),
|
||||||
|
Wildcard(LitStr),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubRoute {
|
impl SubRoute {
|
||||||
@ -80,8 +81,12 @@ enum Entry {
|
|||||||
enum Component {
|
enum Component {
|
||||||
/// This component is a fixed sub directory name. Eg. `foo` or `baz` in `/foo/{bar}/baz`.
|
/// This component is a fixed sub directory name. Eg. `foo` or `baz` in `/foo/{bar}/baz`.
|
||||||
Name(LitStr),
|
Name(LitStr),
|
||||||
|
|
||||||
/// This component matches everything into a parameter. Eg. `bar` in `/foo/{bar}/baz`.
|
/// This component matches everything into a parameter. Eg. `bar` in `/foo/{bar}/baz`.
|
||||||
Match(LitStr),
|
Match(LitStr),
|
||||||
|
|
||||||
|
/// Matches the rest of the path into a parameters
|
||||||
|
Wildcard(LitStr),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A path is just a list of components.
|
/// A path is just a list of components.
|
||||||
@ -91,7 +96,7 @@ impl Router {
|
|||||||
/// Insert a new router at a specific path.
|
/// Insert a new router at a specific path.
|
||||||
///
|
///
|
||||||
/// Note that this does not allow replacing an already existing router node.
|
/// Note that this does not allow replacing an already existing router node.
|
||||||
fn insert(&mut self, path: Path, router: Router) -> Result<(), Error> {
|
fn insert(&mut self, path: Path, mut router: Router) -> Result<(), Error> {
|
||||||
let mut at = self;
|
let mut at = self;
|
||||||
let mut created = false;
|
let mut created = false;
|
||||||
for component in path {
|
for component in path {
|
||||||
@ -106,9 +111,12 @@ impl Router {
|
|||||||
Router::default()
|
Router::default()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
SubRoute::Parameter(_param, _router) => {
|
SubRoute::Parameter(_, _) => {
|
||||||
bail!("subdir '{}' clashes with matched parameter", name.value());
|
bail!("subdir '{}' clashes with matched parameter", name.value());
|
||||||
}
|
}
|
||||||
|
SubRoute::Wildcard(_) => {
|
||||||
|
bail!("cannot add subdir '{}', it is already matched by a wildcard");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Component::Match(name) => {
|
Component::Match(name) => {
|
||||||
@ -117,12 +125,6 @@ impl Router {
|
|||||||
SubRoute::parameter(name.clone())
|
SubRoute::parameter(name.clone())
|
||||||
});
|
});
|
||||||
match subroute {
|
match subroute {
|
||||||
SubRoute::Directories(_) => {
|
|
||||||
bail!(
|
|
||||||
"parameter matcher '{}' clashes with existing directory",
|
|
||||||
name.value()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
SubRoute::Parameter(existing_name, router) => {
|
SubRoute::Parameter(existing_name, router) => {
|
||||||
if name != *existing_name {
|
if name != *existing_name {
|
||||||
bail!(
|
bail!(
|
||||||
@ -133,7 +135,26 @@ impl Router {
|
|||||||
}
|
}
|
||||||
at = router.as_mut();
|
at = router.as_mut();
|
||||||
}
|
}
|
||||||
|
SubRoute::Directories(_) => {
|
||||||
|
bail!(
|
||||||
|
"parameter matcher '{}' clashes with existing directory",
|
||||||
|
name.value()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
SubRoute::Wildcard(_) => {
|
||||||
|
bail!("parameter matcher '{}' clashes with wildcard", name.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Component::Wildcard(name) => {
|
||||||
|
if at.subroute.is_some() {
|
||||||
|
bail!("wildcard clashes with existing subdirectory");
|
||||||
|
}
|
||||||
|
created = true;
|
||||||
|
if router.subroute.is_some() {
|
||||||
|
bail!("wildcard sub router cannot have subdirectories!");
|
||||||
|
}
|
||||||
|
router.subroute = Some(SubRoute::Wildcard(name.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -188,6 +209,11 @@ impl Router {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some(SubRoute::Wildcard(name)) => {
|
||||||
|
out.extend(quote! {
|
||||||
|
.wildcard(#name)
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(name) = name {
|
if let Some(name) = name {
|
||||||
@ -364,6 +390,18 @@ fn parse_path_name(tokens: &mut TokenIter) -> Result<Path, Error> {
|
|||||||
push_component(&mut path, &mut component, &mut span);
|
push_component(&mut path, &mut component, &mut span);
|
||||||
// `component` is cleared, we start the next one
|
// `component` is cleared, we start the next one
|
||||||
}
|
}
|
||||||
|
'*' => {
|
||||||
|
// must be the last component, after a matcher
|
||||||
|
if !component.is_empty() {
|
||||||
|
bail!("wildcard must be the final matcher");
|
||||||
|
}
|
||||||
|
if let Some(Component::Match(name)) = path.pop() {
|
||||||
|
path.push(Component::Wildcard(name));
|
||||||
|
match_colon(&mut *tokens)?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bail!("asterisk only allowed at the end of a match pattern");
|
||||||
|
}
|
||||||
other => bail!("invalid punctuation in path: {:?}", other),
|
other => bail!("invalid punctuation in path: {:?}", other),
|
||||||
},
|
},
|
||||||
Some(other) => bail!(
|
Some(other) => bail!(
|
||||||
|
@ -90,6 +90,8 @@ proxmox_api_macro::router! {
|
|||||||
|
|
||||||
/dir: { GET: non_async_test },
|
/dir: { GET: non_async_test },
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/wild/{param}*: { GET: get_loopback },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,6 +130,12 @@ fn router() {
|
|||||||
check_body(&TEST_ROUTER, "/another/foo", r#"{"data":"foo"}"#);
|
check_body(&TEST_ROUTER, "/another/foo", r#"{"data":"foo"}"#);
|
||||||
check_body(&TEST_ROUTER, "/another/foo/dir", r#"{"data":"foo"}"#);
|
check_body(&TEST_ROUTER, "/another/foo/dir", r#"{"data":"foo"}"#);
|
||||||
|
|
||||||
|
check_body(&TEST_ROUTER, "/wild", r#"{"data":""}"#);
|
||||||
|
check_body(&TEST_ROUTER, "/wild/", r#"{"data":""}"#);
|
||||||
|
check_body(&TEST_ROUTER, "/wild/asdf", r#"{"data":"asdf"}"#);
|
||||||
|
check_body(&TEST_ROUTER, "/wild//asdf", r#"{"data":"asdf"}"#);
|
||||||
|
check_body(&TEST_ROUTER, "/wild/asdf/poiu", r#"{"data":"asdf/poiu"}"#);
|
||||||
|
|
||||||
// And can I...
|
// And can I...
|
||||||
let res = futures::executor::block_on(get_loopback("FOO".to_string()))
|
let res = futures::executor::block_on(get_loopback("FOO".to_string()))
|
||||||
.expect("expected result from get_loopback");
|
.expect("expected result from get_loopback");
|
||||||
|
Loading…
Reference in New Issue
Block a user