mirror of
https://github.com/rwf2/Rocket.git
synced 2025-02-16 13:42:05 +00:00
Add query params to Rocket. Use Ident for attribute params.
This commit is contained in:
parent
ec38d70449
commit
327b28a98e
@ -5,7 +5,7 @@ members = [
|
||||
"examples/extended_validation",
|
||||
"examples/forms",
|
||||
"examples/hello_person",
|
||||
"examples/hello_query_params",
|
||||
"examples/query_params",
|
||||
"examples/hello_world",
|
||||
"examples/manual_routes",
|
||||
"examples/optional_redirect",
|
||||
|
@ -1,28 +0,0 @@
|
||||
#![feature(plugin)]
|
||||
#![plugin(rocket_macros)]
|
||||
|
||||
extern crate rocket;
|
||||
|
||||
use rocket::{Rocket, Error};
|
||||
|
||||
// One idea of what we could get.
|
||||
// #[route(GET, path = "/hello?{name,age}")]
|
||||
// fn hello(name: &str, age: &str) -> String {
|
||||
// "Hello!".to_string()
|
||||
// // format!("Hello, {} year old named {}!", age, name)
|
||||
// }
|
||||
|
||||
// Another idea.
|
||||
// #[route(GET, path = "/hello")]
|
||||
// fn hello(q: QueryParams) -> IOResult<String> {
|
||||
// format!("Hello, {} year old named {}!", q.get("name")?, q.get("age")?)
|
||||
// }
|
||||
|
||||
#[get("/hello")]
|
||||
fn hello() -> &'static str {
|
||||
"Hello there! Don't have query params yet, but we're working on it."
|
||||
}
|
||||
|
||||
fn main() {
|
||||
Rocket::new("localhost", 8000).mount_and_launch("/", routes![hello]);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "hello_query"
|
||||
name = "query_params"
|
||||
version = "0.0.1"
|
||||
authors = ["Sergio Benitez <sb@sergio.bz>"]
|
||||
workspace = "../../"
|
25
examples/query_params/src/main.rs
Normal file
25
examples/query_params/src/main.rs
Normal file
@ -0,0 +1,25 @@
|
||||
#![feature(plugin, custom_derive)]
|
||||
#![plugin(rocket_macros)]
|
||||
|
||||
extern crate rocket;
|
||||
|
||||
use rocket::{Rocket, Error};
|
||||
|
||||
#[derive(FromForm)]
|
||||
struct Person<'r> {
|
||||
name: &'r str,
|
||||
age: Option<u8>
|
||||
}
|
||||
|
||||
#[get("/hello?<person>")]
|
||||
fn hello(person: Person) -> String {
|
||||
if let Some(age) = person.age {
|
||||
format!("Hello, {} year old named {}!", age, person.name)
|
||||
} else {
|
||||
format!("Hello {}!", person.name)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
Rocket::new("localhost", 8000).mount_and_launch("/", routes![hello]);
|
||||
}
|
@ -13,9 +13,9 @@ pub trait FromFormValue<'v>: Sized {
|
||||
|
||||
fn parse(v: &'v str) -> Result<Self, Self::Error>;
|
||||
|
||||
// Returns a default value to be used when the form field does not exist. If
|
||||
// this returns None, then the field is required. Otherwise, this should
|
||||
// return Some(default_value).
|
||||
/// Returns a default value to be used when the form field does not exist.
|
||||
/// If this returns None, then the field is required. Otherwise, this should
|
||||
/// return Some(default_value).
|
||||
fn default() -> Option<Self> {
|
||||
None
|
||||
}
|
||||
@ -95,27 +95,37 @@ impl<'v, T: FromFormValue<'v>> FromFormValue<'v> for Result<T, T::Error> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn form_items<'f>(string: &'f str, items: &mut [(&'f str, &'f str)]) -> usize {
|
||||
let mut param_num = 0;
|
||||
let mut rest = string;
|
||||
while !rest.is_empty() && param_num < items.len() {
|
||||
let (key, remainder) = match rest.find('=') {
|
||||
Some(index) => (&rest[..index], &rest[(index + 1)..]),
|
||||
None => return param_num
|
||||
pub struct FormItems<'f>(pub &'f str);
|
||||
|
||||
impl<'f> Iterator for FormItems<'f> {
|
||||
type Item = (&'f str, &'f str);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let string = self.0;
|
||||
let (key, rest) = match string.find('=') {
|
||||
Some(index) => (&string[..index], &string[(index + 1)..]),
|
||||
None => return None
|
||||
};
|
||||
|
||||
rest = remainder;
|
||||
let (value, remainder) = match rest.find('&') {
|
||||
Some(index) => (&rest[..index], &rest[(index + 1)..]),
|
||||
None => (rest, "")
|
||||
};
|
||||
|
||||
rest = remainder;
|
||||
items[param_num] = (key, value);
|
||||
param_num += 1;
|
||||
self.0 = remainder;
|
||||
Some((key, value))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn form_items<'f>(string: &'f str, items: &mut [(&'f str, &'f str)]) -> usize {
|
||||
let mut param_count = 0;
|
||||
for (i, item) in FormItems(string).take(items.len()).enumerate() {
|
||||
items[i] = item;
|
||||
param_count += 1;
|
||||
}
|
||||
|
||||
param_num
|
||||
param_count
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -16,7 +16,7 @@ impl<'a> URI<'a> {
|
||||
let uri = uri.as_ref();
|
||||
|
||||
let (path, query) = match uri.find('?') {
|
||||
Some(index) => (&uri[..index], Some(&uri[index..])),
|
||||
Some(index) => (&uri[..index], Some(&uri[(index + 1)..])),
|
||||
None => (uri, None)
|
||||
};
|
||||
|
||||
@ -40,6 +40,10 @@ impl<'a> URI<'a> {
|
||||
Segments(self.path)
|
||||
}
|
||||
|
||||
pub fn query(&self) -> Option<&'a str> {
|
||||
self.query
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &'a str {
|
||||
self.uri
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Display;
|
||||
|
||||
use ::{ROUTE_STRUCT_PREFIX, ROUTE_FN_PREFIX, PARAM_PREFIX};
|
||||
use utils::{emit_item, span, sep_by_tok, SpanExt, IdentExt, ArgExt, option_as_expr};
|
||||
@ -6,7 +7,7 @@ use parser::RouteParams;
|
||||
|
||||
use syntax::codemap::{Span, Spanned};
|
||||
use syntax::tokenstream::TokenTree;
|
||||
use syntax::ast::{Arg, Ident, Stmt, Expr, MetaItem, Path};
|
||||
use syntax::ast::{Name, Arg, Ident, Stmt, Expr, MetaItem, Path};
|
||||
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||
use syntax::ext::build::AstBuilder;
|
||||
use syntax::parse::token::{self, str_to_ident};
|
||||
@ -21,6 +22,7 @@ fn method_to_path(ecx: &ExtCtxt, method: Method) -> Path {
|
||||
})
|
||||
}
|
||||
|
||||
// FIXME: This should return an Expr! (Ext is not a path.)
|
||||
fn top_level_to_expr(ecx: &ExtCtxt, level: &TopLevel) -> Path {
|
||||
quote_enum!(ecx, *level => ::rocket::content_type::TopLevel {
|
||||
Star, Text, Image, Audio, Video, Application, Multipart, Model, Message;
|
||||
@ -28,6 +30,7 @@ fn top_level_to_expr(ecx: &ExtCtxt, level: &TopLevel) -> Path {
|
||||
})
|
||||
}
|
||||
|
||||
// FIXME: This should return an Expr! (Ext is not a path.)
|
||||
fn sub_level_to_expr(ecx: &ExtCtxt, level: &SubLevel) -> Path {
|
||||
quote_enum!(ecx, *level => ::rocket::content_type::SubLevel {
|
||||
Star, Plain, Html, Xml, Javascript, Css, EventStream, Json,
|
||||
@ -45,24 +48,31 @@ fn content_type_to_expr(ecx: &ExtCtxt, ct: Option<ContentType>) -> Option<P<Expr
|
||||
}
|
||||
|
||||
trait RouteGenerateExt {
|
||||
fn gen_form(&self, &ExtCtxt, Option<&Spanned<Ident>>, P<Expr>) -> Option<Stmt>;
|
||||
fn missing_declared_err<T: Display>(&self, ecx: &ExtCtxt, arg: &Spanned<T>);
|
||||
|
||||
fn generate_form_statement(&self, ecx: &ExtCtxt) -> Option<Stmt>;
|
||||
fn generate_query_statement(&self, ecx: &ExtCtxt) -> Option<Stmt>;
|
||||
fn generate_param_statements(&self, ecx: &ExtCtxt) -> Vec<Stmt>;
|
||||
fn generate_fn_arguments(&self, ecx: &ExtCtxt) -> Vec<TokenTree>;
|
||||
fn explode(&self, ecx: &ExtCtxt) -> (&String, Path, P<Expr>, P<Expr>);
|
||||
}
|
||||
|
||||
impl RouteGenerateExt for RouteParams {
|
||||
fn generate_form_statement(&self, ecx: &ExtCtxt) -> Option<Stmt> {
|
||||
let param = self.form_param.as_ref();
|
||||
let arg = param.and_then(|p| self.annotated_fn.find_input(p.value()));
|
||||
fn missing_declared_err<T: Display>(&self, ecx: &ExtCtxt, arg: &Spanned<T>) {
|
||||
let fn_span = self.annotated_fn.span();
|
||||
let msg = format!("'{}' is declared as an argument...", arg.node);
|
||||
ecx.span_err(arg.span, &msg);
|
||||
ecx.span_err(fn_span, "...but isn't in the function signature.");
|
||||
}
|
||||
|
||||
fn gen_form(&self, ecx: &ExtCtxt, param: Option<&Spanned<Ident>>,
|
||||
form_string: P<Expr>) -> Option<Stmt> {
|
||||
let arg = param.and_then(|p| self.annotated_fn.find_input(&p.node.name));
|
||||
if param.is_none() {
|
||||
return None;
|
||||
} else if arg.is_none() {
|
||||
let param = param.unwrap();
|
||||
let fn_span = self.annotated_fn.span();
|
||||
let msg = format!("'{}' is declared as an argument...", param.value());
|
||||
ecx.span_err(param.span, &msg);
|
||||
ecx.span_err(fn_span, "...but isn't in the function signature.");
|
||||
self.missing_declared_err(ecx, ¶m.unwrap());
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -70,18 +80,38 @@ impl RouteGenerateExt for RouteParams {
|
||||
let (name, ty) = (arg.ident().unwrap().prepend(PARAM_PREFIX), &arg.ty);
|
||||
Some(quote_stmt!(ecx,
|
||||
let $name: $ty =
|
||||
if let Ok(s) = ::std::str::from_utf8(_req.data.as_slice()) {
|
||||
if let Ok(v) = ::rocket::form::FromForm::from_form_string(s) {
|
||||
v
|
||||
} else {
|
||||
return ::rocket::Response::not_found();
|
||||
}
|
||||
} else {
|
||||
return ::rocket::Response::server_error();
|
||||
match ::rocket::form::FromForm::from_form_string($form_string) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return ::rocket::Response::forward()
|
||||
};
|
||||
).expect("form statement"))
|
||||
}
|
||||
|
||||
fn generate_form_statement(&self, ecx: &ExtCtxt) -> Option<Stmt> {
|
||||
let param = self.form_param.as_ref().map(|p| &p.value);
|
||||
let expr = quote_expr!(ecx,
|
||||
match ::std::str::from_utf8(_req.data.as_slice()) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return ::rocket::Response::server_error()
|
||||
}
|
||||
);
|
||||
|
||||
self.gen_form(ecx, param, expr)
|
||||
}
|
||||
|
||||
fn generate_query_statement(&self, ecx: &ExtCtxt) -> Option<Stmt> {
|
||||
let param = self.query_param.as_ref();
|
||||
let expr = quote_expr!(ecx,
|
||||
match _req.uri().query() {
|
||||
// FIXME: Don't reinterpret as UTF8 again.
|
||||
Some(query) => query,
|
||||
None => return ::rocket::Response::forward()
|
||||
}
|
||||
);
|
||||
|
||||
self.gen_form(ecx, param, expr)
|
||||
}
|
||||
|
||||
// TODO: Add some kind of logging facility in Rocket to get be able to log
|
||||
// an error/debug message if parsing a parameter fails.
|
||||
fn generate_param_statements(&self, ecx: &ExtCtxt) -> Vec<Stmt> {
|
||||
@ -91,16 +121,13 @@ impl RouteGenerateExt for RouteParams {
|
||||
// Retrieve an iterator over the user's path parameters and ensure that
|
||||
// each parameter appears in the function signature.
|
||||
for param in ¶ms {
|
||||
if self.annotated_fn.find_input(param.node).is_none() {
|
||||
let fn_span = self.annotated_fn.span();
|
||||
let msg = format!("'{}' is declared as an argument...", param.node);
|
||||
ecx.span_err(param.span, &msg);
|
||||
ecx.span_err(fn_span, "...but isn't in the function signature.");
|
||||
if self.annotated_fn.find_input(¶m.node.name).is_none() {
|
||||
self.missing_declared_err(ecx, ¶m);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a function thats checks if an argument was declared in `path`.
|
||||
let set: HashSet<&str> = params.iter().map(|p| p.node).collect();
|
||||
let set: HashSet<&Name> = params.iter().map(|p| &p.node.name).collect();
|
||||
let declared = &|arg: &&Arg| set.contains(&*arg.name().unwrap());
|
||||
|
||||
// These are all of the arguments in the function signature.
|
||||
@ -117,11 +144,13 @@ impl RouteGenerateExt for RouteParams {
|
||||
).expect("declared param parsing statement"));
|
||||
}
|
||||
|
||||
// A from_request parameter is one that isnt't declared and isn't `form`.
|
||||
// A from_request parameter is one that isn't declared, `form`, or query.
|
||||
let from_request = |a: &&Arg| {
|
||||
let a_name = &*a.name().unwrap();
|
||||
!declared(a)
|
||||
&& self.form_param.as_ref().map_or(true, |p| p.value() != a_name)
|
||||
!declared(a) && self.form_param.as_ref().map_or(true, |p| {
|
||||
!a.named(&p.value().name)
|
||||
}) && self.query_param.as_ref().map_or(true, |p| {
|
||||
!a.named(&p.node.name)
|
||||
})
|
||||
};
|
||||
|
||||
// Generate the code for `form_request` parameters.
|
||||
@ -170,7 +199,10 @@ fn generic_route_decorator(known_method: Option<Spanned<Method>>,
|
||||
|
||||
// Parse the route and generate the code to create the form and param vars.
|
||||
let route = RouteParams::from(ecx, sp, known_method, meta_item, annotated);
|
||||
debug!("Route params: {:?}", route);
|
||||
|
||||
let form_statement = route.generate_form_statement(ecx);
|
||||
let query_statement = route.generate_query_statement(ecx);
|
||||
let param_statements = route.generate_param_statements(ecx);
|
||||
let fn_arguments = route.generate_fn_arguments(ecx);
|
||||
|
||||
@ -181,6 +213,7 @@ fn generic_route_decorator(known_method: Option<Spanned<Method>>,
|
||||
fn $route_fn_name<'rocket>(_req: &'rocket ::rocket::Request<'rocket>)
|
||||
-> ::rocket::Response<'rocket> {
|
||||
$form_statement
|
||||
$query_statement
|
||||
$param_statements
|
||||
let result = $user_fn_name($fn_arguments);
|
||||
::rocket::Response::new(result)
|
||||
|
@ -3,6 +3,7 @@ use syntax::codemap::{Span, Spanned};
|
||||
use syntax::ext::base::Annotatable;
|
||||
use utils::{ArgExt, span};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Function(Spanned<(Ident, FnDecl)>);
|
||||
|
||||
impl Function {
|
||||
@ -33,7 +34,7 @@ impl Function {
|
||||
self.0.span
|
||||
}
|
||||
|
||||
pub fn find_input<'a>(&'a self, name: &str) -> Option<&'a Arg> {
|
||||
pub fn find_input<'a>(&'a self, name: &Name) -> Option<&'a Arg> {
|
||||
self.decl().inputs.iter().filter(|arg| arg.named(name)).next()
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
use syntax::ast::Ident;
|
||||
use syntax::ext::base::ExtCtxt;
|
||||
use syntax::codemap::{Span, Spanned, BytePos};
|
||||
use syntax::parse::token::str_to_ident;
|
||||
|
||||
use utils::span;
|
||||
|
||||
@ -20,9 +22,9 @@ impl<'s, 'a, 'c: 'a> ParamIter<'s, 'a, 'c> {
|
||||
}
|
||||
|
||||
impl<'s, 'a, 'c> Iterator for ParamIter<'s, 'a, 'c> {
|
||||
type Item = Spanned<&'s str>;
|
||||
type Item = Spanned<Ident>;
|
||||
|
||||
fn next(&mut self) -> Option<Spanned<&'s str>> {
|
||||
fn next(&mut self) -> Option<Spanned<Ident>> {
|
||||
// Find the start and end indexes for the next parameter, if any.
|
||||
let (start, end) = match (self.string.find('<'), self.string.find('>')) {
|
||||
(Some(i), Some(j)) => (i, j),
|
||||
@ -51,7 +53,7 @@ impl<'s, 'a, 'c> Iterator for ParamIter<'s, 'a, 'c> {
|
||||
} else {
|
||||
self.string = &self.string[(end + 1)..];
|
||||
self.span.lo = self.span.lo + BytePos((end + 1) as u32);
|
||||
Some(span(param, param_span))
|
||||
Some(span(str_to_ident(param), param_span))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ use std::collections::HashSet;
|
||||
use syntax::ast::*;
|
||||
use syntax::ext::base::{ExtCtxt, Annotatable};
|
||||
use syntax::codemap::{Span, Spanned, dummy_spanned};
|
||||
use syntax::parse::token::str_to_ident;
|
||||
|
||||
use utils::{span, MetaItemExt, SpanExt};
|
||||
use super::{Function, ParamIter};
|
||||
@ -16,11 +17,13 @@ use rocket::{Method, ContentType};
|
||||
/// the user supplied the information. This structure can only be obtained by
|
||||
/// calling the `RouteParams::from` function and passing in the entire decorator
|
||||
/// environment.
|
||||
#[derive(Debug)]
|
||||
pub struct RouteParams {
|
||||
pub annotated_fn: Function,
|
||||
pub method: Spanned<Method>,
|
||||
pub path: Spanned<String>,
|
||||
pub form_param: Option<KVSpanned<String>>,
|
||||
pub form_param: Option<KVSpanned<Ident>>,
|
||||
pub query_param: Option<Spanned<Ident>>,
|
||||
pub format: Option<KVSpanned<ContentType>>,
|
||||
pub rank: Option<KVSpanned<isize>>,
|
||||
}
|
||||
@ -66,8 +69,8 @@ impl RouteParams {
|
||||
ecx.span_fatal(sp, "malformed attribute");
|
||||
}
|
||||
|
||||
// Parse the required path parameter.
|
||||
let path = parse_path(ecx, &attr_params[0]);
|
||||
// Parse the required path and optional query parameters.
|
||||
let (path, query) = parse_path(ecx, &attr_params[0]);
|
||||
|
||||
// Parse all of the optional parameters.
|
||||
let mut seen_keys = HashSet::new();
|
||||
@ -105,6 +108,7 @@ impl RouteParams {
|
||||
method: method,
|
||||
path: path,
|
||||
form_param: form,
|
||||
query_param: query,
|
||||
format: format,
|
||||
rank: rank,
|
||||
annotated_fn: function,
|
||||
@ -137,6 +141,22 @@ pub fn kv_from_nested(item: &NestedMetaItem) -> Option<KVSpanned<LitKind>> {
|
||||
})
|
||||
}
|
||||
|
||||
fn param_string_to_ident(ecx: &ExtCtxt, s: Spanned<&str>) -> Option<Ident> {
|
||||
let string = s.node;
|
||||
if string.starts_with('<') && string.ends_with('>') {
|
||||
let param = &string[1..(string.len() - 1)];
|
||||
if param.chars().all(char::is_alphanumeric) {
|
||||
return Some(str_to_ident(param));
|
||||
}
|
||||
|
||||
ecx.span_err(s.span, "parameter name must be alphanumeric");
|
||||
} else {
|
||||
ecx.span_err(s.span, "parameters must start with '<' and end with '>'");
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn parse_method(ecx: &ExtCtxt, meta_item: &NestedMetaItem) -> Spanned<Method> {
|
||||
if let Some(word) = meta_item.word() {
|
||||
if let Ok(method) = Method::from_str(&*word.name()) {
|
||||
@ -157,18 +177,29 @@ fn parse_method(ecx: &ExtCtxt, meta_item: &NestedMetaItem) -> Spanned<Method> {
|
||||
return dummy_spanned(Method::Get);
|
||||
}
|
||||
|
||||
fn parse_path(ecx: &ExtCtxt, meta_item: &NestedMetaItem) -> Spanned<String> {
|
||||
fn parse_path(ecx: &ExtCtxt, meta_item: &NestedMetaItem) -> (Spanned<String>, Option<Spanned<Ident>>) {
|
||||
let from_string = |string: &str, sp: Span| {
|
||||
if let Some(q) = string.find('?') {
|
||||
let path = span(string[..q].to_string(), sp);
|
||||
let q_str = span(&string[(q + 1)..], sp);
|
||||
let query = param_string_to_ident(ecx, q_str).map(|i| span(i, sp));
|
||||
return (path, query);
|
||||
} else {
|
||||
return (span(string.to_string(), sp), None)
|
||||
}
|
||||
};
|
||||
|
||||
let sp = meta_item.span();
|
||||
if let Some((name, lit)) = meta_item.name_value() {
|
||||
if name != "path" {
|
||||
ecx.span_err(sp, "the first key, if any, must be 'path'");
|
||||
} else if let LitKind::Str(ref s, _) = lit.node {
|
||||
return span(s.to_string(), lit.span);
|
||||
return from_string(s, lit.span);
|
||||
} else {
|
||||
ecx.span_err(lit.span, "`path` value must be a string")
|
||||
}
|
||||
} else if let Some(s) = meta_item.str_lit() {
|
||||
return span(s.to_string(), sp);
|
||||
return from_string(s, sp);
|
||||
} else {
|
||||
ecx.struct_span_err(sp, r#"expected `path = string` or a path string"#)
|
||||
.help(r#"you can specify the path directly as a string, \
|
||||
@ -177,7 +208,7 @@ fn parse_path(ecx: &ExtCtxt, meta_item: &NestedMetaItem) -> Spanned<String> {
|
||||
.emit();
|
||||
}
|
||||
|
||||
dummy_spanned("".to_string())
|
||||
(dummy_spanned("".to_string()), None)
|
||||
}
|
||||
|
||||
fn parse_opt<O, T, F>(ecx: &ExtCtxt, kv: &KVSpanned<T>, f: F) -> Option<KVSpanned<O>>
|
||||
@ -186,15 +217,10 @@ fn parse_opt<O, T, F>(ecx: &ExtCtxt, kv: &KVSpanned<T>, f: F) -> Option<KVSpanne
|
||||
Some(kv.map_ref(|_| f(ecx, kv)))
|
||||
}
|
||||
|
||||
fn parse_form(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> String {
|
||||
fn parse_form(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> Ident {
|
||||
if let LitKind::Str(ref s, _) = *kv.value() {
|
||||
if s.starts_with('<') && s.ends_with('>') {
|
||||
let form_param = s[1..(s.len() - 1)].to_string();
|
||||
if form_param.chars().all(char::is_alphanumeric) {
|
||||
return form_param;
|
||||
}
|
||||
|
||||
ecx.span_err(kv.value.span, "parameter name must be alphanumeric");
|
||||
if let Some(ident) = param_string_to_ident(ecx, span(s, kv.value.span)) {
|
||||
return ident;
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,7 +230,7 @@ fn parse_form(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> String {
|
||||
parameter inside '<' '>'. e.g: form = "<login>""#)
|
||||
.emit();
|
||||
|
||||
"".to_string()
|
||||
str_to_ident("")
|
||||
}
|
||||
|
||||
fn parse_rank(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> isize {
|
||||
|
@ -1,15 +1,15 @@
|
||||
use syntax::ast::{Arg, PatKind, Ident};
|
||||
use syntax::ast::{Arg, PatKind, Ident, Name};
|
||||
|
||||
pub trait ArgExt {
|
||||
fn ident(&self) -> Option<&Ident>;
|
||||
|
||||
fn name(&self) -> Option<String> {
|
||||
fn name(&self) -> Option<&Name> {
|
||||
self.ident().map(|ident| {
|
||||
ident.name.to_string()
|
||||
&ident.name
|
||||
})
|
||||
}
|
||||
|
||||
fn named(&self, name: &str) -> bool {
|
||||
fn named(&self, name: &Name) -> bool {
|
||||
self.name().map_or(false, |a| a == name)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user