Rouille hello world example显示了如何将router!
宏用于固定路线集。
以下示例代码说明了需要能够从数据库或可插入代码“引导”路由 - 我目前可以使用Iron Web框架执行此操作:
pub struct Route {
pub http_method: String,
pub url_path: String,
pub callback_func: fn(_: &mut Request) -> IronResult<Response>,
}
impl Route {
pub fn new(m: String, u: String, f: fn(_: &mut Request) -> IronResult<Response>) -> Route {
Route {
http_method: m,
url_path: u,
callback_func: f,
}
}
}
fn main() {
// router is an Iron middleware
let mut router = Router::new();
// prepare routes for bootstrapping into the Iron router
let r1 = Route::new("get".to_string(), "/*".to_string(), my_callback_func);
let r2 = Route::new("get".to_string(), "/".to_string(), my_callback_func);
let mut routes = Vec::new();
routes.push(r1);
routes.push(r2);
for route in routes.iter_mut() {
if route.http_method == "get" {
// passes each route to the Iron router
router.get(&route.url_path, (&*route).callback_func);
} // else if, put, post, delete...
}
Iron::new(router).http("localhost:3000").unwrap();
}
fn my_callback_func(_: &mut Request) -> IronResult<Response> {
//...
}
虽然我正在阅读Rust中的宏,但我对Rouille's router!
macro,Rust或宏一般都没有足够的了解,以弄清楚如何用Rouille实现等效。
答案 0 :(得分:2)
如果你检查the main source of the router
macro,那就太长了,但并不是非常复杂:
($request:expr, $(($method:ident) ($($pat:tt)+) => $value:block,)* _ => $def:expr) => {
{
let request = &$request;
// ignoring the GET parameters (everything after `?`)
let request_url = request.url();
let request_url = {
let pos = request_url.find('?').unwrap_or(request_url.len());
&request_url[..pos]
};
let mut ret = None;
$({
if ret.is_none() && request.method() == stringify!($method) {
ret = router!(__check_pattern request_url $value $($pat)+);
}
})+
if let Some(ret) = ret {
ret
} else {
$def
}
}
};
简而言之,它需要一个请求,零或多个模式匹配,以及默认值。它获取了URL,然后调度到宏的其他部分,以查看URL是否与路径匹配,并使用一些递归技巧来定义一些带有路径组件的变量。无论哪个臂首先匹配都将设置返回值,如果没有匹配,将使用默认值。
不幸的是,宏期望方法和路径ident
,所以基本上你不能将它与表达式一起使用。这意味着我们无法传递像"foo"
这样的变量或文字。这使你很难。
因此,我们会做所有优秀程序员所做的事情:复制并粘贴代码。从宏中提取块并重新调整它们:
#[macro_use]
extern crate rouille;
use rouille::Request;
use rouille::Response;
struct Route(&'static str, &'static str, fn(&Request) -> Response);
fn main() {
let routes = [
Route("GET", "/one", do_one),
Route("GET", "/two", do_two),
];
rouille::start_server("0.0.0.0:9080", move |request| {
let mut result = None;
let request = &request;
// ignoring the GET parameters (everything after `?`)
let request_url = request.url();
let request_url = {
let pos = request_url.find('?').unwrap_or(request_url.len());
&request_url[..pos]
};
for &Route(method, path, f) in &routes {
if result.is_none() {
// This checking of the path is terrible, limited, and hacky
if request.method() == method && request_url.ends_with(path) {
result = Some(f(request));
}
}
}
result.unwrap_or_else(|| Response::text("Default!"))
});
}
fn do_one(_: &Request) -> Response {
Response::text("hello world one")
}
fn do_two(_: &Request) -> Response {
Response::text("hello world two")
}
这会运行/one
,/two
以及其他所有内容的各种处理程序。
我不是Rouille的专家,事实上我从来没有在今天之前使用过它,但看起来你似乎正试图将它用于超出目前设计目标的东西。也许这是故意的,作者试图提出一个非常自以为是的工具。也许这是偶然的,他们没有想到这个用例。也许这是暂时的,他们只是没有接触到它。
无论哪种方式,我建议问作者。如果它不是预期的用例,他们可以更新项目文档以明确说明。否则,他们可能会产生问题来实现该功能,您可以帮助设计它。