WIP
This commit is contained in:
		
							parent
							
								
									d3265f3251
								
							
						
					
					
						commit
						17b11d1a25
					
				
							
								
								
									
										22
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								Cargo.toml
									
									
									
									
									
								
							| @ -11,22 +11,24 @@ repository = "https://github.com/ruma/ruma-api-macros" | |||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| quote = "0.3.15" | quote = "0.5.2" | ||||||
| ruma-api = "0.4.0" | ruma-api = "0.5.0" | ||||||
| synom = "0.11.3" |  | ||||||
| 
 | 
 | ||||||
| [dependencies.syn] | [dependencies.syn] | ||||||
|  | version = "0.13.4" | ||||||
| features = ["full"] | features = ["full"] | ||||||
| version = "0.11.11" | 
 | ||||||
|  | [dependencies.proc-macro2] | ||||||
|  | version = "0.3.8" | ||||||
|  | features = ["nightly"] | ||||||
| 
 | 
 | ||||||
| [dev-dependencies] | [dev-dependencies] | ||||||
| futures = "0.1.14" | futures = "0.1.21" | ||||||
| hyper = "0.11" | serde = "1.0.45" | ||||||
| serde = "1.0.8" | serde_derive = "1.0.45" | ||||||
| serde_derive = "1.0.8" | serde_json = "1.0.17" | ||||||
| serde_json = "1.0.2" |  | ||||||
| serde_urlencoded = "0.5.1" | serde_urlencoded = "0.5.1" | ||||||
| url = "1.5.1" | url = "1.7.0" | ||||||
| 
 | 
 | ||||||
