diff --git a/README.md b/README.md index 9010b58a..cb5d1f0e 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,6 @@ Rocket is a web framework for Rust (nightly) with a focus on ease-of-use, expressibility, and speed. Here's an example of a complete Rocket application: ```rust -#![feature(proc_macro_hygiene)] - #[macro_use] extern crate rocket; #[get("//")] @@ -19,8 +17,9 @@ fn hello(name: String, age: u8) -> String { format!("Hello, {} year old named {}!", age, name) } -fn main() { - rocket::ignite().mount("/hello", routes![hello]).launch(); +#[launch] +fn rocket() -> rocket::Rocket { + rocket::ignite().mount("/hello", routes![hello]) } ``` diff --git a/contrib/lib/src/databases.rs b/contrib/lib/src/databases.rs index 79067550..2251a8ec 100644 --- a/contrib/lib/src/databases.rs +++ b/contrib/lib/src/databases.rs @@ -62,12 +62,9 @@ //! #[database("sqlite_logs")] //! struct LogsDbConn(diesel::SqliteConnection); //! -//! #[rocket::main] -//! async fn main() { -//! rocket::ignite() -//! .attach(LogsDbConn::fairing()) -//! .launch() -//! .await; +//! #[rocket::launch] +//! fn rocket() -> rocket::Rocket { +//! rocket::ignite().attach(LogsDbConn::fairing()) //! } //! # } fn main() {} //! ``` @@ -152,8 +149,8 @@ //! use std::collections::HashMap; //! use rocket::config::{Config, Environment, Value}; //! -//! #[rocket::main] -//! async fn main() { +//! #[rocket::launch] +//! fn rocket() -> rocket::Rocket { //! let mut database_config = HashMap::new(); //! let mut databases = HashMap::new(); //! @@ -167,7 +164,7 @@ //! .finalize() //! .unwrap(); //! -//! rocket::custom(config).launch().await; +//! rocket::custom(config) //! } //! # } fn main() {} //! ``` @@ -266,8 +263,8 @@ //! #[database("my_db")] //! struct MyDatabase(diesel::SqliteConnection); //! -//! #[rocket::main] -//! async fn main() { +//! #[rocket::launch] +//! fn rocket() -> rocket::Rocket { //! # let mut db_config = HashMap::new(); //! # let mut databases = HashMap::new(); //! # @@ -280,10 +277,7 @@ //! # .finalize() //! # .unwrap(); //! # -//! rocket::custom(config) -//! .attach(MyDatabase::fairing()) -//! .launch() -//! .await; +//! rocket::custom(config).attach(MyDatabase::fairing()) //! } //! # } fn main() {} //! ``` diff --git a/contrib/lib/src/serve.rs b/contrib/lib/src/serve.rs index 9217410f..c643d2ba 100644 --- a/contrib/lib/src/serve.rs +++ b/contrib/lib/src/serve.rs @@ -130,19 +130,14 @@ impl std::ops::BitOr for Options { /// `/public` path, allowing `index.html` files to be used to respond to /// requests for a directory (the default), you might write the following: /// -/// ```rust +/// ```rust,no_run /// # extern crate rocket; /// # extern crate rocket_contrib; /// use rocket_contrib::serve::StaticFiles; /// -/// #[rocket::main] -/// async fn main() { -/// # if false { -/// rocket::ignite() -/// .mount("/public", StaticFiles::from("/static")) -/// .launch() -/// .await; -/// # } +/// #[rocket::launch] +/// fn rocket() -> rocket::Rocket { +/// rocket::ignite().mount("/public", StaticFiles::from("/static")) /// } /// ``` /// @@ -158,19 +153,15 @@ impl std::ops::BitOr for Options { /// For example, to serve files in the `static` subdirectory of your crate at /// `/`, you might write: /// -/// ```rust +/// ```rust,no_run /// # extern crate rocket; /// # extern crate rocket_contrib; /// use rocket_contrib::serve::StaticFiles; /// -/// #[rocket::main] -/// async fn main() { -/// # if false { +/// #[rocket::launch] +/// fn rocket() -> rocket::Rocket { /// rocket::ignite() /// .mount("/", StaticFiles::from(concat!(env!("CARGO_MANIFEST_DIR"), "/static"))) -/// .launch() -/// .await; -/// # } /// } /// ``` #[derive(Clone)] @@ -195,37 +186,27 @@ impl StaticFiles { /// Serve the static files in the `/www/public` local directory on path /// `/static`. /// - /// ```rust + /// ```rust,no_run /// # extern crate rocket; /// # extern crate rocket_contrib; /// use rocket_contrib::serve::StaticFiles; /// - /// #[rocket::main] - /// async fn main() { - /// # if false { - /// rocket::ignite() - /// .mount("/static", StaticFiles::from("/www/public")) - /// .launch() - /// .await; - /// # } + /// #[rocket::launch] + /// fn rocket() -> rocket::Rocket { + /// rocket::ignite().mount("/static", StaticFiles::from("/www/public")) /// } /// ``` /// /// Exactly as before, but set the rank for generated routes to `30`. /// - /// ```rust + /// ```rust,no_run /// # extern crate rocket; /// # extern crate rocket_contrib; /// use rocket_contrib::serve::StaticFiles; /// - /// #[rocket::main] - /// async fn main() { - /// # if false { - /// rocket::ignite() - /// .mount("/static", StaticFiles::from("/www/public").rank(30)) - /// .launch() - /// .await; - /// # } + /// #[rocket::launch] + /// fn rocket() -> rocket::Rocket { + /// rocket::ignite().mount("/static", StaticFiles::from("/www/public").rank(30)) /// } /// ``` pub fn from>(path: P) -> Self { @@ -243,21 +224,17 @@ impl StaticFiles { /// the same files on `/pub` with a route rank of -1 while also serving /// index files and dot files. /// - /// ```rust + /// ```rust,no_run /// # extern crate rocket; /// # extern crate rocket_contrib; /// use rocket_contrib::serve::{StaticFiles, Options}; /// - /// #[rocket::main] - /// async fn main() { - /// # if false { + /// #[rocket::launch] + /// fn rocket() -> rocket::Rocket { /// let options = Options::Index | Options::DotFiles; /// rocket::ignite() /// .mount("/static", StaticFiles::from("/www/public")) /// .mount("/pub", StaticFiles::new("/www/public", options).rank(-1)) - /// .launch() - /// .await; - /// # } /// } /// ``` pub fn new>(path: P, options: Options) -> Self { diff --git a/core/codegen/src/attribute/async_entry.rs b/core/codegen/src/attribute/async_entry.rs index c6ef4eb8..e121129e 100644 --- a/core/codegen/src/attribute/async_entry.rs +++ b/core/codegen/src/attribute/async_entry.rs @@ -1,83 +1,126 @@ use proc_macro::{TokenStream, Span}; -use devise::{syn, Result}; +use devise::{syn, Spanned, Result}; use crate::proc_macro2::TokenStream as TokenStream2; use crate::syn_ext::syn_to_diag; -#[derive(Copy, Clone)] -enum Kind { - Main, - Test, +trait EntryAttr { + /// Whether the attribute requires the attributed function to be `async`. + const REQUIRES_ASYNC: bool; + + /// Return a new or rewritten function, using block as the main execution. + fn function(f: &syn::ItemFn, body: &syn::Block) -> Result; } -impl Kind { - // The name of the attribute, used for error messages - fn attr_name(&self) -> &'static str { - match self { - Kind::Main => "main", - Kind::Test => "async_test", - } - } +struct Main; - // Attributes to decorate the generated function with - fn attrs(&self) -> Option { - match self { - Kind::Main => None, - Kind::Test => Some(quote!{ #[test] }), - } - } +impl EntryAttr for Main { + const REQUIRES_ASYNC: bool = true; - // The path to the function to call - fn fn_path(&self) -> TokenStream2 { - match self { - Kind::Main => quote! { rocket :: async_main }, - Kind::Test => quote! { rocket :: async_test }, + fn function(f: &syn::ItemFn, block: &syn::Block) -> Result { + let (attrs, vis, mut sig) = (&f.attrs, &f.vis, f.sig.clone()); + if sig.ident != "main" { + Span::call_site() + .warning("attribute is typically applied to `main` function") + .span_note(sig.span(), "this function is not `main`") + .emit(); } + + sig.asyncness = None; + Ok(quote_spanned!(block.span().into() => #(#attrs)* #vis #sig { + ::rocket::async_main(async move #block) + })) } } -fn parse_input(input: TokenStream, attr_name: &str) -> Result { - let function: syn::ItemFn = syn::parse(input).map_err(syn_to_diag) - .map_err(|diag| diag.help(format!("`#[{}]` can only be applied to async functions", attr_name)))?; +struct Test; - if function.sig.asyncness.is_none() { - return Err(Span::call_site().error(format!("`#[{}]` can only be applied to async functions", attr_name))) +impl EntryAttr for Test { + const REQUIRES_ASYNC: bool = true; + + fn function(f: &syn::ItemFn, block: &syn::Block) -> Result { + let (attrs, vis, mut sig) = (&f.attrs, &f.vis, f.sig.clone()); + sig.asyncness = None; + Ok(quote_spanned!(block.span().into() => #(#attrs)* #[test] #vis #sig { + ::rocket::async_test(async move #block) + })) + } +} + +struct Launch; + +impl EntryAttr for Launch { + const REQUIRES_ASYNC: bool = false; + + fn function(f: &syn::ItemFn, block: &syn::Block) -> Result { + if f.sig.ident == "main" { + return Err(Span::call_site() + .error("attribute cannot be applied to `main` function") + .note("this attribute generates a `main` function") + .span_note(f.sig.span(), "this function cannot be `main`")); + } + + let ty = match &f.sig.output { + syn::ReturnType::Type(_, ty) => ty, + _ => return Err(Span::call_site() + .error("attribute can only be applied to functions that return a value") + .span_note(f.sig.span(), "this function must return a value")) + }; + + let rocket = quote_spanned!(ty.span().into() => { + let ___rocket: #ty = #block; + let ___rocket: ::rocket::Rocket = ___rocket; + ___rocket + }); + + let (vis, mut sig) = (&f.vis, f.sig.clone()); + sig.ident = syn::Ident::new("main", sig.ident.span()); + sig.output = syn::ReturnType::Default; + sig.asyncness = None; + + Ok(quote_spanned!(block.span().into() => + #[allow(dead_code)] #f + + #vis #sig { + ::rocket::async_main(async move { let _ = #rocket.launch().await; }) + } + )) + } +} + +fn parse_input(input: TokenStream) -> Result { + let function: syn::ItemFn = syn::parse(input) + .map_err(syn_to_diag) + .map_err(|d| d.help("attribute can only be applied to functions"))?; + + if A::REQUIRES_ASYNC && function.sig.asyncness.is_none() { + return Err(Span::call_site() + .error("attribute can only be applied to `async` functions") + .span_note(function.sig.span(), "this function must be `async`")); } if !function.sig.inputs.is_empty() { - return Err(Span::call_site().error(format!("`#[{}]` can only be applied to functions with no parameters", attr_name))); + return Err(Span::call_site() + .error("attribute can only be applied to functions without arguments") + .span_note(function.sig.span(), "this function must take no arguments")); } Ok(function) } -fn _async_entry(_args: TokenStream, input: TokenStream, kind: Kind) -> Result { - let function = parse_input(input, kind.attr_name())?; - - let attrs = &function.attrs; - let vis = &function.vis; - let name = &function.sig.ident; - let output = &function.sig.output; - let body = &function.block; - - let test_attr = kind.attrs(); - let fn_path = kind.fn_path(); - - Ok(quote! { - #test_attr - #(#attrs)* - #vis fn #name() #output { - #fn_path (async move { - #body - }) - } - }.into()) +fn _async_entry(_args: TokenStream, input: TokenStream) -> Result { + let function = parse_input::(input)?; + A::function(&function, &function.block).map(|t| t.into()) } pub fn async_test_attribute(args: TokenStream, input: TokenStream) -> TokenStream { - _async_entry(args, input, Kind::Test).unwrap_or_else(|d| { d.emit(); TokenStream::new() }) + _async_entry::(args, input).unwrap_or_else(|d| { d.emit(); TokenStream::new() }) } pub fn main_attribute(args: TokenStream, input: TokenStream) -> TokenStream { - _async_entry(args, input, Kind::Main).unwrap_or_else(|d| { d.emit(); TokenStream::new() }) + _async_entry::
(args, input).unwrap_or_else(|d| { d.emit(); quote!(fn main() {}).into() }) +} + +pub fn launch_attribute(args: TokenStream, input: TokenStream) -> TokenStream { + _async_entry::(args, input).unwrap_or_else(|d| { d.emit(); quote!(fn main() {}).into() }) } diff --git a/core/codegen/src/lib.rs b/core/codegen/src/lib.rs index 9d4662b3..365a9890 100644 --- a/core/codegen/src/lib.rs +++ b/core/codegen/src/lib.rs @@ -405,16 +405,24 @@ pub fn catch(args: TokenStream, input: TokenStream) -> TokenStream { emit!(attribute::catch::catch_attribute(args, input)) } +/// FIXME: Document. #[proc_macro_attribute] pub fn async_test(args: TokenStream, input: TokenStream) -> TokenStream { emit!(attribute::async_entry::async_test_attribute(args, input)) } +/// FIXME: Document. #[proc_macro_attribute] pub fn main(args: TokenStream, input: TokenStream) -> TokenStream { emit!(attribute::async_entry::main_attribute(args, input)) } +/// FIXME: Document. +#[proc_macro_attribute] +pub fn launch(args: TokenStream, input: TokenStream) -> TokenStream { + emit!(attribute::async_entry::launch_attribute(args, input)) +} + /// Derive for the [`FromFormValue`] trait. /// /// The [`FromFormValue`] derive can be applied to enums with nullary diff --git a/core/codegen/tests/async-entry.rs b/core/codegen/tests/async-entry.rs new file mode 100644 index 00000000..c4851c69 --- /dev/null +++ b/core/codegen/tests/async-entry.rs @@ -0,0 +1,76 @@ +#![allow(dead_code, unused_variables)] + +mod a { + // async launch that is async. + #[rocket::launch] + async fn rocket() -> rocket::Rocket { + let _ = rocket::ignite().launch().await; + rocket::ignite() + } + + async fn use_it() { + let rocket: rocket::Rocket = rocket().await; + } +} + +mod b { + // async launch that isn't async. + #[rocket::launch] + async fn main2() -> rocket::Rocket { + rocket::ignite() + } + + async fn use_it() { + let rocket: rocket::Rocket = main2().await; + } +} + +mod c { + // non-async launch. + #[rocket::launch] + fn rocket() -> rocket::Rocket { + rocket::ignite() + } + + fn use_it() { + let rocket: rocket::Rocket = rocket(); + } +} + +mod d { + // main with async, is async. + #[rocket::main] + async fn main() { + let _ = rocket::ignite().launch().await; + } +} + +mod e { + // main with async, isn't async. + #[rocket::main] + async fn main() { } +} + +mod f { + // main with async, is async, with termination return. + #[rocket::main] + async fn main() -> Result<(), String> { + let result = rocket::ignite().launch().await; + result.map_err(|e| e.to_string()) + } +} + +mod g { + // main with async, isn't async, with termination return. + #[rocket::main] + async fn main() -> Result<(), String> { + Ok(()) + } +} + +// main with async, is async, with termination return. +#[rocket::main] +async fn main() -> Result<(), String> { + let result = rocket::ignite().launch().await; + result.map_err(|e| e.to_string()) +} diff --git a/core/codegen/tests/ui-fail/async-entry.rs b/core/codegen/tests/ui-fail/async-entry.rs new file mode 100644 index 00000000..8f6de514 --- /dev/null +++ b/core/codegen/tests/ui-fail/async-entry.rs @@ -0,0 +1,109 @@ +#![allow(dead_code)] + +// rocket::main + +mod main_a { + #[rocket::main] + fn foo() { } + //~^^ ERROR `async` +} + +mod main_b { + #[rocket::main] + async fn foo() { } + //~^^ WARNING `main` +} + +mod main_d { + #[rocket::main] + fn main() { + //~^^ ERROR `async` + let _ = rocket::ignite().launch().await; + } +} + +mod main_f { + #[rocket::main] + async fn main() { + //~^ ERROR mismatched types + rocket::ignite() + } +} + +// rocket::launch + +mod launch_a { + #[rocket::launch] + async fn rocket() -> String { + //~^ ERROR mismatched types + let _ = rocket::ignite().launch().await; + rocket::ignite() + //~^ ERROR mismatched types + } +} + +mod launch_b { + #[rocket::launch] + async fn rocket() -> rocket::Rocket { + let _ = rocket::ignite().launch().await; + "hi".to_string() + //~^ ERROR mismatched types + } +} + +mod launch_c { + #[rocket::launch] + fn main() -> rocket::Rocket { + //~^^ ERROR `main` + rocket::ignite() + } +} + +mod launch_d { + #[rocket::launch] + async fn rocket() { + //~^^ ERROR functions that return + let _ = rocket::ignite().launch().await; + rocket::ignite() + } +} + +mod launch_e { + #[rocket::launch] + fn rocket() { + //~^^ ERROR functions that return + rocket::ignite() + } +} + +mod launch_f { + #[rocket::launch] + fn rocket() -> rocket::Rocket { + let _ = rocket::ignite().launch().await; + //~^ ERROR only allowed inside `async` + rocket::ignite() + } +} + +mod launch_g { + #[rocket::launch] + fn main() -> &'static str { + //~^^ ERROR `main` + let _ = rocket::ignite().launch().await; + "hi" + } +} + +mod launch_h { + #[rocket::launch] + async fn main() -> rocket::Rocket { + //~^^ ERROR `main` + rocket::ignite() + } +} + +#[rocket::main] +async fn main() -> rocket::Rocket { + //~^ ERROR invalid return type + rocket::ignite() +} diff --git a/core/codegen/tests/ui-fail/async-entry.stderr b/core/codegen/tests/ui-fail/async-entry.stderr new file mode 100644 index 00000000..a5eba8ac --- /dev/null +++ b/core/codegen/tests/ui-fail/async-entry.stderr @@ -0,0 +1,162 @@ +error: attribute can only be applied to `async` functions + --> $DIR/async-entry.rs:6:5 + | +6 | #[rocket::main] + | ^^^^^^^^^^^^^^^ + | +note: this function must be `async` + --> $DIR/async-entry.rs:7:5 + | +7 | fn foo() { } + | ^^^^^^^^ + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: attribute is typically applied to `main` function + --> $DIR/async-entry.rs:12:5 + | +12 | #[rocket::main] + | ^^^^^^^^^^^^^^^ + | +note: this function is not `main` + --> $DIR/async-entry.rs:13:5 + | +13 | async fn foo() { } + | ^^^^^^^^^^^^^^ + = note: this warning originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: attribute can only be applied to `async` functions + --> $DIR/async-entry.rs:18:5 + | +18 | #[rocket::main] + | ^^^^^^^^^^^^^^^ + | +note: this function must be `async` + --> $DIR/async-entry.rs:19:5 + | +19 | fn main() { + | ^^^^^^^^^ + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: attribute cannot be applied to `main` function + --> $DIR/async-entry.rs:55:5 + | +55 | #[rocket::launch] + | ^^^^^^^^^^^^^^^^^ + | + = note: this attribute generates a `main` function +note: this function cannot be `main` + --> $DIR/async-entry.rs:56:5 + | +56 | fn main() -> rocket::Rocket { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: attribute can only be applied to functions that return a value + --> $DIR/async-entry.rs:63:5 + | +63 | #[rocket::launch] + | ^^^^^^^^^^^^^^^^^ + | +note: this function must return a value + --> $DIR/async-entry.rs:64:5 + | +64 | async fn rocket() { + | ^^^^^^^^^^^^^^^^^ + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: attribute can only be applied to functions that return a value + --> $DIR/async-entry.rs:72:5 + | +72 | #[rocket::launch] + | ^^^^^^^^^^^^^^^^^ + | +note: this function must return a value + --> $DIR/async-entry.rs:73:5 + | +73 | fn rocket() { + | ^^^^^^^^^^^ + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: attribute cannot be applied to `main` function + --> $DIR/async-entry.rs:89:5 + | +89 | #[rocket::launch] + | ^^^^^^^^^^^^^^^^^ + | + = note: this attribute generates a `main` function +note: this function cannot be `main` + --> $DIR/async-entry.rs:90:5 + | +90 | fn main() -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: attribute cannot be applied to `main` function + --> $DIR/async-entry.rs:98:5 + | +98 | #[rocket::launch] + | ^^^^^^^^^^^^^^^^^ + | + = note: this attribute generates a `main` function +note: this function cannot be `main` + --> $DIR/async-entry.rs:99:5 + | +99 | async fn main() -> rocket::Rocket { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/async-entry.rs:82:17 + | +81 | fn rocket() -> rocket::Rocket { + | ------ this is not `async` +82 | let _ = rocket::ignite().launch().await; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only allowed inside `async` functions and blocks + +error[E0308]: mismatched types + --> $DIR/async-entry.rs:40:9 + | +40 | rocket::ignite() + | ^^^^^^^^^^^^^^^^ expected struct `std::string::String`, found struct `rocket::rocket::Rocket` + +error[E0308]: mismatched types + --> $DIR/async-entry.rs:49:9 + | +49 | "hi".to_string() + | ^^^^^^^^^^^^^^^^ expected struct `rocket::rocket::Rocket`, found struct `std::string::String` + +error[E0308]: mismatched types + --> $DIR/async-entry.rs:27:21 + | +27 | async fn main() { + | ^ expected `()` because of default return type + | _____________________| + | | +28 | | //~^ ERROR mismatched types +29 | | rocket::ignite() +30 | | } + | | ^- help: try adding a semicolon: `;` + | |_____| + | expected `()`, found struct `rocket::rocket::Rocket` + +error[E0308]: mismatched types + --> $DIR/async-entry.rs:37:26 + | +37 | async fn rocket() -> String { + | ^^^^^^ + | | + | expected struct `rocket::rocket::Rocket`, found struct `std::string::String` + | expected due to this + +error[E0277]: `main` has invalid return type `rocket::rocket::Rocket` + --> $DIR/async-entry.rs:106:20 + | +106 | async fn main() -> rocket::Rocket { + | ^^^^^^^^^^^^^^ `main` can only return types that implement `std::process::Termination` + | + = help: consider using `()`, or a `Result` + +error: aborting due to 13 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0277, E0308, E0728. +For more information about an error, try `rustc --explain E0277`. diff --git a/core/lib/src/catcher.rs b/core/lib/src/catcher.rs index f4f8aeba..40061d72 100644 --- a/core/lib/src/catcher.rs +++ b/core/lib/src/catcher.rs @@ -35,7 +35,7 @@ use yansi::Color::*; /// Catchers should rarely be used directly. Instead, they are typically /// declared using the `catch` decorator, as follows: /// -/// ```rust +/// ```rust,no_run /// #![feature(proc_macro_hygiene)] /// /// #[macro_use] extern crate rocket; @@ -52,11 +52,9 @@ use yansi::Color::*; /// format!("I couldn't find '{}'. Try something else?", req.uri()) /// } /// -/// #[rocket::main] -/// async fn main() { -/// # if false { // We don't actually want to launch the server in an example. -/// rocket::ignite().register(catchers![internal_error, not_found]).launch().await; -/// # } +/// #[rocket::launch] +/// fn rocket() -> rocket::Rocket { +/// rocket::ignite().register(catchers![internal_error, not_found]) /// } /// ``` /// diff --git a/core/lib/src/handler.rs b/core/lib/src/handler.rs index 12eb3308..8d346270 100644 --- a/core/lib/src/handler.rs +++ b/core/lib/src/handler.rs @@ -41,7 +41,7 @@ pub type HandlerFuture<'r> = BoxFuture<'r, Outcome<'r>>; /// /// Such a handler might be written and used as follows: /// -/// ```rust +/// ```rust,no_run /// # #[derive(Copy, Clone)] enum Kind { Simple, Intermediate, Complex, } /// use rocket::{Request, Data, Route, http::Method}; /// use rocket::handler::{self, Handler, Outcome, HandlerFuture}; @@ -65,14 +65,9 @@ pub type HandlerFuture<'r> = BoxFuture<'r, Outcome<'r>>; /// } /// } /// -/// #[rocket::main] -/// async fn main() { -/// # if false { -/// rocket::ignite() -/// .mount("/", CustomHandler(Kind::Simple)) -/// .launch() -/// .await; -/// # } +/// #[rocket::launch] +/// fn rocket() -> rocket::Rocket { +/// rocket::ignite().mount("/", CustomHandler(Kind::Simple)) /// } /// ``` /// @@ -92,7 +87,7 @@ pub type HandlerFuture<'r> = BoxFuture<'r, Outcome<'r>>; /// The previous example could have been implemented using a combination of /// managed state and a static route, as follows: /// -/// ```rust +/// ```rust,no_run /// # #![feature(proc_macro_hygiene)] /// # #[macro_use] extern crate rocket; /// # @@ -114,15 +109,11 @@ pub type HandlerFuture<'r> = BoxFuture<'r, Outcome<'r>>; /// } /// } /// -/// #[rocket::main] -/// async fn main() { -/// # if false { +/// #[rocket::launch] +/// fn rocket() -> rocket::Rocket { /// rocket::ignite() /// .mount("/", routes![custom_handler]) /// .manage(Kind::Simple) -/// .launch() -/// .await; -/// # } /// } /// ``` /// diff --git a/core/lib/src/lib.rs b/core/lib/src/lib.rs index e7a3fa16..5aac7fab 100644 --- a/core/lib/src/lib.rs +++ b/core/lib/src/lib.rs @@ -57,7 +57,7 @@ //! See the [guide](https://rocket.rs/v0.5/guide) for more information on how to //! write Rocket applications. Here's a simple example to get you started: //! -//! ```rust +//! ```rust,no_run //! #![feature(proc_macro_hygiene)] //! //! #[macro_use] extern crate rocket; @@ -67,11 +67,9 @@ //! "Hello, world!" //! } //! -//! #[rocket::main] -//! async fn main() { -//! # if false { // We don't actually want to launch the server in an example. -//! rocket::ignite().mount("/", routes![hello]).launch().await; -//! # } +//! #[rocket::launch] +//! fn rocket() -> rocket::Rocket { +//! rocket::ignite().mount("/", routes![hello]) //! } //! ``` //! diff --git a/core/lib/src/request/state.rs b/core/lib/src/request/state.rs index a40bd842..7a6f71e0 100644 --- a/core/lib/src/request/state.rs +++ b/core/lib/src/request/state.rs @@ -21,7 +21,7 @@ use crate::http::Status; /// like to initialize at start-up and later access it in several handlers. The /// following example does just this: /// -/// ```rust +/// ```rust,no_run /// # #![feature(proc_macro_hygiene)] /// # #[macro_use] extern crate rocket; /// use rocket::State; @@ -42,19 +42,11 @@ use crate::http::Status; /// state.inner().user_val.as_str() /// } /// -/// #[rocket::main] -/// async fn main() { -/// let config = MyConfig { -/// user_val: "user input".to_string() -/// }; -/// -/// # if false { // We don't actually want to launch the server in an example. +/// #[rocket::launch] +/// fn rocket() -> rocket::Rocket { /// rocket::ignite() /// .mount("/", routes![index, raw_config_value]) -/// .manage(config) -/// .launch() -/// .await; -/// # } +/// .manage(MyConfig { user_val: "user input".to_string() }) /// } /// ``` /// diff --git a/core/lib/src/response/flash.rs b/core/lib/src/response/flash.rs index b96757e4..686c3d21 100644 --- a/core/lib/src/response/flash.rs +++ b/core/lib/src/response/flash.rs @@ -44,8 +44,8 @@ const FLASH_COOKIE_DELIM: char = ':'; /// /// # Example /// -/// The following complete Rocket application illustrates the use of a `Flash` -/// message on both the request and response sides. +/// The following routes illustrate the use of a `Flash` message on both the +/// request and response sides. /// /// ```rust /// # #![feature(proc_macro_hygiene)] @@ -68,13 +68,6 @@ const FLASH_COOKIE_DELIM: char = ':'; /// flash.map(|msg| format!("{}: {}", msg.name(), msg.msg())) /// .unwrap_or_else(|| "Welcome!".to_string()) /// } -/// -/// #[rocket::main] -/// async fn main() { -/// # if false { // We don't actually want to launch the server in an example. -/// rocket::ignite().mount("/", routes![login, index]).launch().await; -/// # } -/// } /// ``` /// /// On the response side (in `login`), a `Flash` error message is set if some diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index 320dd3fc..f9e054fa 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -647,7 +647,7 @@ impl Rocket { /// generation facilities. Requests to the `/hello/world` URI will be /// dispatched to the `hi` route. /// - /// ```rust + /// ```rust,no_run /// # #![feature(proc_macro_hygiene)] /// # #[macro_use] extern crate rocket; /// # @@ -656,12 +656,9 @@ impl Rocket { /// "Hello!" /// } /// - /// #[rocket::main] - /// async fn main() { - /// # if false { // We don't actually want to launch the server in an example. + /// #[rocket::launch] + /// fn rocket() -> rocket::Rocket { /// rocket::ignite().mount("/hello", routes![hi]) - /// # .launch().await; - /// # } /// } /// ``` /// @@ -704,7 +701,7 @@ impl Rocket { /// /// # Examples /// - /// ```rust + /// ```rust,no_run /// # #![feature(proc_macro_hygiene)] /// # #[macro_use] extern crate rocket; /// use rocket::Request; @@ -719,13 +716,9 @@ impl Rocket { /// format!("I couldn't find '{}'. Try something else?", req.uri()) /// } /// - /// #[rocket::main] - /// async fn main() { - /// # if false { // We don't actually want to launch the server in an example. - /// rocket::ignite() - /// .register(catchers![internal_error, not_found]) - /// # .launch().await; - /// # } + /// #[rocket::launch] + /// fn rocket() -> rocket::Rocket { + /// rocket::ignite().register(catchers![internal_error, not_found]) /// } /// ``` #[inline] @@ -750,7 +743,7 @@ impl Rocket { /// /// # Example /// - /// ```rust + /// ```rust,no_run /// # #![feature(proc_macro_hygiene)] /// # #[macro_use] extern crate rocket; /// use rocket::State; @@ -762,15 +755,11 @@ impl Rocket { /// format!("The stateful value is: {}", state.0) /// } /// - /// #[rocket::main] - /// async fn main() { - /// # if false { // We don't actually want to launch the server in an example. + /// #[rocket::launch] + /// fn rocket() -> rocket::Rocket { /// rocket::ignite() /// .mount("/", routes![index]) /// .manage(MyValue(10)) - /// .launch() - /// .await; - /// # } /// } /// ``` #[inline] @@ -793,22 +782,18 @@ impl Rocket { /// /// # Example /// - /// ```rust + /// ```rust,no_run /// # #![feature(proc_macro_hygiene)] /// # #[macro_use] extern crate rocket; /// use rocket::Rocket; /// use rocket::fairing::AdHoc; /// - /// #[rocket::main] - /// async fn main() { - /// # if false { // We don't actually want to launch the server in an example. + /// #[rocket::launch] + /// fn rocket() -> rocket::Rocket { /// rocket::ignite() /// .attach(AdHoc::on_launch("Launch Message", |_| { /// println!("Rocket is launching!"); /// })) - /// .launch() - /// .await; - /// # } /// } /// ``` #[inline] @@ -1126,22 +1111,18 @@ impl Manifest { /// /// # Example /// - /// ```rust + /// ```rust,no_run /// # #![feature(proc_macro_hygiene)] /// # #[macro_use] extern crate rocket; /// use rocket::Rocket; /// use rocket::fairing::AdHoc; /// - /// #[rocket::main] - /// async fn main() { - /// # if false { // We don't actually want to launch the server in an example. + /// #[rocket::launch] + /// fn rocket() -> rocket::Rocket { /// rocket::ignite() /// .attach(AdHoc::on_launch("Config Printer", |manifest| { /// println!("Rocket launch config: {:?}", manifest.config()); /// })) - /// .launch() - /// .await; - /// # } /// } /// ``` #[inline(always)] diff --git a/core/lib/src/shutdown.rs b/core/lib/src/shutdown.rs index 4a05d054..6d87ad9e 100644 --- a/core/lib/src/shutdown.rs +++ b/core/lib/src/shutdown.rs @@ -12,7 +12,7 @@ use tokio::sync::mpsc; /// /// # Example /// -/// ```rust +/// ```rust,no_run /// # #![feature(proc_macro_hygiene)] /// # #[macro_use] extern crate rocket; /// # @@ -26,13 +26,13 @@ use tokio::sync::mpsc; /// /// #[rocket::main] /// async fn main() { -/// # if false { -/// rocket::ignite() +/// let result = rocket::ignite() /// .mount("/", routes![shutdown]) /// .launch() -/// .await -/// .expect("server failed unexpectedly"); -/// # } +/// .await; +/// +/// // If the server shut down (by visting `/shutdown`), `result` is `Ok`. +/// result.expect("server failed unexpectedly"); /// } /// ``` #[derive(Debug, Clone)] diff --git a/examples/config/src/main.rs b/examples/config/src/main.rs index 4f246302..4ae267cc 100644 --- a/examples/config/src/main.rs +++ b/examples/config/src/main.rs @@ -1,5 +1,3 @@ // This example's illustration is the Rocket.toml file. -#[rocket::main] -async fn main() { - let _ = rocket::ignite().launch().await; -} +#[rocket::launch] +fn rocket() -> rocket::Rocket { rocket::ignite() } diff --git a/examples/content_types/src/main.rs b/examples/content_types/src/main.rs index 777fc2e5..ce984471 100644 --- a/examples/content_types/src/main.rs +++ b/examples/content_types/src/main.rs @@ -63,10 +63,9 @@ fn not_found(request: &Request<'_>) -> Html { Html(html) } -#[rocket::main] -async fn main() { - let _ = rocket::ignite() +#[rocket::launch] +fn rocket() -> rocket::Rocket { + rocket::ignite() .mount("/hello", routes![get_hello, post_hello]) .register(catchers![not_found]) - .launch().await; } diff --git a/examples/content_types/src/tests.rs b/examples/content_types/src/tests.rs index 3e0ff50c..6c251d65 100644 --- a/examples/content_types/src/tests.rs +++ b/examples/content_types/src/tests.rs @@ -5,11 +5,7 @@ use rocket::local::Client; async fn test(method: Method, uri: &str, header: H, status: Status, body: String) where H: Into> { - let rocket = rocket::ignite() - .mount("/hello", routes![super::get_hello, super::post_hello]) - .register(catchers![super::not_found]); - - let client = Client::new(rocket).await.unwrap(); + let client = Client::new(super::rocket()).await.unwrap(); let mut response = client.req(method, uri).header(header).dispatch().await; assert_eq!(response.status(), status); assert_eq!(response.body_string().await, Some(body)); diff --git a/examples/cookies/src/main.rs b/examples/cookies/src/main.rs index 6e669759..d46b2fb8 100644 --- a/examples/cookies/src/main.rs +++ b/examples/cookies/src/main.rs @@ -34,11 +34,7 @@ fn index(cookies: Cookies<'_>) -> Template { Template::render("index", &context) } +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite().mount("/", routes![submit, index]).attach(Template::fairing()) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/fairings/src/main.rs b/examples/fairings/src/main.rs index d810a72d..023f166e 100644 --- a/examples/fairings/src/main.rs +++ b/examples/fairings/src/main.rs @@ -63,6 +63,7 @@ fn token(token: State<'_, Token>) -> String { format!("{}", token.0) } +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite() .mount("/", routes![hello, token]) @@ -93,8 +94,3 @@ fn rocket() -> rocket::Rocket { }) })) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/form_kitchen_sink/src/main.rs b/examples/form_kitchen_sink/src/main.rs index bb6dcb05..7fb0b10b 100644 --- a/examples/form_kitchen_sink/src/main.rs +++ b/examples/form_kitchen_sink/src/main.rs @@ -41,11 +41,7 @@ fn index() -> Option { NamedFile::open("static/index.html").ok() } +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite().mount("/", routes![index, sink]) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/form_validation/src/main.rs b/examples/form_validation/src/main.rs index 6f179b6b..3158de04 100644 --- a/examples/form_validation/src/main.rs +++ b/examples/form_validation/src/main.rs @@ -76,12 +76,8 @@ fn user_page(username: &RawStr) -> String { format!("This is {}'s page.", username) } +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite() .mount("/", routes![files::index, files::files, user_page, login]) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/handlebars_templates/src/main.rs b/examples/handlebars_templates/src/main.rs index 2595734e..abf3879d 100644 --- a/examples/handlebars_templates/src/main.rs +++ b/examples/handlebars_templates/src/main.rs @@ -68,6 +68,7 @@ fn wow_helper( Ok(()) } +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite() .mount("/", routes![index, hello, about]) @@ -76,8 +77,3 @@ fn rocket() -> rocket::Rocket { engines.handlebars.register_helper("wow", Box::new(wow_helper)); })) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/hello_2018/src/main.rs b/examples/hello_2018/src/main.rs index a61d9242..d73f65fc 100644 --- a/examples/hello_2018/src/main.rs +++ b/examples/hello_2018/src/main.rs @@ -1,15 +1,11 @@ -#![feature(proc_macro_hygiene)] - #[cfg(test)] mod tests; -use rocket::{get, routes}; - -#[get("/")] +#[rocket::get("/")] fn hello() -> &'static str { "Hello, Rust 2018!" } -#[rocket::main] -async fn main() { - let _ = rocket::ignite().mount("/", routes![hello]).launch().await; +#[rocket::launch] +fn rocket() -> rocket::Rocket { + rocket::ignite().mount("/", rocket::routes![hello]) } diff --git a/examples/hello_2018/src/tests.rs b/examples/hello_2018/src/tests.rs index 28c11e29..15329a9b 100644 --- a/examples/hello_2018/src/tests.rs +++ b/examples/hello_2018/src/tests.rs @@ -1,9 +1,8 @@ -use rocket::{self, routes, local::Client}; +use rocket::{self, local::Client}; #[rocket::async_test] async fn hello_world() { - let rocket = rocket::ignite().mount("/", routes![super::hello]); - let client = Client::new(rocket).await.unwrap(); + let client = Client::new(super::rocket()).await.unwrap(); let mut response = client.get("/").dispatch().await; assert_eq!(response.body_string().await, Some("Hello, Rust 2018!".into())); } diff --git a/examples/hello_person/src/main.rs b/examples/hello_person/src/main.rs index d3321844..b70c560a 100644 --- a/examples/hello_person/src/main.rs +++ b/examples/hello_person/src/main.rs @@ -14,7 +14,7 @@ fn hi(name: String) -> String { name } -#[rocket::main] -async fn main() { - let _ = rocket::ignite().mount("/", routes![hello, hi]).launch().await; +#[rocket::launch] +fn rocket() -> rocket::Rocket { + rocket::ignite().mount("/", routes![hello, hi]) } diff --git a/examples/hello_person/src/tests.rs b/examples/hello_person/src/tests.rs index 644453af..e07fd712 100644 --- a/examples/hello_person/src/tests.rs +++ b/examples/hello_person/src/tests.rs @@ -1,17 +1,13 @@ use rocket::local::Client; use rocket::http::Status; -async fn client() -> Client { - Client::new(rocket::ignite().mount("/", routes![super::hello, super::hi])).await.unwrap() -} - async fn test(uri: String, expected: String) { - let client = client().await; + let client = Client::new(super::rocket()).await.unwrap(); assert_eq!(client.get(&uri).dispatch().await.body_string().await, Some(expected)); } async fn test_404(uri: &'static str) { - let client = client().await; + let client = Client::new(super::rocket()).await.unwrap(); assert_eq!(client.get(uri).dispatch().await.status(), Status::NotFound); } diff --git a/examples/hello_world/src/main.rs b/examples/hello_world/src/main.rs index 04ae994d..0d210d84 100644 --- a/examples/hello_world/src/main.rs +++ b/examples/hello_world/src/main.rs @@ -1,5 +1,3 @@ -#![feature(proc_macro_hygiene)] - #[macro_use] extern crate rocket; #[cfg(test)] mod tests; @@ -9,7 +7,7 @@ fn hello() -> &'static str { "Hello, world!" } -#[rocket::main] -async fn main() { - let _ = rocket::ignite().mount("/", routes![hello]).launch().await; +#[launch] +fn rocket() -> rocket::Rocket { + rocket::ignite().mount("/", routes![hello]) } diff --git a/examples/hello_world/src/tests.rs b/examples/hello_world/src/tests.rs index 604f03da..bb8b62a4 100644 --- a/examples/hello_world/src/tests.rs +++ b/examples/hello_world/src/tests.rs @@ -2,8 +2,7 @@ use rocket::local::Client; #[rocket::async_test] async fn hello_world() { - let rocket = rocket::ignite().mount("/", routes![super::hello]); - let client = Client::new(rocket).await.unwrap(); + let client = Client::new(super::rocket()).await.unwrap(); let mut response = client.get("/").dispatch().await; assert_eq!(response.body_string().await, Some("Hello, world!".into())); } diff --git a/examples/json/src/main.rs b/examples/json/src/main.rs index 9d2a8a9a..930d4d58 100644 --- a/examples/json/src/main.rs +++ b/examples/json/src/main.rs @@ -69,14 +69,10 @@ fn not_found() -> JsonValue { }) } +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite() .mount("/message", routes![new, update, get]) .register(catchers![not_found]) .manage(Mutex::new(HashMap::::new())) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/managed_queue/src/main.rs b/examples/managed_queue/src/main.rs index 595ec825..7c6edc5a 100644 --- a/examples/managed_queue/src/main.rs +++ b/examples/managed_queue/src/main.rs @@ -19,13 +19,9 @@ fn pop(queue: State<'_, LogChannel>) -> Option { queue.0.pop().ok() } +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite() .mount("/", routes![push, pop]) .manage(LogChannel(SegQueue::new())) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/manual_routes/src/main.rs b/examples/manual_routes/src/main.rs index d5581632..ada738a1 100644 --- a/examples/manual_routes/src/main.rs +++ b/examples/manual_routes/src/main.rs @@ -98,6 +98,7 @@ impl Handler for CustomHandler { } } +#[rocket::launch] fn rocket() -> rocket::Rocket { let always_forward = Route::ranked(1, Get, "/", forward); let hello = Route::ranked(2, Get, "/", hi); @@ -117,8 +118,3 @@ fn rocket() -> rocket::Rocket { .mount("/custom", CustomHandler::new("some data here")) .register(vec![not_found_catcher]) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/msgpack/src/main.rs b/examples/msgpack/src/main.rs index 3ef38e02..c4f63fb3 100644 --- a/examples/msgpack/src/main.rs +++ b/examples/msgpack/src/main.rs @@ -23,11 +23,7 @@ fn create(data: MsgPack>) -> String { data.contents.to_string() } +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite().mount("/message", routes![get, create]) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/optional_redirect/src/main.rs b/examples/optional_redirect/src/main.rs index cf821b62..a4b167c5 100644 --- a/examples/optional_redirect/src/main.rs +++ b/examples/optional_redirect/src/main.rs @@ -26,7 +26,7 @@ fn login() -> &'static str { "Hi! That user doesn't exist. Maybe you need to log in?" } -#[rocket::main] -async fn main() { - let _ = rocket::ignite().mount("/", routes![root, user, login]).launch().await; +#[rocket::launch] +fn rocket() -> rocket::Rocket { + rocket::ignite().mount("/", routes![root, user, login]) } diff --git a/examples/optional_redirect/src/tests.rs b/examples/optional_redirect/src/tests.rs index 4d54fc57..b21d207f 100644 --- a/examples/optional_redirect/src/tests.rs +++ b/examples/optional_redirect/src/tests.rs @@ -1,22 +1,15 @@ use rocket::local::Client; use rocket::http::Status; -async fn client() -> Client { - let rocket = rocket::ignite() - .mount("/", routes![super::root, super::user, super::login]); - Client::new(rocket).await.unwrap() - -} - async fn test_200(uri: &str, expected_body: &str) { - let client = client().await; + let client = Client::new(super::rocket()).await.unwrap(); let mut response = client.get(uri).dispatch().await; assert_eq!(response.status(), Status::Ok); assert_eq!(response.body_string().await, Some(expected_body.to_string())); } async fn test_303(uri: &str, expected_location: &str) { - let client = client().await; + let client = Client::new(super::rocket()).await.unwrap(); let response = client.get(uri).dispatch().await; let location_headers: Vec<_> = response.headers().get("Location").collect(); assert_eq!(response.status(), Status::SeeOther); diff --git a/examples/pastebin/src/main.rs b/examples/pastebin/src/main.rs index a0b8ce15..0251bf11 100644 --- a/examples/pastebin/src/main.rs +++ b/examples/pastebin/src/main.rs @@ -51,11 +51,7 @@ fn index() -> &'static str { " } +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite().mount("/", routes![index, upload, retrieve]) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/query_params/src/main.rs b/examples/query_params/src/main.rs index 9ee23e6b..98b56b86 100644 --- a/examples/query_params/src/main.rs +++ b/examples/query_params/src/main.rs @@ -32,11 +32,7 @@ fn hello_20(person: LenientForm) -> String { format!("20 years old? Hi, {}!", person.name) } +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite().mount("/", routes![hello, hello_20]) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/ranking/src/main.rs b/examples/ranking/src/main.rs index f2844d34..83c2d7dd 100644 --- a/examples/ranking/src/main.rs +++ b/examples/ranking/src/main.rs @@ -16,7 +16,7 @@ fn hi(name: String, age: &RawStr) -> String { format!("Hi {}! Your age ({}) is kind of funky.", name, age) } -#[rocket::main] -async fn main() { - let _ = rocket::ignite().mount("/", routes![hi, hello]).launch().await; +#[rocket::launch] +fn rocket() -> rocket::Rocket { + rocket::ignite().mount("/", routes![hi, hello]) } diff --git a/examples/ranking/src/tests.rs b/examples/ranking/src/tests.rs index b9cd1301..6efc886d 100644 --- a/examples/ranking/src/tests.rs +++ b/examples/ranking/src/tests.rs @@ -1,8 +1,7 @@ use rocket::local::Client; async fn test(uri: String, expected: String) { - let rocket = rocket::ignite().mount("/", routes![super::hello, super::hi]); - let client = Client::new(rocket).await.unwrap(); + let client = Client::new(super::rocket()).await.unwrap(); let mut response = client.get(&uri).dispatch().await; assert_eq!(response.body_string().await, Some(expected)); } diff --git a/examples/raw_sqlite/src/main.rs b/examples/raw_sqlite/src/main.rs index d7e3dc93..79211e75 100644 --- a/examples/raw_sqlite/src/main.rs +++ b/examples/raw_sqlite/src/main.rs @@ -2,15 +2,12 @@ #[macro_use] extern crate rocket; -use rusqlite::types::ToSql; - #[cfg(test)] mod tests; use std::sync::Mutex; use rocket::{Rocket, State, response::Debug}; - -use rusqlite::{Connection, Error}; +use rusqlite::{Connection, Error, types::ToSql}; type DbConn = Mutex; @@ -35,6 +32,7 @@ fn hello(db_conn: State<'_, DbConn>) -> Result> { .map_err(Debug) } +#[rocket::launch] fn rocket() -> Rocket { // Open a new in-memory SQLite database. let conn = Connection::open_in_memory().expect("in memory db"); @@ -47,8 +45,3 @@ fn rocket() -> Rocket { .manage(Mutex::new(conn)) .mount("/", routes![hello]) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/raw_upload/src/main.rs b/examples/raw_upload/src/main.rs index 0c717895..5e493d78 100644 --- a/examples/raw_upload/src/main.rs +++ b/examples/raw_upload/src/main.rs @@ -17,11 +17,7 @@ fn index() -> &'static str { "Upload your text files by POSTing them to /upload." } +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite().mount("/", routes![index, upload]) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/redirect/src/main.rs b/examples/redirect/src/main.rs index 82905a80..c6cb01e6 100644 --- a/examples/redirect/src/main.rs +++ b/examples/redirect/src/main.rs @@ -16,7 +16,7 @@ fn login() -> &'static str { "Hi! Please log in before continuing." } -#[rocket::main] -async fn main() { - let _ = rocket::ignite().mount("/", routes![root, login]).launch().await; +#[rocket::launch] +fn rocket() -> rocket::Rocket { + rocket::ignite().mount("/", routes![root, login]) } diff --git a/examples/request_guard/src/main.rs b/examples/request_guard/src/main.rs index 7e17fad9..8e75fcd1 100644 --- a/examples/request_guard/src/main.rs +++ b/examples/request_guard/src/main.rs @@ -22,15 +22,11 @@ fn header_count(header_count: HeaderCount) -> String { format!("Your request contained {} headers!", header_count.0) } +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite().mount("/", routes![header_count]) } -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} - #[cfg(test)] mod test { use rocket::local::Client; diff --git a/examples/request_local_state/src/main.rs b/examples/request_local_state/src/main.rs index db29bad8..30d28e79 100644 --- a/examples/request_local_state/src/main.rs +++ b/examples/request_local_state/src/main.rs @@ -78,13 +78,9 @@ async fn r_async(_g1: Guard3, _g2: Guard4) { // This exists only to run the request guards. } +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite() .manage(Atomics::default()) .mount("/", routes![r_sync, r_async]) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/session/src/main.rs b/examples/session/src/main.rs index 2ee4cf25..cac74831 100644 --- a/examples/session/src/main.rs +++ b/examples/session/src/main.rs @@ -80,13 +80,9 @@ fn index() -> Redirect { Redirect::to(uri!(login_page)) } +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite() .attach(Template::fairing()) .mount("/", routes![index, user_index, login, logout, login_user, login_page]) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/state/src/main.rs b/examples/state/src/main.rs index 8d62f839..b558458f 100644 --- a/examples/state/src/main.rs +++ b/examples/state/src/main.rs @@ -24,13 +24,9 @@ fn count(hit_count: State<'_, HitCount>) -> String { hit_count.0.load(Ordering::Relaxed).to_string() } +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite() .mount("/", routes![index, count]) .manage(HitCount(AtomicUsize::new(0))) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/static_files/src/main.rs b/examples/static_files/src/main.rs index 767d1835..6df85937 100644 --- a/examples/static_files/src/main.rs +++ b/examples/static_files/src/main.rs @@ -5,11 +5,7 @@ extern crate rocket_contrib; use rocket_contrib::serve::StaticFiles; +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite().mount("/", StaticFiles::from("static")) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/stream/src/main.rs b/examples/stream/src/main.rs index 860f82d8..2fec56dd 100644 --- a/examples/stream/src/main.rs +++ b/examples/stream/src/main.rs @@ -22,11 +22,7 @@ async fn file() -> Option> { File::open(FILENAME).await.map(Stream::from).ok() } +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite().mount("/", routes![root, file]) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/tera_templates/src/main.rs b/examples/tera_templates/src/main.rs index 2970e04a..8692be09 100644 --- a/examples/tera_templates/src/main.rs +++ b/examples/tera_templates/src/main.rs @@ -35,14 +35,10 @@ fn not_found(req: &Request<'_>) -> Template { Template::render("error/404", &map) } +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite() .mount("/", routes![index, get]) .attach(Template::fairing()) .register(catchers![not_found]) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/testing/src/main.rs b/examples/testing/src/main.rs index a6f297d0..a035f553 100644 --- a/examples/testing/src/main.rs +++ b/examples/testing/src/main.rs @@ -7,15 +7,11 @@ fn hello() -> &'static str { "Hello, world!" } +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite().mount("/", routes![hello]) } -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} - #[cfg(test)] mod test { use super::rocket; diff --git a/examples/tls/src/main.rs b/examples/tls/src/main.rs index 04ae994d..429f7479 100644 --- a/examples/tls/src/main.rs +++ b/examples/tls/src/main.rs @@ -9,7 +9,7 @@ fn hello() -> &'static str { "Hello, world!" } -#[rocket::main] -async fn main() { - let _ = rocket::ignite().mount("/", routes![hello]).launch().await; +#[rocket::launch] +fn rocket() -> rocket::Rocket { + rocket::ignite().mount("/", routes![hello]) } diff --git a/examples/tls/src/tests.rs b/examples/tls/src/tests.rs index 604f03da..bb8b62a4 100644 --- a/examples/tls/src/tests.rs +++ b/examples/tls/src/tests.rs @@ -2,8 +2,7 @@ use rocket::local::Client; #[rocket::async_test] async fn hello_world() { - let rocket = rocket::ignite().mount("/", routes![super::hello]); - let client = Client::new(rocket).await.unwrap(); + let client = Client::new(super::rocket()).await.unwrap(); let mut response = client.get("/").dispatch().await; assert_eq!(response.body_string().await, Some("Hello, world!".into())); } diff --git a/examples/todo/src/main.rs b/examples/todo/src/main.rs index 08c41641..06bb6cbd 100644 --- a/examples/todo/src/main.rs +++ b/examples/todo/src/main.rs @@ -104,6 +104,7 @@ async fn run_db_migrations(mut rocket: Rocket) -> Result { } } +#[rocket::launch] fn rocket() -> Rocket { rocket::ignite() .attach(DbConn::fairing()) @@ -113,8 +114,3 @@ fn rocket() -> Rocket { .mount("/todo", routes![new, toggle, delete]) .attach(Template::fairing()) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/examples/uuid/src/main.rs b/examples/uuid/src/main.rs index 6a853df4..09ce4d30 100644 --- a/examples/uuid/src/main.rs +++ b/examples/uuid/src/main.rs @@ -33,11 +33,7 @@ fn people(id: Uuid) -> Result { .ok_or_else(|| format!("Person not found for UUID: {}", id))?) } +#[rocket::launch] fn rocket() -> rocket::Rocket { rocket::ignite().mount("/", routes![people]) } - -#[rocket::main] -async fn main() { - let _ = rocket().launch().await; -} diff --git a/site/guide/3-overview.md b/site/guide/3-overview.md index 4ca13caa..9dd1a97a 100644 --- a/site/guide/3-overview.md +++ b/site/guide/3-overview.md @@ -238,9 +238,9 @@ You can find async-ready libraries on [crates.io](https://crates.io) with the ! note - Rocket 0.5 uses the tokio (0.2) runtime. The runtime is started for you - if you use `#[rocket::main]`, but you can still `launch()` a rocket - instance on a custom-built `Runtime`. + Rocket 0.5 uses the tokio (0.2) runtime. The runtime is started for you if you + use `#[rocket::launch]` or `#[rocket::main]`, but you can still `launch()` a + rocket instance on a custom-built `Runtime`. ### Cooperative Multitasking diff --git a/site/guide/4-requests.md b/site/guide/4-requests.md index 1c6ae186..197f5cbe 100644 --- a/site/guide/4-requests.md +++ b/site/guide/4-requests.md @@ -232,7 +232,7 @@ fn main() { ``` Notice the `rank` parameters in `user_int` and `user_str`. If we run this -application with the routes mounted at the root path, as is done in `main` +application with the routes mounted at the root path, as is done in `rocket()` above, requests to `/user/` (such as `/user/123`, `/user/Bob`, and so on) will be routed as follows: diff --git a/site/index.toml b/site/index.toml index 13efdc23..32671951 100644 --- a/site/index.toml +++ b/site/index.toml @@ -47,8 +47,6 @@ margin = 9 [[sections]] title = "Hello, Rocket!" code = ''' - #![feature(proc_macro_hygiene)] - #[macro_use] extern crate rocket; #[get("/hello//")] @@ -56,8 +54,9 @@ code = ''' format!("Hello, {} year old named {}!", age, name) } - fn main() { - rocket::ignite().mount("/", routes![hello]).launch(); + #[launch] + fn rocket() -> rocket::Rocket { + rocket::ignite().mount("/", routes![hello]) } ''' text = ''' diff --git a/site/overview.toml b/site/overview.toml index 80d71347..fc15e986 100644 --- a/site/overview.toml +++ b/site/overview.toml @@ -136,22 +136,23 @@ fn not_found() -> T { ... } name = "Launching" content = ''' Launching a Rocket application is the funnest part! For Rocket to begin -dispatching requests to routes, the routes need to be _mounted_. After mounting, -the application needs to be _launched_. These two steps, usually done in `main`, -look like: +dispatching requests to routes, routes need to be _mounted_. After mounting, the +application needs to be _launched_. These two steps, usually done in a `rocket` +function, look like: ```rust -rocket::ignite() - .mount("/base", routes![index, another]) - .launch(); +#[launch] +fn rocket() -> Rocket { + rocket::ignite().mount("/base", routes![index, another]) +} ``` The `mount` call takes a base path and a set of routes via the `routes!` macro. The base path (`/base` above) is prepended to the path of every route in the -list. This effectively namespaces the routes, allowing for easier composition. - -The `launch` call starts the server. In development, Rocket prints useful -information to the console to let you know everything is okay. +list. This namespaces the routes, allowing for composition. The `#[launch]` +attribute creates a `main` function that starts the server. In development, +Rocket prints useful information to the console to let you know everything is +okay. ```sh 🚀 Rocket has launched from http://localhost:8000