2

I'm working on a proc_macro2/syn macro, and I've been using Cursor to do low-level parsing of something... Rust-like. But deep in my grammar, I need to parse a syn::Type. As far as I know, the only really good way to do this is to use the impl syn::parse::Parse for syn::Type.

So what I really would like to find is a nice way to write a function of signature

fn parse_generic<T: Parse>(c: Cursor) -> Option<(T, Cursor)>;

My question then is what is the best (simplest, least problematic) way to implement a function of this type?


Here is the best I've come up with so far:

fn parse_generic<T: Parse>(mut c: Cursor) -> Option<(T, Cursor)> {                                                                       
    match T::parse.parse2(c.token_stream()) {                                                                                            
        Ok(t) => Some((t, Cursor::empty())), // because parse2 checks for end-of-stream!                                                 
        Err(e) => {                                                                                                                      
            // OK, because parse2 checks for end-of-stream, let's chop                                                                   
            // the input at the position of the error and try again (!).                                                                 
            let mut collected = Vec::new();                                                                                              
            let upto = e.span().start();                                                                                                 
            while !c.eof() && c.span().start() != upto {                                                                                 
                let (tt, next) = c.token_tree().unwrap();                                                                                
                collected.push(tt);                                                                                                      
                c = next;                                                                                                                
            }                                                                                                                            
            match T::parse.parse2(collected.into_iter().collect()) {                                                                     
                Ok(t) => Some((t, c)),                                                                                                   
                Err(_) => None,                                                                                                          
            }                                                                                                                            
        }                                                                                                                                
    }                                                                                                                                    
}                                                                                                                                        

It's not disastrous, but it's not ideal either.

0 Answers0