| [lib] | [lib] | ||||||
| proc-macro = true | proc-macro = true | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| use quote::{ToTokens, Tokens}; | use quote::{ToTokens, Tokens}; | ||||||
|  | use syn::synom::Synom; | ||||||
| use syn::{Expr, Ident}; | use syn::{Expr, Ident}; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| @ -11,6 +12,55 @@ pub struct Metadata { | |||||||
|     pub requires_authentication: Tokens, |     pub requires_authentication: Tokens, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | impl Synom for Metadata { | ||||||
|  |     named!(parse -> Self, do_parse!( | ||||||
|  |         ident: syn!(Ident) >> | ||||||
|  |         cond_reduce!(ident == "description") >> | ||||||
|  |         punct!(:) >> | ||||||
|  |         description: syn!(Expr) >> | ||||||
|  |         punct!(,) >> | ||||||
|  | 
 | ||||||
|  |         ident: syn!(Ident) >> | ||||||
|  |         cond_reduce!(ident == "method") >> | ||||||
|  |         punct!(:) >> | ||||||
|  |         method: syn!(Expr) >> | ||||||
|  |         punct!(,) >> | ||||||
|  | 
 | ||||||
|  |         ident: syn!(Ident) >> | ||||||
|  |         cond_reduce!(ident == "name") >> | ||||||
|  |         punct!(:) >> | ||||||
|  |         name: syn!(Expr) >> | ||||||
|  |         punct!(,) >> | ||||||
|  | 
 | ||||||
|  |         ident: syn!(Ident) >> | ||||||
|  |         cond_reduce!(ident == "path") >> | ||||||
|  |         punct!(:) >> | ||||||
|  |         path: syn!(Expr) >> | ||||||
|  |         punct!(,) >> | ||||||
|  | 
 | ||||||
|  |         ident: syn!(Ident) >> | ||||||
|  |         cond_reduce!(ident == "rate_limited") >> | ||||||
|  |         punct!(:) >> | ||||||
|  |         rate_limited: syn!(Expr) >> | ||||||
|  |         punct!(,) >> | ||||||
|  | 
 | ||||||
|  |         ident: syn!(Ident) >> | ||||||
|  |         cond_reduce!(ident == "requires_authentication") >> | ||||||
|  |         punct!(:) >> | ||||||
|  |         requires_authentication: syn!(Expr) >> | ||||||
|  |         punct!(,) >> | ||||||
|  | 
 | ||||||
|  |         (Metadata { | ||||||
|  |             description, | ||||||
|  |             method, | ||||||
|  |             name, | ||||||
|  |             path, | ||||||
|  |             rate_limited, | ||||||
|  |             requires_authentication, | ||||||
|  |         }) | ||||||
|  |     )); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| impl From<Vec<(Ident, Expr)>> for Metadata { | impl From<Vec<(Ident, Expr)>> for Metadata { | ||||||
|     fn from(fields: Vec<(Ident, Expr)>) -> Self { |     fn from(fields: Vec<(Ident, Expr)>) -> Self { | ||||||
|         let mut description = None; |         let mut description = None; | ||||||
|  | |||||||
							
								
								
									
										632
									
								
								src/api/mod.rs
									
									
									
									
									
								
							
							
						
						
									
										632
									
								
								src/api/mod.rs
									
									
									
									
									
								
							| @ -1,35 +1,39 @@ | |||||||
|  | use std::convert::{TryFrom, TryInto}; | ||||||
|  | 
 | ||||||
| use quote::{ToTokens, Tokens}; | use quote::{ToTokens, Tokens}; | ||||||
| use syn::{Field, MetaItem}; | use syn::punctuated::Pair; | ||||||
|  | use syn::synom::Synom; | ||||||
|  | use syn::{Expr, Field, Ident, Meta}; | ||||||
| 
 | 
 | ||||||
| mod metadata; | mod metadata; | ||||||
| mod request; | mod request; | ||||||
| mod response; | mod response; | ||||||
| 
 | 
 | ||||||
| use parse::Entry; | // use parse::Entry;
 | ||||||
| use self::metadata::Metadata; | use self::metadata::Metadata; | ||||||
| use self::request::Request; | use self::request::Request; | ||||||
| use self::response::Response; | use self::response::Response; | ||||||
| 
 | 
 | ||||||
| pub fn strip_serde_attrs(field: &Field) -> Field { | // pub fn strip_serde_attrs(field: &Field) -> Field {
 | ||||||
|     let mut field = field.clone(); | //     let mut field = field.clone();
 | ||||||
| 
 | 
 | ||||||
|     field.attrs = field.attrs.into_iter().filter(|attr| { | //     field.attrs = field.attrs.into_iter().filter(|attr| {
 | ||||||
|         let (attr_ident, _) = match attr.value { | //         let (attr_ident, _) = match attr.value {
 | ||||||
|             MetaItem::List(ref attr_ident, _) => { | //             Meta::List(ref attr_ident, _) => {
 | ||||||
|                 (attr_ident, ()) | //                 (attr_ident, ())
 | ||||||
|             } | //             }
 | ||||||
|             _ => return true, | //             _ => return true,
 | ||||||
|         }; | //         };
 | ||||||
| 
 | 
 | ||||||
|         if attr_ident != "serde" { | //         if attr_ident != "serde" {
 | ||||||
|             return true; | //             return true;
 | ||||||
|         } | //         }
 | ||||||
| 
 | 
 | ||||||
|         false | //         false
 | ||||||
|     }).collect(); | //     }).collect();
 | ||||||
| 
 | 
 | ||||||
|     field | //     field
 | ||||||
| } | // }
 | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct Api { | pub struct Api { | ||||||
| @ -38,291 +42,333 @@ pub struct Api { | |||||||
|     response: Response, |     response: Response, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl ToTokens for Api { | impl TryFrom<Vec<Expr>> for Api { | ||||||
|     fn to_tokens(&self, tokens: &mut Tokens) { |     type Error = &'static str; | ||||||
|         let description = &self.metadata.description; |  | ||||||
|         let method = &self.metadata.method; |  | ||||||
|         let name = &self.metadata.name; |  | ||||||
|         let path = &self.metadata.path; |  | ||||||
|         let rate_limited = &self.metadata.rate_limited; |  | ||||||
|         let requires_authentication = &self.metadata.requires_authentication; |  | ||||||
| 
 | 
 | ||||||
|         let request_types = { |     fn try_from(exprs: Vec<Expr>) -> Result<Self, Self::Error> { | ||||||
|             let mut tokens = Tokens::new(); |         if exprs.len() != 3 { | ||||||
|             self.request.to_tokens(&mut tokens); |             return Err("ruma_api! expects 3 blocks: metadata, request, and response"); | ||||||
|             tokens |  | ||||||
|         }; |  | ||||||
|         let response_types = { |  | ||||||
|             let mut tokens = Tokens::new(); |  | ||||||
|             self.response.to_tokens(&mut tokens); |  | ||||||
|             tokens |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         let set_request_path = if self.request.has_path_fields() { |  | ||||||
|             let path_str_quoted = path.as_str(); |  | ||||||
|             assert!( |  | ||||||
|                 path_str_quoted.starts_with('"') && path_str_quoted.ends_with('"'), |  | ||||||
|                 "path needs to be a string literal" |  | ||||||
|             ); |  | ||||||
| 
 |  | ||||||
|             let path_str = &path_str_quoted[1 .. path_str_quoted.len() - 1]; |  | ||||||
| 
 |  | ||||||
|             assert!(path_str.starts_with('/'), "path needs to start with '/'"); |  | ||||||
|             assert!( |  | ||||||
|                 path_str.chars().filter(|c| *c == ':').count() == self.request.path_field_count(), |  | ||||||
|                 "number of declared path parameters needs to match amount of placeholders in path" |  | ||||||
|             ); |  | ||||||
| 
 |  | ||||||
|             let request_path_init_fields = self.request.request_path_init_fields(); |  | ||||||
| 
 |  | ||||||
|             let mut tokens = quote! { |  | ||||||
|                 let request_path = RequestPath { |  | ||||||
|                     #request_path_init_fields |  | ||||||
|                 }; |  | ||||||
| 
 |  | ||||||
|                 // This `unwrap()` can only fail when the url is a
 |  | ||||||
|                 // cannot-be-base url like `mailto:` or `data:`, which is not
 |  | ||||||
|                 // the case for our placeholder url.
 |  | ||||||
|                 let mut path_segments = url.path_segments_mut().unwrap(); |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             for segment in path_str[1..].split('/') { |  | ||||||
|                 tokens.append(quote! { |  | ||||||
|                     path_segments.push |  | ||||||
|                 }); |  | ||||||
| 
 |  | ||||||
|                 tokens.append("("); |  | ||||||
| 
 |  | ||||||
|                 if segment.starts_with(':') { |  | ||||||
|                     tokens.append("&request_path."); |  | ||||||
|                     tokens.append(&segment[1..]); |  | ||||||
|                     tokens.append(".to_string()"); |  | ||||||
|                 } else { |  | ||||||
|                     tokens.append("\""); |  | ||||||
|                     tokens.append(segment); |  | ||||||
|                     tokens.append("\""); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 tokens.append(");"); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             tokens |  | ||||||
|         } else { |  | ||||||
|             quote! { |  | ||||||
|                 url.set_path(metadata.path); |  | ||||||
|             } |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         let set_request_query = if self.request.has_query_fields() { |  | ||||||
|             let request_query_init_fields = self.request.request_query_init_fields(); |  | ||||||
| 
 |  | ||||||
|             quote! { |  | ||||||
|                 let request_query = RequestQuery { |  | ||||||
|                     #request_query_init_fields |  | ||||||
|                 }; |  | ||||||
| 
 |  | ||||||
|                 url.set_query(Some(&::serde_urlencoded::to_string(request_query)?)); |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             Tokens::new() |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         let add_body_to_request = if let Some(field) = self.request.newtype_body_field() { |  | ||||||
|             let field_name = field.ident.as_ref().expect("expected body field to have a name"); |  | ||||||
| 
 |  | ||||||
|             quote! { |  | ||||||
|                 let request_body = RequestBody(request.#field_name); |  | ||||||
| 
 |  | ||||||
|                 hyper_request.set_body(::serde_json::to_vec(&request_body)?); |  | ||||||
|             } |  | ||||||
|         } else if self.request.has_body_fields() { |  | ||||||
|             let request_body_init_fields = self.request.request_body_init_fields(); |  | ||||||
| 
 |  | ||||||
|             quote! { |  | ||||||
|                 let request_body = RequestBody { |  | ||||||
|                     #request_body_init_fields |  | ||||||
|                 }; |  | ||||||
| 
 |  | ||||||
|                 hyper_request.set_body(::serde_json::to_vec(&request_body)?); |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             Tokens::new() |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         let deserialize_response_body = if let Some(field) = self.response.newtype_body_field() { |  | ||||||
|             let field_type = &field.ty; |  | ||||||
|             let mut tokens = Tokens::new(); |  | ||||||
| 
 |  | ||||||
|             tokens.append(quote! { |  | ||||||
|                 let future_response = hyper_response.body() |  | ||||||
|                     .fold::<_, _, Result<_, ::std::io::Error>>(Vec::new(), |mut bytes, chunk| { |  | ||||||
|                         bytes.write_all(&chunk)?; |  | ||||||
| 
 |  | ||||||
|                         Ok(bytes) |  | ||||||
|                     }) |  | ||||||
|                     .map_err(::ruma_api::Error::from) |  | ||||||
|                     .and_then(|bytes| { |  | ||||||
|                         ::serde_json::from_slice::<#field_type>(bytes.as_slice()) |  | ||||||
|                             .map_err(::ruma_api::Error::from) |  | ||||||
|                     }) |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|             tokens.append(".and_then(move |response_body| {"); |  | ||||||
| 
 |  | ||||||
|             tokens |  | ||||||
|         } else if self.response.has_body_fields() { |  | ||||||
|             let mut tokens = Tokens::new(); |  | ||||||
| 
 |  | ||||||
|             tokens.append(quote! { |  | ||||||
|                 let future_response = hyper_response.body() |  | ||||||
|                     .fold::<_, _, Result<_, ::std::io::Error>>(Vec::new(), |mut bytes, chunk| { |  | ||||||
|                         bytes.write_all(&chunk)?; |  | ||||||
| 
 |  | ||||||
|                         Ok(bytes) |  | ||||||
|                     }) |  | ||||||
|                     .map_err(::ruma_api::Error::from) |  | ||||||
|                     .and_then(|bytes| { |  | ||||||
|                         ::serde_json::from_slice::<ResponseBody>(bytes.as_slice()) |  | ||||||
|                             .map_err(::ruma_api::Error::from) |  | ||||||
|                     }) |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|             tokens.append(".and_then(move |response_body| {"); |  | ||||||
| 
 |  | ||||||
|             tokens |  | ||||||
|         } else { |  | ||||||
|             let mut tokens = Tokens::new(); |  | ||||||
| 
 |  | ||||||
|             tokens.append(quote! { |  | ||||||
|                 let future_response = ::futures::future::ok(()) |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|             tokens.append(".and_then(move |_| {"); |  | ||||||
| 
 |  | ||||||
|             tokens |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         let mut closure_end = Tokens::new(); |  | ||||||
|         closure_end.append("});"); |  | ||||||
| 
 |  | ||||||
|         let extract_headers = if self.response.has_header_fields() { |  | ||||||
|             quote! { |  | ||||||
|                 let mut headers = hyper_response.headers().clone(); |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             Tokens::new() |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         let response_init_fields = if self.response.has_fields() { |  | ||||||
|             self.response.init_fields() |  | ||||||
|         } else { |  | ||||||
|             Tokens::new() |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         tokens.append(quote! { |  | ||||||
|             #[allow(unused_imports)] |  | ||||||
|             use std::io::Write as _Write; |  | ||||||
| 
 |  | ||||||
|             #[allow(unused_imports)] |  | ||||||
|             use ::futures::{Future as _Future, Stream as _Stream}; |  | ||||||
|             use ::ruma_api::Endpoint as _RumaApiEndpoint; |  | ||||||
| 
 |  | ||||||
|             /// The API endpoint.
 |  | ||||||
|             #[derive(Debug)] |  | ||||||
|             pub struct Endpoint; |  | ||||||
| 
 |  | ||||||
|             #request_types |  | ||||||
| 
 |  | ||||||
|             impl ::std::convert::TryFrom<Request> for ::hyper::Request { |  | ||||||
|                 type Error = ::ruma_api::Error; |  | ||||||
| 
 |  | ||||||
|                 #[allow(unused_mut, unused_variables)] |  | ||||||
|                 fn try_from(request: Request) -> Result<Self, Self::Error> { |  | ||||||
|                     let metadata = Endpoint::METADATA; |  | ||||||
| 
 |  | ||||||
|                     // Use dummy homeserver url which has to be overwritten in
 |  | ||||||
|                     // the calling code. Previously (with hyper::Uri) this was
 |  | ||||||
|                     // not required, but Url::parse only accepts absolute urls.
 |  | ||||||
|                     let mut url = ::url::Url::parse("http://invalid-host-please-change/").unwrap(); |  | ||||||
| 
 |  | ||||||
|                     { #set_request_path } |  | ||||||
|                     { #set_request_query } |  | ||||||
| 
 |  | ||||||
|                     let mut hyper_request = ::hyper::Request::new( |  | ||||||
|                         metadata.method, |  | ||||||
|                         // Every valid URL is a valid URI
 |  | ||||||
|                         url.into_string().parse().unwrap(), |  | ||||||
|                     ); |  | ||||||
| 
 |  | ||||||
|                     { #add_body_to_request } |  | ||||||
| 
 |  | ||||||
|                     Ok(hyper_request) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             #response_types |  | ||||||
| 
 |  | ||||||
|             impl ::futures::future::FutureFrom<::hyper::Response> for Response { |  | ||||||
|                 type Future = Box<_Future<Item = Self, Error = Self::Error>>; |  | ||||||
|                 type Error = ::ruma_api::Error; |  | ||||||
| 
 |  | ||||||
|                 #[allow(unused_variables)] |  | ||||||
|                 fn future_from(hyper_response: ::hyper::Response) |  | ||||||
|                 -> Box<_Future<Item = Self, Error = Self::Error>> { |  | ||||||
|                     #extract_headers |  | ||||||
| 
 |  | ||||||
|                     #deserialize_response_body |  | ||||||
| 
 |  | ||||||
|                     let response = Response { |  | ||||||
|                         #response_init_fields |  | ||||||
|                     }; |  | ||||||
| 
 |  | ||||||
|                     Ok(response) |  | ||||||
|                     #closure_end |  | ||||||
| 
 |  | ||||||
|                     Box::new(future_response) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             impl ::ruma_api::Endpoint for Endpoint { |  | ||||||
|                 type Request = Request; |  | ||||||
|                 type Response = Response; |  | ||||||
| 
 |  | ||||||
|                 const METADATA: ::ruma_api::Metadata = ::ruma_api::Metadata { |  | ||||||
|                     description: #description, |  | ||||||
|                     method: ::hyper::#method, |  | ||||||
|                     name: #name, |  | ||||||
|                     path: #path, |  | ||||||
|                     rate_limited: #rate_limited, |  | ||||||
|                     requires_authentication: #requires_authentication, |  | ||||||
|                 }; |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl From<Vec<Entry>> for Api { |  | ||||||
|     fn from(entries: Vec<Entry>) -> Api { |  | ||||||
|         if entries.len() != 3 { |  | ||||||
|             panic!("ruma_api! expects 3 blocks: metadata, request, and response"); |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let mut metadata = None; |         let mut metadata = None; | ||||||
|         let mut request = None; |         let mut request = None; | ||||||
|         let mut response = None; |         let mut response = None; | ||||||
| 
 | 
 | ||||||
|         for entry in entries { |         for expr in exprs { | ||||||
|             match entry { |             let expr = match expr { | ||||||
|                 Entry::Metadata(fields) => metadata = Some(Metadata::from(fields)), |                 Expr::Struct(expr) => expr, | ||||||
|                 Entry::Request(fields) => request = Some(Request::from(fields)), |                 _ => return Err("ruma_api! blocks should use struct syntax"), | ||||||
|                 Entry::Response(fields) => response = Some(Response::from(fields)), |             }; | ||||||
|  | 
 | ||||||
|  |             let segments = expr.path.segments; | ||||||
|  | 
 | ||||||
|  |             if segments.len() != 1 { | ||||||
|  |                 return Err("ruma_api! blocks must be one of: metadata, request, or response"); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             let Pair::End(last_segment) = segments.last().unwrap(); | ||||||
|  | 
 | ||||||
|  |             match last_segment.ident.as_ref() { | ||||||
|  |                 "metadata" => metadata = Some(expr.try_into()?), | ||||||
|  |                 "request" => request = Some(expr.try_into()?), | ||||||
|  |                 "response" => response = Some(expr.try_into()?), | ||||||
|  |                 _ => return Err("ruma_api! blocks must be one of: metadata, request, or response"), | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         Api { |         if metadata.is_none() { | ||||||
|             metadata: metadata.expect("ruma_api! is missing metadata"), |             return Err("ruma_api! is missing metadata"); | ||||||
|             request: request.expect("ruma_api! is missing request"), |  | ||||||
|             response: response.expect("ruma_api! is missing response"), |  | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         if request.is_none() { | ||||||
|  |             return Err("ruma_api! is missing request"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if response.is_none() { | ||||||
|  |             return Err("ruma_api! is missing response"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Ok(Api { | ||||||
|  |             metadata: metadata.unwrap(), | ||||||
|  |             request: request.unwrap(), | ||||||
|  |             response: response.unwrap(), | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | pub struct Exprs { | ||||||
|  |     pub inner: Vec<Expr>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Synom for Exprs { | ||||||
|  |     named!(parse -> Self, do_parse!( | ||||||
|  |         exprs: many0!(syn!(Expr)) >> | ||||||
|  |         (Exprs { | ||||||
|  |             inner: exprs, | ||||||
|  |         }) | ||||||
|  |     )); | ||||||
|  | } | ||||||
|  | // impl ToTokens for Api {
 | ||||||
|  | //     fn to_tokens(&self, tokens: &mut Tokens) {
 | ||||||
|  | //         let description = &self.metadata.description;
 | ||||||
|  | //         let method = &self.metadata.method;
 | ||||||
|  | //         let name = &self.metadata.name;
 | ||||||
|  | //         let path = &self.metadata.path;
 | ||||||
|  | //         let rate_limited = &self.metadata.rate_limited;
 | ||||||
|  | //         let requires_authentication = &self.metadata.requires_authentication;
 | ||||||
|  | 
 | ||||||
|  | //         let request_types = {
 | ||||||
|  | //             let mut tokens = Tokens::new();
 | ||||||
|  | //             self.request.to_tokens(&mut tokens);
 | ||||||
|  | //             tokens
 | ||||||
|  | //         };
 | ||||||
|  | //         let response_types = {
 | ||||||
|  | //             let mut tokens = Tokens::new();
 | ||||||
|  | //             self.response.to_tokens(&mut tokens);
 | ||||||
|  | //             tokens
 | ||||||
|  | //         };
 | ||||||
|  | 
 | ||||||
|  | //         let set_request_path = if self.request.has_path_fields() {
 | ||||||
|  | //             let path_str_quoted = path.as_str();
 | ||||||
|  | //             assert!(
 | ||||||
|  | //                 path_str_quoted.starts_with('"') && path_str_quoted.ends_with('"'),
 | ||||||
|  | //                 "path needs to be a string literal"
 | ||||||
|  | //             );
 | ||||||
|  | 
 | ||||||
|  | //             let path_str = &path_str_quoted[1 .. path_str_quoted.len() - 1];
 | ||||||
|  | 
 | ||||||
|  | //             assert!(path_str.starts_with('/'), "path needs to start with '/'");
 | ||||||
|  | //             assert!(
 | ||||||
|  | //                 path_str.chars().filter(|c| *c == ':').count() == self.request.path_field_count(),
 | ||||||
|  | //                 "number of declared path parameters needs to match amount of placeholders in path"
 | ||||||
|  | //             );
 | ||||||
|  | 
 | ||||||
|  | //             let request_path_init_fields = self.request.request_path_init_fields();
 | ||||||
|  | 
 | ||||||
|  | //             let mut tokens = quote! {
 | ||||||
|  | //                 let request_path = RequestPath {
 | ||||||
|  | //                     #request_path_init_fields
 | ||||||
|  | //                 };
 | ||||||
|  | 
 | ||||||
|  | //                 // This `unwrap()` can only fail when the url is a
 | ||||||
|  | //                 // cannot-be-base url like `mailto:` or `data:`, which is not
 | ||||||
|  | //                 // the case for our placeholder url.
 | ||||||
|  | //                 let mut path_segments = url.path_segments_mut().unwrap();
 | ||||||
|  | //             };
 | ||||||
|  | 
 | ||||||
|  | //             for segment in path_str[1..].split('/') {
 | ||||||
|  | //                 tokens.append(quote! {
 | ||||||
|  | //                     path_segments.push
 | ||||||
|  | //                 });
 | ||||||
|  | 
 | ||||||
|  | //                 tokens.append("(");
 | ||||||
|  | 
 | ||||||
|  | //                 if segment.starts_with(':') {
 | ||||||
|  | //                     tokens.append("&request_path.");
 | ||||||
|  | //                     tokens.append(&segment[1..]);
 | ||||||
|  | //                     tokens.append(".to_string()");
 | ||||||
|  | //                 } else {
 | ||||||
|  | //                     tokens.append("\"");
 | ||||||
|  | //                     tokens.append(segment);
 | ||||||
|  | //                     tokens.append("\"");
 | ||||||
|  | //                 }
 | ||||||
|  | 
 | ||||||
|  | //                 tokens.append(");");
 | ||||||
|  | //             }
 | ||||||
|  | 
 | ||||||
|  | //             tokens
 | ||||||
|  | //         } else {
 | ||||||
|  | //             quote! {
 | ||||||
|  | //                 url.set_path(metadata.path);
 | ||||||
|  | //             }
 | ||||||
|  | //         };
 | ||||||
|  | 
 | ||||||
|  | //         let set_request_query = if self.request.has_query_fields() {
 | ||||||
|  | //             let request_query_init_fields = self.request.request_query_init_fields();
 | ||||||
|  | 
 | ||||||
|  | //             quote! {
 | ||||||
|  | //                 let request_query = RequestQuery {
 | ||||||
|  | //                     #request_query_init_fields
 | ||||||
|  | //                 };
 | ||||||
|  | 
 | ||||||
|  | //                 url.set_query(Some(&::serde_urlencoded::to_string(request_query)?));
 | ||||||
|  | //             }
 | ||||||
|  | //         } else {
 | ||||||
|  | //             Tokens::new()
 | ||||||
|  | //         };
 | ||||||
|  | 
 | ||||||
|  | //         let add_body_to_request = if let Some(field) = self.request.newtype_body_field() {
 | ||||||
|  | //             let field_name = field.ident.as_ref().expect("expected body field to have a name");
 | ||||||
|  | 
 | ||||||
|  | //             quote! {
 | ||||||
|  | //                 let request_body = RequestBody(request.#field_name);
 | ||||||
|  | 
 | ||||||
|  | //                 hyper_request.set_body(::serde_json::to_vec(&request_body)?);
 | ||||||
|  | //             }
 | ||||||
|  | //         } else if self.request.has_body_fields() {
 | ||||||
|  | //             let request_body_init_fields = self.request.request_body_init_fields();
 | ||||||
|  | 
 | ||||||
|  | //             quote! {
 | ||||||
|  | //                 let request_body = RequestBody {
 | ||||||
|  | //                     #request_body_init_fields
 | ||||||
|  | //                 };
 | ||||||
|  | 
 | ||||||
|  | //                 hyper_request.set_body(::serde_json::to_vec(&request_body)?);
 | ||||||
|  | //             }
 | ||||||
|  | //         } else {
 | ||||||
|  | //             Tokens::new()
 | ||||||
|  | //         };
 | ||||||
|  | 
 | ||||||
|  | //         let deserialize_response_body = if let Some(field) = self.response.newtype_body_field() {
 | ||||||
|  | //             let field_type = &field.ty;
 | ||||||
|  | //             let mut tokens = Tokens::new();
 | ||||||
|  | 
 | ||||||
|  | //             tokens.append(quote! {
 | ||||||
|  | //                 let future_response = hyper_response.body()
 | ||||||
|  | //                     .fold::<_, _, Result<_, ::std::io::Error>>(Vec::new(), |mut bytes, chunk| {
 | ||||||
|  | //                         bytes.write_all(&chunk)?;
 | ||||||
|  | 
 | ||||||
|  | //                         Ok(bytes)
 | ||||||
|  | //                     })
 | ||||||
|  | //                     .map_err(::ruma_api::Error::from)
 | ||||||
|  | //                     .and_then(|bytes| {
 | ||||||
|  | //                         ::serde_json::from_slice::<#field_type>(bytes.as_slice())
 | ||||||
|  | //                             .map_err(::ruma_api::Error::from)
 | ||||||
|  | //                     })
 | ||||||
|  | //             });
 | ||||||
|  | 
 | ||||||
|  | //             tokens.append(".and_then(move |response_body| {");
 | ||||||
|  | 
 | ||||||
|  | //             tokens
 | ||||||
|  | //         } else if self.response.has_body_fields() {
 | ||||||
|  | //             let mut tokens = Tokens::new();
 | ||||||
|  | 
 | ||||||
|  | //             tokens.append(quote! {
 | ||||||
|  | //                 let future_response = hyper_response.body()
 | ||||||
|  | //                     .fold::<_, _, Result<_, ::std::io::Error>>(Vec::new(), |mut bytes, chunk| {
 | ||||||
|  | //                         bytes.write_all(&chunk)?;
 | ||||||
|  | 
 | ||||||
|  | //                         Ok(bytes)
 | ||||||
|  | //                     })
 | ||||||
|  | //                     .map_err(::ruma_api::Error::from)
 | ||||||
|  | //                     .and_then(|bytes| {
 | ||||||
|  | //                         ::serde_json::from_slice::<ResponseBody>(bytes.as_slice())
 | ||||||
|  | //                             .map_err(::ruma_api::Error::from)
 | ||||||
|  | //                     })
 | ||||||
|  | //             });
 | ||||||
|  | 
 | ||||||
|  | //             tokens.append(".and_then(move |response_body| {");
 | ||||||
|  | 
 | ||||||
|  | //             tokens
 | ||||||
|  | //         } else {
 | ||||||
|  | //             let mut tokens = Tokens::new();
 | ||||||
|  | 
 | ||||||
|  | //             tokens.append(quote! {
 | ||||||
|  | //                 let future_response = ::futures::future::ok(())
 | ||||||
|  | //             });
 | ||||||
|  | 
 | ||||||
|  | //             tokens.append(".and_then(move |_| {");
 | ||||||
|  | 
 | ||||||
|  | //             tokens
 | ||||||
|  | //         };
 | ||||||
|  | 
 | ||||||
|  | //         let mut closure_end = Tokens::new();
 | ||||||
|  | //         closure_end.append("});");
 | ||||||
|  | 
 | ||||||
|  | //         let extract_headers = if self.response.has_header_fields() {
 | ||||||
|  | //             quote! {
 | ||||||
|  | //                 let mut headers = hyper_response.headers().clone();
 | ||||||
|  | //             }
 | ||||||
|  | //         } else {
 | ||||||
|  | //             Tokens::new()
 | ||||||
|  | //         };
 | ||||||
|  | 
 | ||||||
|  | //         let response_init_fields = if self.response.has_fields() {
 | ||||||
|  | //             self.response.init_fields()
 | ||||||
|  | //         } else {
 | ||||||
|  | //             Tokens::new()
 | ||||||
|  | //         };
 | ||||||
|  | 
 | ||||||
|  | //         tokens.append(quote! {
 | ||||||
|  | //             #[allow(unused_imports)]
 | ||||||
|  | //             use std::io::Write as _Write;
 | ||||||
|  | 
 | ||||||
|  | //             #[allow(unused_imports)]
 | ||||||
|  | //             use ::futures::{Future as _Future, Stream as _Stream};
 | ||||||
|  | //             use ::ruma_api::Endpoint as _RumaApiEndpoint;
 | ||||||
|  | 
 | ||||||
|  | //             /// The API endpoint.
 | ||||||
|  | //             #[derive(Debug)]
 | ||||||
|  | //             pub struct Endpoint;
 | ||||||
|  | 
 | ||||||
|  | //             #request_types
 | ||||||
|  | 
 | ||||||
|  | //             impl ::std::convert::TryFrom<Request> for ::hyper::Request {
 | ||||||
|  | //                 type Error = ::ruma_api::Error;
 | ||||||
|  | 
 | ||||||
|  | //                 #[allow(unused_mut, unused_variables)]
 | ||||||
|  | //                 fn try_from(request: Request) -> Result<Self, Self::Error> {
 | ||||||
|  | //                     let metadata = Endpoint::METADATA;
 | ||||||
|  | 
 | ||||||
|  | //                     // Use dummy homeserver url which has to be overwritten in
 | ||||||
|  | //                     // the calling code. Previously (with hyper::Uri) this was
 | ||||||
|  | //                     // not required, but Url::parse only accepts absolute urls.
 | ||||||
|  | //                     let mut url = ::url::Url::parse("http://invalid-host-please-change/").unwrap();
 | ||||||
|  | 
 | ||||||
|  | //                     { #set_request_path }
 | ||||||
|  | //                     { #set_request_query }
 | ||||||
|  | 
 | ||||||
|  | //                     let mut hyper_request = ::hyper::Request::new(
 | ||||||
|  | //                         metadata.method,
 | ||||||
|  | //                         // Every valid URL is a valid URI
 | ||||||
|  | //                         url.into_string().parse().unwrap(),
 | ||||||
|  | //                     );
 | ||||||
|  | 
 | ||||||
|  | //                     { #add_body_to_request }
 | ||||||
|  | 
 | ||||||
|  | //                     Ok(hyper_request)
 | ||||||
|  | //                 }
 | ||||||
|  | //             }
 | ||||||
|  | 
 | ||||||
|  | //             #response_types
 | ||||||
|  | 
 | ||||||
|  | //             impl ::futures::future::FutureFrom<::hyper::Response> for Response {
 | ||||||
|  | //                 type Future = Box<_Future<Item = Self, Error = Self::Error>>;
 | ||||||
|  | //                 type Error = ::ruma_api::Error;
 | ||||||
|  | 
 | ||||||
|  | //                 #[allow(unused_variables)]
 | ||||||
|  | //                 fn future_from(hyper_response: ::hyper::Response)
 | ||||||
|  | //                 -> Box<_Future<Item = Self, Error = Self::Error>> {
 | ||||||
|  | //                     #extract_headers
 | ||||||
|  | 
 | ||||||
|  | //                     #deserialize_response_body
 | ||||||
|  | 
 | ||||||
|  | //                     let response = Response {
 | ||||||
|  | //                         #response_init_fields
 | ||||||
|  | //                     };
 | ||||||
|  | 
 | ||||||
|  | //                     Ok(response)
 | ||||||
|  | //                     #closure_end
 | ||||||
|  | 
 | ||||||
|  | //                     Box::new(future_response)
 | ||||||
|  | //                 }
 | ||||||
|  | //             }
 | ||||||
|  | 
 | ||||||
|  | //             impl ::ruma_api::Endpoint for Endpoint {
 | ||||||
|  | //                 type Request = Request;
 | ||||||
|  | //                 type Response = Response;
 | ||||||
|  | 
 | ||||||
|  | //                 const METADATA: ::ruma_api::Metadata = ::ruma_api::Metadata {
 | ||||||
|  | //                     description: #description,
 | ||||||
|  | //                     method: ::hyper::#method,
 | ||||||
|  | //                     name: #name,
 | ||||||
|  | //                     path: #path,
 | ||||||
|  | //                     rate_limited: #rate_limited,
 | ||||||
|  | //                     requires_authentication: #requires_authentication,
 | ||||||
|  | //                 };
 | ||||||
|  | //             }
 | ||||||
|  | //         });
 | ||||||
|  | //     }
 | ||||||
|  | // }
 | ||||||
|  | 
 | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| use quote::{ToTokens, Tokens}; | use quote::{ToTokens, Tokens}; | ||||||
| use syn::{Field, MetaItem, NestedMetaItem}; | use syn::synom::Synom; | ||||||
|  | use syn::{Field, FieldsNamed, Meta, NestedMeta}; | ||||||
| 
 | 
 | ||||||
| use api::strip_serde_attrs; | use api::strip_serde_attrs; | ||||||
| 
 | 
 | ||||||
| @ -8,6 +9,15 @@ pub struct Request { | |||||||
|     fields: Vec<RequestField>, |     fields: Vec<RequestField>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | impl Synom for Request { | ||||||
|  |     named!(parse -> Self, do_parse!( | ||||||
|  |         fields: syn!(FieldsNamed) >> | ||||||
|  |         (Request { | ||||||
|  |             fields, | ||||||
|  |         }) | ||||||
|  |     )); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| impl Request { | impl Request { | ||||||
|     pub fn has_body_fields(&self) -> bool { |     pub fn has_body_fields(&self) -> bool { | ||||||
|         self.fields.iter().any(|field| field.is_body()) |         self.fields.iter().any(|field| field.is_body()) | ||||||
| @ -74,7 +84,7 @@ impl From<Vec<Field>> for Request { | |||||||
| 
 | 
 | ||||||
|             field.attrs = field.attrs.into_iter().filter(|attr| { |             field.attrs = field.attrs.into_iter().filter(|attr| { | ||||||
|                 let (attr_ident, nested_meta_items) = match attr.value { |                 let (attr_ident, nested_meta_items) = match attr.value { | ||||||
|                     MetaItem::List(ref attr_ident, ref nested_meta_items) => (attr_ident, nested_meta_items), |                     Meta::List(ref attr_ident, ref nested_meta_items) => (attr_ident, nested_meta_items), | ||||||
|                     _ => return true, |                     _ => return true, | ||||||
|                 }; |                 }; | ||||||
| 
 | 
 | ||||||
| @ -84,9 +94,9 @@ impl From<Vec<Field>> for Request { | |||||||
| 
 | 
 | ||||||
|                 for nested_meta_item in nested_meta_items { |                 for nested_meta_item in nested_meta_items { | ||||||
|                     match *nested_meta_item { |                     match *nested_meta_item { | ||||||
|                         NestedMetaItem::MetaItem(ref meta_item) => { |                         NestedMeta::Meta(ref meta_item) => { | ||||||
|                             match *meta_item { |                             match *meta_item { | ||||||
|                                 MetaItem::Word(ref ident) => { |                                 Meta::Word(ref ident) => { | ||||||
|                                     if ident == "body" { |                                     if ident == "body" { | ||||||
|                                         has_newtype_body = true; |                                         has_newtype_body = true; | ||||||
|                                         request_field_kind = RequestFieldKind::NewtypeBody; |                                         request_field_kind = RequestFieldKind::NewtypeBody; | ||||||
| @ -107,7 +117,7 @@ impl From<Vec<Field>> for Request { | |||||||
|                                 ), |                                 ), | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                         NestedMetaItem::Literal(_) => panic!( |                         NestedMeta::Literal(_) => panic!( | ||||||
|                             "ruma_api! attribute meta item on requests must be: body, header, path, or query" |                             "ruma_api! attribute meta item on requests must be: body, header, path, or query" | ||||||
|                         ), |                         ), | ||||||
|                     } |                     } | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| use quote::{ToTokens, Tokens}; | use quote::{ToTokens, Tokens}; | ||||||
| use syn::{Field, MetaItem, NestedMetaItem}; | use syn::{Field, Meta, NestedMeta}; | ||||||
| 
 | 
 | ||||||
| use api::strip_serde_attrs; | use api::strip_serde_attrs; | ||||||
| 
 | 
 | ||||||
| @ -83,7 +83,7 @@ impl From<Vec<Field>> for Response { | |||||||
| 
 | 
 | ||||||
|             field.attrs = field.attrs.into_iter().filter(|attr| { |             field.attrs = field.attrs.into_iter().filter(|attr| { | ||||||
|                 let (attr_ident, nested_meta_items) = match attr.value { |                 let (attr_ident, nested_meta_items) = match attr.value { | ||||||
|                     MetaItem::List(ref attr_ident, ref nested_meta_items) => { |                     Meta::List(ref attr_ident, ref nested_meta_items) => { | ||||||
|                         (attr_ident, nested_meta_items) |                         (attr_ident, nested_meta_items) | ||||||
|                     } |                     } | ||||||
|                     _ => return true, |                     _ => return true, | ||||||
| @ -95,9 +95,9 @@ impl From<Vec<Field>> for Response { | |||||||
| 
 | 
 | ||||||
|                 for nested_meta_item in nested_meta_items { |                 for nested_meta_item in nested_meta_items { | ||||||
|                     match *nested_meta_item { |                     match *nested_meta_item { | ||||||
|                         NestedMetaItem::MetaItem(ref meta_item) => { |                         NestedMeta::Meta(ref meta_item) => { | ||||||
|                             match *meta_item { |                             match *meta_item { | ||||||
|                                 MetaItem::Word(ref ident) => { |                                 Meta::Word(ref ident) => { | ||||||
|                                     if ident == "body" { |                                     if ident == "body" { | ||||||
|                                         has_newtype_body = true; |                                         has_newtype_body = true; | ||||||
|                                         response_field_kind = ResponseFieldKind::NewtypeBody; |                                         response_field_kind = ResponseFieldKind::NewtypeBody; | ||||||
| @ -114,7 +114,7 @@ impl From<Vec<Field>> for Response { | |||||||
|                                 ), |                                 ), | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                         NestedMetaItem::Literal(_) => panic!( |                         NestedMeta::Literal(_) => panic!( | ||||||
|                             "ruma_api! attribute meta item on responses must be: header" |                             "ruma_api! attribute meta item on responses must be: header" | ||||||
|                         ), |                         ), | ||||||
|                     } |                     } | ||||||
|  | |||||||
							
								
								
									
										22
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								src/lib.rs
									
									
									
									
									
								
							| @ -4,24 +4,24 @@ | |||||||
| //! See the documentation for the `ruma_api!` macro for usage details.
 | //! See the documentation for the `ruma_api!` macro for usage details.
 | ||||||
| 
 | 
 | ||||||
| #![deny(missing_debug_implementations)] | #![deny(missing_debug_implementations)] | ||||||
| #![feature(proc_macro)] | #![feature(proc_macro, try_from)] | ||||||
| #![recursion_limit="256"] | #![recursion_limit="256"] | ||||||
|  | #![allow(warnings)] | ||||||
| 
 | 
 | ||||||
| extern crate proc_macro; | extern crate proc_macro; | ||||||
| #[macro_use] extern crate quote; | #[macro_use] extern crate quote; | ||||||
| extern crate ruma_api; | extern crate ruma_api; | ||||||
| extern crate syn; | #[macro_use] extern crate syn; | ||||||
| #[macro_use] extern crate synom; |  | ||||||
| 
 | 
 | ||||||
| use proc_macro::TokenStream; | use proc_macro::TokenStream; | ||||||
|  | use std::convert::TryFrom; | ||||||
| 
 | 
 | ||||||
| use quote::{ToTokens, Tokens}; | use quote::{ToTokens, Tokens}; | ||||||
| 
 | 
 | ||||||
| use api::Api; | use api::{Api, Exprs}; | ||||||
| use parse::parse_entries; |  | ||||||
| 
 | 
 | ||||||
| mod api; | mod api; | ||||||
| mod parse; | // mod parse;
 | ||||||
| 
 | 
 | ||||||
| /// Generates a `ruma_api::Endpoint` from a concise definition.
 | /// Generates a `ruma_api::Endpoint` from a concise definition.
 | ||||||
| ///
 | ///
 | ||||||
| @ -196,13 +196,15 @@ mod parse; | |||||||
| /// ```
 | /// ```
 | ||||||
| #[proc_macro] | #[proc_macro] | ||||||
| pub fn ruma_api(input: TokenStream) -> TokenStream { | pub fn ruma_api(input: TokenStream) -> TokenStream { | ||||||
|     let entries = parse_entries(&input.to_string()).expect("ruma_api! failed to parse input"); |     let exprs: Exprs = syn::parse(input).expect("ruma_api! failed to parse input"); | ||||||
| 
 |     let api = match Api::try_from(exprs.inner) { | ||||||
|     let api = Api::from(entries); |         Ok(api) => api, | ||||||
|  |         Err(error) => panic!("{}", error), | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     let mut tokens = Tokens::new(); |     let mut tokens = Tokens::new(); | ||||||
| 
 | 
 | ||||||
|     api.to_tokens(&mut tokens); |     api.to_tokens(&mut tokens); | ||||||
| 
 | 
 | ||||||
|     tokens.parse().expect("ruma_api! failed to parse output tokens as a TokenStream") |     tokens.into() | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										79
									
								
								src/parse.rs
									
									
									
									
									
								
							
							
						
						
									
										79
									
								
								src/parse.rs
									
									
									
									
									
								
							| @ -6,12 +6,12 @@ use syn::{ | |||||||
|     Expr, |     Expr, | ||||||
|     Field, |     Field, | ||||||
|     Ident, |     Ident, | ||||||
|     MetaItem, |     Meta, | ||||||
|     NestedMetaItem, |     NestedMeta, | ||||||
|     Visibility, |     Visibility, | ||||||
| }; | }; | ||||||
| use syn::parse::{expr, ident, lit, ty}; | // use syn::parse::{expr, ident, lit, ty};
 | ||||||
| use synom::space::{block_comment, whitespace}; | // use synom::space::{block_comment, whitespace};
 | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub enum Entry { | pub enum Entry { | ||||||
| @ -27,27 +27,24 @@ named!(pub parse_entries -> Vec<Entry>, do_parse!( | |||||||
| 
 | 
 | ||||||
| named!(entry -> Entry, alt!( | named!(entry -> Entry, alt!( | ||||||
|     do_parse!( |     do_parse!( | ||||||
|         keyword!("metadata") >> |         block_type: syn!(Ident) >> | ||||||
|         punct!("{") >> |         cond_reduce!(block_type == "metadata") >> | ||||||
|         fields: many0!(struct_init_field) >> |         brace_and_fields: braces!(many0!(struct_init_field)) >> | ||||||
|         punct!("}") >> |         (Entry::Metadata(brace_and_fields.1)) | ||||||
|         (Entry::Metadata(fields)) |  | ||||||
|     ) |     ) | ||||||
|     | |     | | ||||||
|     do_parse!( |     do_parse!( | ||||||
|         keyword!("request") >> |         block_type: syn!(Ident) >> | ||||||
|         punct!("{") >> |         cond_reduce!(block_type == "request") >> | ||||||
|         fields: terminated_list!(punct!(","), struct_field) >> |         brace_and_fields: braces!(terminated_list!(punct!(","), struct_field)) >> | ||||||
|         punct!("}") >> |         (Entry::Request(brace_and_fields.1)) | ||||||
|         (Entry::Request(fields)) |  | ||||||
|     ) |     ) | ||||||
|     | |     | | ||||||
|     do_parse!( |     do_parse!( | ||||||
|         keyword!("response") >> |         block_type: syn!(Ident) >> | ||||||
|         punct!("{") >> |         cond_reduce!(block_type == "response") >> | ||||||
|         fields: terminated_list!(punct!(","), struct_field) >> |         brace_and_fields: braces!(terminated_list!(punct!(","), struct_field)) >> | ||||||
|         punct!("}") >> |         (Entry::Response(brace_and_fields.1)) | ||||||
|         (Entry::Response(fields)) |  | ||||||
|     ) |     ) | ||||||
| )); | )); | ||||||
| 
 | 
 | ||||||
| @ -55,9 +52,9 @@ named!(entry -> Entry, alt!( | |||||||
| 
 | 
 | ||||||
| named!(struct_init_field -> (Ident, Expr), do_parse!( | named!(struct_init_field -> (Ident, Expr), do_parse!( | ||||||
|     ident: ident >> |     ident: ident >> | ||||||
|     punct!(":") >> |     punct!(:) >> | ||||||
|     expr: expr >> |     expr: expr >> | ||||||
|     punct!(",") >> |     punct!(,) >> | ||||||
|     (ident, expr) |     (ident, expr) | ||||||
| )); | )); | ||||||
| 
 | 
 | ||||||
| @ -65,7 +62,7 @@ named!(struct_field -> Field, do_parse!( | |||||||
|     attrs: many0!(outer_attr) >> |     attrs: many0!(outer_attr) >> | ||||||
|     visibility >> |     visibility >> | ||||||
|     id: ident >> |     id: ident >> | ||||||
|     punct!(":") >> |     punct!(:) >> | ||||||
|     ty: ty >> |     ty: ty >> | ||||||
|     (Field { |     (Field { | ||||||
|         ident: Some(id), |         ident: Some(id), | ||||||
| @ -77,24 +74,24 @@ named!(struct_field -> Field, do_parse!( | |||||||
| 
 | 
 | ||||||
| named!(outer_attr -> Attribute, alt!( | named!(outer_attr -> Attribute, alt!( | ||||||
|     do_parse!( |     do_parse!( | ||||||
|         punct!("#") >> |         punct!(#) >> | ||||||
|         punct!("[") >> |         brackets_and_meta_item: brackets!(meta_item) >> | ||||||
|         meta_item: meta_item >> |  | ||||||
|         punct!("]") >> |  | ||||||
|         (Attribute { |         (Attribute { | ||||||
|             style: AttrStyle::Outer, |             style: AttrStyle::Outer, | ||||||
|             value: meta_item, |             value: brackets_and_meta_item.1, | ||||||
|             is_sugared_doc: false, |             is_sugared_doc: false, | ||||||
|         }) |         }) | ||||||
|     ) |     ) | ||||||
|     | |     | | ||||||
|     do_parse!( |     do_parse!( | ||||||
|         punct!("///") >> |         punct!(/) >> | ||||||
|  |         punct!(/) >> | ||||||
|  |         punct!(/) >> | ||||||
|         not!(tag!("/")) >> |         not!(tag!("/")) >> | ||||||
|         content: take_until!("\n") >> |         content: take_until!("\n") >> | ||||||
|         (Attribute { |         (Attribute { | ||||||
|             style: AttrStyle::Outer, |             style: AttrStyle::Outer, | ||||||
|             value: MetaItem::NameValue( |             value: Meta::NameValue( | ||||||
|                 "doc".into(), |                 "doc".into(), | ||||||
|                 format!("///{}", content).into(), |                 format!("///{}", content).into(), | ||||||
|             ), |             ), | ||||||
| @ -108,7 +105,7 @@ named!(outer_attr -> Attribute, alt!( | |||||||
|         com: block_comment >> |         com: block_comment >> | ||||||
|         (Attribute { |         (Attribute { | ||||||
|             style: AttrStyle::Outer, |             style: AttrStyle::Outer, | ||||||
|             value: MetaItem::NameValue( |             value: Meta::NameValue( | ||||||
|                 "doc".into(), |                 "doc".into(), | ||||||
|                 com.into(), |                 com.into(), | ||||||
|             ), |             ), | ||||||
| @ -117,33 +114,31 @@ named!(outer_attr -> Attribute, alt!( | |||||||
|     ) |     ) | ||||||
| )); | )); | ||||||
| 
 | 
 | ||||||
| named!(meta_item -> MetaItem, alt!( | named!(meta_item -> Meta, alt!( | ||||||
|     do_parse!( |     do_parse!( | ||||||
|         id: ident >> |         id: ident >> | ||||||
|         punct!("(") >> |         parens_and_inner: parens!(terminated_list!(punct!(,), nested_meta_item)) >> | ||||||
|         inner: terminated_list!(punct!(","), nested_meta_item) >> |         (Meta::List(id, parens_and_inner.1)) | ||||||
|         punct!(")") >> |  | ||||||
|         (MetaItem::List(id, inner)) |  | ||||||
|     ) |     ) | ||||||
|     | |     | | ||||||
|     do_parse!( |     do_parse!( | ||||||
|         name: ident >> |         name: ident >> | ||||||
|         punct!("=") >> |         punct!(=) >> | ||||||
|         value: lit >> |         value: lit >> | ||||||
|         (MetaItem::NameValue(name, value)) |         (Meta::NameValue(name, value)) | ||||||
|     ) |     ) | ||||||
|     | |     | | ||||||
|     map!(ident, MetaItem::Word) |     map!(ident, Meta::Word) | ||||||
| )); | )); | ||||||
| 
 | 
 | ||||||
| named!(nested_meta_item -> NestedMetaItem, alt!( | named!(nested_meta_item -> NestedMeta, alt!( | ||||||
|     meta_item => { NestedMetaItem::MetaItem } |     meta_item => { NestedMeta::Meta } | ||||||
|     | |     | | ||||||
|     lit => { NestedMetaItem::Literal } |     lit => { NestedMeta::Literal } | ||||||
| )); | )); | ||||||
| 
 | 
 | ||||||
| named!(visibility -> Visibility, alt!( | named!(visibility -> Visibility, alt!( | ||||||
|     keyword!("pub") => { |_| Visibility::Public } |     keyword!(pub) => { |_| Visibility::Public } | ||||||
|     | |     | | ||||||
|     epsilon!() => { |_| Visibility::Inherited } |     epsilon!() => { |_| Visibility::Inherited } | ||||||
| )); | )); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user