use syntax::ast::Ident;
use syntax::ext::base::ExtCtxt;
use syntax::codemap::{Span, Spanned, BytePos};
use utils::{span, SpanExt, is_valid_ident};
#[derive(Debug)]
pub enum Param {
Single(Spanned<Ident>),
Many(Spanned<Ident>)
}
impl Param {
pub fn inner(&self) -> &Spanned<Ident> {
match *self {
Param::Single(ref ident) | Param::Many(ref ident) => ident
}
}
pub fn ident(&self) -> &Ident {
match *self {
Param::Single(ref ident) | Param::Many(ref ident) => &ident.node
}
}
}
pub struct ParamIter<'s, 'a, 'c: 'a> {
ctxt: &'a ExtCtxt<'c>,
span: Span,
string: &'s str,
}
impl<'s, 'a, 'c: 'a> ParamIter<'s, 'a, 'c> {
pub fn new(c: &'a ExtCtxt<'c>, s: &'s str, p: Span) -> ParamIter<'s, 'a, 'c> {
ParamIter {
ctxt: c,
span: p,
string: s,
}
}
}
impl<'s, 'a, 'c> Iterator for ParamIter<'s, 'a, 'c> {
type Item = Param;
fn next(&mut self) -> Option<Param> {
let err = |ecx: &ExtCtxt, sp: Span, msg: &str| {
ecx.span_err(sp, msg);
return None;
};
let (start, end) = match self.string.find('<') {
Some(i) => match self.string.find('>') {
Some(j) => (i, j),
None => return err(self.ctxt, self.span, "malformed parameter list")
},
_ => return None,
};
if end <= start {
return err(self.ctxt, self.span, "malformed parameter list");
}
let full_param = &self.string[(start + 1)..end];
let (is_many, param) = match full_param.ends_with("..") {
true => (true, &full_param[..(full_param.len() - 2)]),
false => (false, full_param)
};
let mut param_span = self.span;
param_span.lo = self.span.lo + BytePos(start as u32);
param_span.hi = self.span.lo + BytePos((end + 1) as u32);
self.string = &self.string[(end + 1)..];
self.span.lo = self.span.lo + BytePos((end + 1) as u32);
if param.is_empty() {
err(self.ctxt, param_span, "parameter names cannot be empty")
} else if !is_valid_ident(param) {
err(self.ctxt, param_span, "parameter names must be valid identifiers")
} else if param.starts_with("_") {
err(self.ctxt, param_span, "parameters cannot be ignored")
} else if is_many && !self.string.is_empty() {
let sp = self.span.shorten_to(self.string.len());
self.ctxt.struct_span_err(sp, "text after a trailing '..' param")
.span_note(param_span, "trailing param is here")
.emit();
None
} else {
let spanned_ident = span(Ident::from_str(param), param_span);
match is_many {
true => Some(Param::Many(spanned_ident)),
false => Some(Param::Single(spanned_ident))
}
}
}
}