diff --git a/.circleci/config.yml b/.circleci/config.yml index 348153559..22ab1a122 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -244,7 +244,7 @@ workflows: - workspace-fmt matrix: parameters: - framework: ["web-axum", "web-rocket", "web-poem", "web-tide", "web-tower", "web-salvo", "bot-serenity"] + framework: ["web-axum", "web-rocket", "web-poem", "web-tide", "web-tower","web-warp", "web-salvo", "bot-serenity"] - check-standalone: matrix: parameters: diff --git a/Cargo.lock b/Cargo.lock index ac2e5f074..225296d68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -417,7 +417,7 @@ dependencies = [ "pin-project-lite 0.2.9", "tokio", "tokio-rustls 0.23.4", - "tungstenite", + "tungstenite 0.17.3", "webpki-roots", ] @@ -981,6 +981,16 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "buf_redux" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" +dependencies = [ + "memchr", + "safemem", +] + [[package]] name = "bumpalo" version = "3.10.0" @@ -2423,7 +2433,7 @@ dependencies = [ "http", "httpdate", "mime", - "sha-1", + "sha-1 0.10.0", ] [[package]] @@ -3169,7 +3179,7 @@ dependencies = [ "serde", "serde_bytes", "serde_with", - "sha-1", + "sha-1 0.10.0", "sha2 0.10.2", "socket2", "stringprep", @@ -3215,6 +3225,24 @@ dependencies = [ "serde", ] +[[package]] +name = "multipart" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" +dependencies = [ + "buf_redux", + "httparse", + "log", + "mime", + "mime_guess", + "quick-error", + "rand 0.8.5", + "safemem", + "tempfile", + "twoway", +] + [[package]] name = "native-tls" version = "0.2.10" @@ -4564,9 +4592,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.144" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553" dependencies = [ "serde_derive", ] @@ -4592,9 +4620,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.144" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391" dependencies = [ "proc-macro2", "quote", @@ -4621,9 +4649,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" dependencies = [ "indexmap", "itoa", @@ -4704,6 +4732,19 @@ dependencies = [ "url", ] +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "sha-1" version = "0.10.0" @@ -4918,6 +4959,7 @@ dependencies = [ "tracing", "tracing-subscriber", "uuid 1.1.2", + "warp", ] [[package]] @@ -5085,7 +5127,7 @@ dependencies = [ "rand 0.8.5", "serde", "serde_json", - "sha-1", + "sha-1 0.10.0", "sha2 0.10.2", "smallvec", "sqlformat", @@ -5608,6 +5650,19 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "tokio-tungstenite" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511de3f85caf1c98983545490c3d09685fa8eb634e57eec22bb4db271f46cbd8" +dependencies = [ + "futures-util", + "log", + "pin-project", + "tokio", + "tungstenite 0.14.0", +] + [[package]] name = "tokio-util" version = "0.6.10" @@ -5928,6 +5983,25 @@ dependencies = [ "toml", ] +[[package]] +name = "tungstenite" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b2d8558abd2e276b0a8df5c05a2ec762609344191e5fd23e292c910e9165b5" +dependencies = [ + "base64 0.13.0", + "byteorder", + "bytes 1.1.0", + "http", + "httparse", + "log", + "rand 0.8.5", + "sha-1 0.9.8", + "thiserror", + "url", + "utf-8", +] + [[package]] name = "tungstenite" version = "0.17.3" @@ -5942,13 +6016,22 @@ dependencies = [ "log", "rand 0.8.5", "rustls 0.20.6", - "sha-1", + "sha-1 0.10.0", "thiserror", "url", "utf-8", "webpki 0.22.0", ] +[[package]] +name = "twoway" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" +dependencies = [ + "memchr", +] + [[package]] name = "typed-builder" version = "0.10.0" @@ -6224,6 +6307,36 @@ dependencies = [ "try-lock", ] +[[package]] +name = "warp" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cef4e1e9114a4b7f1ac799f16ce71c14de5778500c5450ec6b7b920c55b587e" +dependencies = [ + "bytes 1.1.0", + "futures-channel", + "futures-util", + "headers", + "http", + "hyper", + "log", + "mime", + "mime_guess", + "multipart", + "percent-encoding", + "pin-project", + "scoped-tls", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-stream", + "tokio-tungstenite", + "tokio-util 0.6.10", + "tower-service", + "tracing", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" diff --git a/api/users.toml b/api/users.toml index 6d3a1b46f..40c0e8ab5 100644 --- a/api/users.toml +++ b/api/users.toml @@ -1,15 +1,16 @@ [test-key] name = 'ci' projects = [ - 'hello-world-rocket-app', - 'postgres-rocket-app', - 'hello-world-axum-app', 'websocket-axum-app', 'hello-world-salvo-app', 'authentication-rocket-app', + 'hello-world-axum-app', + 'hello-world-poem-app', + 'hello-world-rocket-app', 'hello-world-tide-app', 'hello-world-tower-app', + 'hello-world-warp-app', + 'postgres-poem-app', + 'postgres-rocket-app', 'postgres-tide-app', - 'hello-world-poem-app', - 'postgres-poem-app' ] diff --git a/cargo-shuttle/src/args.rs b/cargo-shuttle/src/args.rs index 1e02475e1..71facdbd4 100644 --- a/cargo-shuttle/src/args.rs +++ b/cargo-shuttle/src/args.rs @@ -98,26 +98,29 @@ pub struct RunArgs { #[derive(Parser, Debug)] pub struct InitArgs { /// Initialize with axum framework - #[clap(long, conflicts_with_all = &["rocket", "tide", "tower", "poem", "serenity", "salvo"])] + #[clap(long, conflicts_with_all = &["rocket", "tide", "tower", "poem", "serenity", "warp", "salvo"])] pub axum: bool, /// Initialize with actix-web framework - #[clap(long, conflicts_with_all = &["axum", "tide", "tower", "poem", "serenity", "salvo"])] + #[clap(long, conflicts_with_all = &["axum", "tide", "tower", "poem", "serenity", "warp", "salvo"])] pub rocket: bool, /// Initialize with tide framework - #[clap(long, conflicts_with_all = &["axum", "rocket", "tower", "poem", "serenity", "salvo"])] + #[clap(long, conflicts_with_all = &["axum", "rocket", "tower", "poem", "serenity", "warp", "salvo"])] pub tide: bool, /// Initialize with tower framework - #[clap(long, conflicts_with_all = &["axum", "rocket", "tide", "poem", "serenity", "salvo"])] + #[clap(long, conflicts_with_all = &["axum", "rocket", "tide", "poem", "serenity", "warp", "salvo"])] pub tower: bool, /// Initialize with poem framework - #[clap(long, conflicts_with_all = &["axum", "rocket", "tide", "tower", "serenity", "salvo"])] + #[clap(long, conflicts_with_all = &["axum", "rocket", "tide", "tower", "serenity", "warp", "salvo"])] pub poem: bool, /// Initialize with salvo framework - #[clap(long, conflicts_with_all = &["axum", "rocket", "tide", "tower", "poem", "serenity"])] + #[clap(long, conflicts_with_all = &["axum", "rocket", "tide", "tower", "poem", "warp", "serenity"])] pub salvo: bool, /// Initialize with serenity framework - #[clap(long, conflicts_with_all = &["axum", "rocket", "tide", "tower", "poem", "salvo"])] + #[clap(long, conflicts_with_all = &["axum", "rocket", "tide", "tower", "poem", "warp", "salvo"])] pub serenity: bool, + /// Initialize with warp framework + #[clap(long, conflicts_with_all = &["axum", "rocket", "tide", "tower", "poem", "serenity", "salvo"])] + pub warp: bool, /// Path to initialize a new shuttle project #[clap( parse(try_from_os_str = parse_init_path), diff --git a/cargo-shuttle/src/init.rs b/cargo-shuttle/src/init.rs index f35619af5..258c393f0 100644 --- a/cargo-shuttle/src/init.rs +++ b/cargo-shuttle/src/init.rs @@ -444,6 +444,45 @@ impl ShuttleInit for ShuttleInitTower { } } +pub struct ShuttleInitWarp; + +impl ShuttleInit for ShuttleInitWarp { + fn set_cargo_dependencies( + &self, + dependencies: &mut Table, + manifest_path: &Path, + url: &Url, + get_dependency_version_fn: GetDependencyVersionFn, + ) { + set_inline_table_dependency_features( + "shuttle-service", + dependencies, + vec!["web-warp".to_string()], + ); + + set_key_value_dependency_version( + "warp", + dependencies, + manifest_path, + url, + false, + get_dependency_version_fn, + ); + } + + fn get_boilerplate_code_for_framework(&self) -> &'static str { + indoc! {r#" + use warp::Filter; + use warp::Reply; + + #[shuttle_service::main] + async fn warp() -> shuttle_service::ShuttleWarp<(impl Reply,)> { + let route = warp::any().map(|| "Hello, World"); + Ok(route.boxed()) + }"#} + } +} + pub struct ShuttleInitNoOp; impl ShuttleInit for ShuttleInitNoOp { fn set_cargo_dependencies( @@ -484,10 +523,18 @@ pub fn get_framework(init_args: &InitArgs) -> Box { return Box::new(ShuttleInitPoem); } + if init_args.salvo { + return Box::new(ShuttleInitSalvo); + } + if init_args.serenity { return Box::new(ShuttleInitSerenity); } + if init_args.warp { + return Box::new(ShuttleInitWarp); + } + Box::new(ShuttleInitNoOp) } @@ -640,6 +687,7 @@ mod shuttle_init_tests { poem: false, salvo: false, serenity: false, + warp: false, path: PathBuf::new(), }; @@ -651,6 +699,7 @@ mod shuttle_init_tests { "poem" => init_args.poem = true, "salvo" => init_args.salvo = true, "serenity" => init_args.serenity = true, + "warp" => init_args.warp = true, _ => unreachable!(), } @@ -676,14 +725,18 @@ mod shuttle_init_tests { #[test] fn test_get_framework_via_get_boilerplate_code() { - let frameworks = vec!["axum", "rocket", "tide", "tower", "poem"]; + let frameworks = vec![ + "axum", "rocket", "tide", "tower", "poem", "salvo", "serenity", "warp", + ]; let framework_inits: Vec> = vec![ Box::new(ShuttleInitAxum), Box::new(ShuttleInitRocket), Box::new(ShuttleInitTide), Box::new(ShuttleInitTower), Box::new(ShuttleInitPoem), + Box::new(ShuttleInitSalvo), Box::new(ShuttleInitSerenity), + Box::new(ShuttleInitWarp), ]; for (framework, expected_framework_init) in frameworks.into_iter().zip(framework_inits) { @@ -990,6 +1043,38 @@ mod shuttle_init_tests { assert_eq!(cargo_toml.to_string(), expected); } + #[test] + fn test_set_cargo_dependencies_warp() { + let mut cargo_toml = cargo_toml_factory(); + let dependencies = cargo_toml["dependencies"].as_table_mut().unwrap(); + let manifest_path = PathBuf::new(); + let url = Url::parse("https://shuttle.rs").unwrap(); + + set_inline_table_dependency_version( + "shuttle-service", + dependencies, + &manifest_path, + &url, + false, + mock_get_latest_dependency_version, + ); + + ShuttleInitWarp.set_cargo_dependencies( + dependencies, + &manifest_path, + &url, + mock_get_latest_dependency_version, + ); + + let expected = indoc! {r#" + [dependencies] + shuttle-service = { version = "1.0", features = ["web-warp"] } + warp = "1.0" + "#}; + + assert_eq!(cargo_toml.to_string(), expected); + } + #[test] /// Makes sure that Rocket uses allow_prerelease flag when fetching the latest version fn test_get_latest_dependency_version_rocket() { diff --git a/cargo-shuttle/tests/integration/init.rs b/cargo-shuttle/tests/integration/init.rs index 80c524a8b..f21b9c3d6 100644 --- a/cargo-shuttle/tests/integration/init.rs +++ b/cargo-shuttle/tests/integration/init.rs @@ -26,6 +26,7 @@ async fn cargo_shuttle_init(path: PathBuf) -> anyhow::Result { poem: false, salvo: false, serenity: false, + warp: false, path, }), }) @@ -51,6 +52,7 @@ async fn cargo_shuttle_init_framework(path: PathBuf) -> anyhow::Result shuttle_service::ShuttleWarp<(impl Reply,)> { + let route = warp::any().map(|| "Hello, World"); + Ok(route.boxed()) +} diff --git a/service/Cargo.toml b/service/Cargo.toml index 2e1b7a721..f0884d297 100644 --- a/service/Cargo.toml +++ b/service/Cargo.toml @@ -34,6 +34,7 @@ tokio = { version = "=1.20.1", features = ["rt", "rt-multi-thread"] } tower = { version = "0.4.13", features = ["make"], optional = true } tracing = "0.1.36" tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } +warp = { version = "0.3.2", optional = true } # Tide does not have tokio support. So make sure async-std is compatible with tokio # https://github.com/http-rs/tide/issues/791 @@ -68,3 +69,4 @@ web-poem = ["poem"] web-salvo = ["salvo"] bot-serenity = ["serenity"] +web-warp = ["warp"] diff --git a/service/src/lib.rs b/service/src/lib.rs index 9610532de..d2c9cfbfb 100644 --- a/service/src/lib.rs +++ b/service/src/lib.rs @@ -202,7 +202,6 @@ //! //! You can also [open an issue or a discussion on GitHub](https://github.com/shuttle-hq/shuttle). //! - use std::future::Future; use std::net::SocketAddr; use std::pin::Pin; @@ -504,6 +503,22 @@ where #[cfg(feature = "web-poem")] pub type ShuttlePoem = Result; +#[cfg(feature = "web-warp")] +#[async_trait] +impl Service for T +where + T: Send + Sync + Clone + 'static + warp::Filter, + T::Extract: warp::reply::Reply, +{ + async fn bind(mut self: Box, addr: SocketAddr) -> Result<(), error::Error> { + warp::serve(*self).run(addr).await; + Ok(()) + } +} + +#[cfg(feature = "web-warp")] +pub type ShuttleWarp = Result, Error>; + #[cfg(feature = "web-axum")] #[async_trait] impl Service for sync_wrapper::SyncWrapper {