diff --git a/core/codegen/src/bang/mod.rs b/core/codegen/src/bang/mod.rs index 627ea338..58e32ae3 100644 --- a/core/codegen/src/bang/mod.rs +++ b/core/codegen/src/bang/mod.rs @@ -1,12 +1,13 @@ -use devise::Result; -use syn::{Path, punctuated::Punctuated, parse::Parser, Token}; -use syn::spanned::Spanned; -use proc_macro2::TokenStream; - mod uri; mod uri_parsing; mod test_guide; mod export; +mod typed_stream; + +use devise::Result; +use syn::{Path, punctuated::Punctuated, parse::Parser, Token}; +use syn::spanned::Spanned; +use proc_macro2::TokenStream; fn struct_maker_vec( input: proc_macro::TokenStream, @@ -70,3 +71,8 @@ pub fn export_internal(input: proc_macro::TokenStream) -> TokenStream { export::_macro(input) .unwrap_or_else(|diag| diag.emit_as_item_tokens()) } + +pub fn typed_stream(input: proc_macro::TokenStream) -> TokenStream { + typed_stream::_macro(input) + .unwrap_or_else(|diag| diag.emit_as_item_tokens()) +} diff --git a/core/codegen/src/bang/typed_stream.rs b/core/codegen/src/bang/typed_stream.rs new file mode 100644 index 00000000..2199e31f --- /dev/null +++ b/core/codegen/src/bang/typed_stream.rs @@ -0,0 +1,53 @@ +use proc_macro2::TokenStream; +use syn::parse::{Parse, ParseStream, discouraged::Speculative}; + +enum Input { + Type(syn::Type), + Tokens(TokenStream) +} + +struct Invocation { + ty_stream_ty: syn::Path, + stream_mac: syn::Path, + stream_trait: syn::Path, + input: Input, +} + +impl Parse for Invocation { + fn parse(input: ParseStream<'_>) -> syn::Result { + let ty_stream_ty = input.parse()?; + input.parse::()?; + + let stream_mac = input.parse()?; + input.parse::()?; + + let stream_trait = input.parse()?; + input.parse::()?; + + let fork = input.fork(); + let input = match fork.parse() { + Ok(ty) => { + input.advance_to(&fork); + Input::Type(ty) + } + Err(_) => Input::Tokens(input.parse()?) + }; + + Ok(Invocation { ty_stream_ty, stream_mac, stream_trait, input }) + } +} + +/// This macro exists because we want to disambiguate between input of a type +/// and input of an expression that looks like a type. `macro_rules` matches +/// eagerly on a single token, so something like `foo!(for x in 0..10 {})` will +/// match a `($ty)` branch as will anything that starts with a path. +pub fn _macro(input: proc_macro::TokenStream) -> devise::Result { + let i: Invocation = syn::parse(input)?; + let (s_ty, mac, s_trait) = (i.ty_stream_ty, i.stream_mac, i.stream_trait); + let tokens = match i.input { + Input::Tokens(tt) => quote!(#s_ty::from(#mac!(#tt))), + Input::Type(ty) => quote!(#s_ty>), + }; + + Ok(tokens) +} diff --git a/core/codegen/src/lib.rs b/core/codegen/src/lib.rs index e9d5608f..86b0ec36 100644 --- a/core/codegen/src/lib.rs +++ b/core/codegen/src/lib.rs @@ -1345,23 +1345,30 @@ pub fn uri(input: TokenStream) -> TokenStream { emit!(bang::uri_macro(input)) } -#[doc(hidden)] -#[proc_macro] /// Internal macro: `rocket_internal_uri!`. +#[proc_macro] +#[doc(hidden)] pub fn rocket_internal_uri(input: TokenStream) -> TokenStream { emit!(bang::uri_internal_macro(input)) } -#[doc(hidden)] +/// Internal macro: `__typed_stream!`. #[proc_macro] +#[doc(hidden)] +pub fn __typed_stream(input: TokenStream) -> TokenStream { + emit!(bang::typed_stream(input)) +} + /// Private Rocket internal macro: `internal_guide_tests!`. +#[proc_macro] +#[doc(hidden)] pub fn internal_guide_tests(input: TokenStream) -> TokenStream { emit!(bang::guide_tests_internal(input)) } -#[doc(hidden)] -#[proc_macro] /// Private Rocket internal macro: `export!`. +#[proc_macro] +#[doc(hidden)] pub fn export(input: TokenStream) -> TokenStream { emit!(bang::export_internal(input)) } diff --git a/core/codegen/tests/catcher.rs b/core/codegen/tests/catcher.rs index e984560b..d3ecdd70 100644 --- a/core/codegen/tests/catcher.rs +++ b/core/codegen/tests/catcher.rs @@ -7,7 +7,7 @@ use rocket::{Request, Rocket, Build}; use rocket::local::blocking::Client; -use rocket::http::{Status, ContentType}; +use rocket::http::Status; #[catch(404)] fn not_found_0() -> &'static str { "404-0" } #[catch(404)] fn not_found_1(_: &Request) -> &'static str { "404-1" } diff --git a/core/lib/src/response/stream/bytes.rs b/core/lib/src/response/stream/bytes.rs index 731edbe3..8aab6483 100644 --- a/core/lib/src/response/stream/bytes.rs +++ b/core/lib/src/response/stream/bytes.rs @@ -78,7 +78,6 @@ crate::export! { /// See [`struct@ByteStream`] and the [module level /// docs](crate::response::stream#typed-streams) for usage details. macro_rules! ByteStream { - ($T:ty) => ($crate::_typed_stream!(ByteStream, $T)); ($($s:tt)*) => ($crate::_typed_stream!(ByteStream, $($s)*)); } } diff --git a/core/lib/src/response/stream/mod.rs b/core/lib/src/response/stream/mod.rs index c1a70f16..3bd2a6d7 100644 --- a/core/lib/src/response/stream/mod.rs +++ b/core/lib/src/response/stream/mod.rs @@ -223,10 +223,12 @@ crate::export! { #[doc(hidden)] #[macro_export] macro_rules! _typed_stream { - ($S:ident, $T:ty) => ( - $crate::response::stream::$S> - ); ($S:ident, $($t:tt)*) => ( - $crate::response::stream::$S::from($crate::response::stream::stream!($($t)*)) - ); + $crate::__typed_stream! { + $crate::response::stream::$S, + $crate::response::stream::stream, + $crate::futures::stream::Stream, + $($t)* + } + ) } diff --git a/core/lib/src/response/stream/reader.rs b/core/lib/src/response/stream/reader.rs index a0b1ce88..d70c8e5c 100644 --- a/core/lib/src/response/stream/reader.rs +++ b/core/lib/src/response/stream/reader.rs @@ -199,7 +199,6 @@ crate::export! { /// See [`struct@ReaderStream`] and the [module level /// docs](crate::response::stream#typed-streams) for usage details. macro_rules! ReaderStream { - ($T:ty) => ($crate::_typed_stream!(ReaderStream, $T)); ($($s:tt)*) => ($crate::_typed_stream!(ReaderStream, $($s)*)); } } diff --git a/core/lib/src/response/stream/text.rs b/core/lib/src/response/stream/text.rs index e2957db8..7bd23fc3 100644 --- a/core/lib/src/response/stream/text.rs +++ b/core/lib/src/response/stream/text.rs @@ -88,7 +88,6 @@ crate::export! { /// See [`struct@TextStream`] and the [module level /// docs](crate::response::stream#typed-streams) for usage details. macro_rules! TextStream { - ($T:ty) => ($crate::_typed_stream!(TextStream, $T)); ($($s:tt)*) => ($crate::_typed_stream!(TextStream, $($s)*)); } }