Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support Poise #560

Merged
merged 5 commits into from
Jan 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ workflows:
- workspace-fmt
matrix:
parameters:
framework: ["web-actix-web", "web-axum", "web-rocket", "web-poem", "web-thruster", "web-tide", "web-tower","web-warp", "web-salvo", "bot-serenity"]
framework: ["web-actix-web", "web-axum", "web-rocket", "web-poem", "web-thruster", "web-tide", "web-tower","web-warp", "web-salvo", "bot-serenity", "bot-poise"]
- check-standalone:
matrix:
parameters:
Expand Down
37 changes: 37 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 18 additions & 11 deletions cargo-shuttle/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,37 +148,40 @@ pub struct RunArgs {
#[derive(Parser, Debug)]
pub struct InitArgs {
/// Initialize with actix-web framework
#[clap(long="actix-web", conflicts_with_all = &["axum", "rocket", "tide", "tower", "poem", "serenity", "warp", "salvo", "thruster", "no-framework"])]
#[clap(long="actix-web", conflicts_with_all = &["axum", "rocket", "tide", "tower", "poem", "serenity", "poise", "warp", "salvo", "thruster", "no-framework"])]
pub actix_web: bool,
/// Initialize with axum framework
#[clap(long, conflicts_with_all = &["actix-web","rocket", "tide", "tower", "poem", "serenity", "warp", "salvo", "thruster", "no-framework"])]
#[clap(long, conflicts_with_all = &["actix-web","rocket", "tide", "tower", "poem", "serenity", "poise", "warp", "salvo", "thruster", "no-framework"])]
pub axum: bool,
/// Initialize with rocket framework
#[clap(long, conflicts_with_all = &["actix-web","axum", "tide", "tower", "poem", "serenity", "warp", "salvo", "thruster", "no-framework"])]
#[clap(long, conflicts_with_all = &["actix-web","axum", "tide", "tower", "poem", "serenity", "poise", "warp", "salvo", "thruster", "no-framework"])]
pub rocket: bool,
/// Initialize with tide framework
#[clap(long, conflicts_with_all = &["actix-web","axum", "rocket", "tower", "poem", "serenity", "warp", "salvo", "thruster", "no-framework"])]
#[clap(long, conflicts_with_all = &["actix-web","axum", "rocket", "tower", "poem", "serenity", "poise", "warp", "salvo", "thruster", "no-framework"])]
pub tide: bool,
/// Initialize with tower framework
#[clap(long, conflicts_with_all = &["actix-web","axum", "rocket", "tide", "poem", "serenity", "warp", "salvo", "thruster", "no-framework"])]
#[clap(long, conflicts_with_all = &["actix-web","axum", "rocket", "tide", "poem", "serenity", "poise", "warp", "salvo", "thruster", "no-framework"])]
pub tower: bool,
/// Initialize with poem framework
#[clap(long, conflicts_with_all = &["actix-web","axum", "rocket", "tide", "tower", "serenity", "warp", "salvo", "thruster", "no-framework"])]
#[clap(long, conflicts_with_all = &["actix-web","axum", "rocket", "tide", "tower", "serenity", "poise", "warp", "salvo", "thruster", "no-framework"])]
pub poem: bool,
/// Initialize with salvo framework
#[clap(long, conflicts_with_all = &["actix-web","axum", "rocket", "tide", "tower", "poem", "warp", "serenity", "thruster", "no-framework"])]
#[clap(long, conflicts_with_all = &["actix-web","axum", "rocket", "tide", "tower", "poem", "warp", "serenity", "poise", "thruster", "no-framework"])]
pub salvo: bool,
/// Initialize with serenity framework
#[clap(long, conflicts_with_all = &["actix-web","axum", "rocket", "tide", "tower", "poem", "warp", "salvo", "thruster", "no-framework"])]
#[clap(long, conflicts_with_all = &["actix-web","axum", "rocket", "tide", "tower", "poem", "warp", "poise", "salvo", "thruster", "no-framework"])]
pub serenity: bool,
/// Initialize with poise framework
#[clap(long, conflicts_with_all = &["actix-web","axum", "rocket", "tide", "tower", "poem", "warp", "serenity", "salvo", "thruster", "no-framework"])]
pub poise: bool,
/// Initialize with warp framework
#[clap(long, conflicts_with_all = &["actix-web","axum", "rocket", "tide", "tower", "poem", "serenity", "salvo", "thruster", "no-framework"])]
#[clap(long, conflicts_with_all = &["actix-web","axum", "rocket", "tide", "tower", "poem", "serenity", "poise", "salvo", "thruster", "no-framework"])]
pub warp: bool,
/// Initialize with thruster framework
#[clap(long, conflicts_with_all = &["actix-web","axum", "rocket", "tide", "tower", "poem", "warp", "salvo", "serenity", "no-framework"])]
#[clap(long, conflicts_with_all = &["actix-web","axum", "rocket", "tide", "tower", "poem", "warp", "salvo", "serenity", "poise", "no-framework"])]
pub thruster: bool,
/// Initialize without a framework
#[clap(long, conflicts_with_all = &["actix-web","axum", "rocket", "tide", "tower", "poem", "warp", "salvo", "serenity", "thruster"])]
#[clap(long, conflicts_with_all = &["actix-web","axum", "rocket", "tide", "tower", "poem", "warp", "salvo", "serenity", "poise", "thruster"])]
pub no_framework: bool,
/// Whether to create the environment for this project on Shuttle
#[clap(long)]
Expand Down Expand Up @@ -209,6 +212,8 @@ impl InitArgs {
Some(Framework::Poem)
} else if self.salvo {
Some(Framework::Salvo)
} else if self.poise {
Some(Framework::Poise)
} else if self.serenity {
Some(Framework::Serenity)
} else if self.warp {
Expand Down Expand Up @@ -257,6 +262,7 @@ mod tests {
poem: false,
salvo: false,
serenity: false,
poise: false,
warp: false,
thruster: false,
no_framework: false,
Expand All @@ -274,6 +280,7 @@ mod tests {
"poem" => init_args.poem = true,
"salvo" => init_args.salvo = true,
"serenity" => init_args.serenity = true,
"poise" => init_args.poise = true,
"warp" => init_args.warp = true,
"thruster" => init_args.thruster = true,
"none" => init_args.no_framework = true,
Expand Down
137 changes: 137 additions & 0 deletions cargo-shuttle/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub enum Framework {
Poem,
Salvo,
Serenity,
Poise,
Warp,
Thruster,
None,
Expand All @@ -39,6 +40,7 @@ impl Framework {
Framework::Poem => Box::new(ShuttleInitPoem),
Framework::Salvo => Box::new(ShuttleInitSalvo),
Framework::Serenity => Box::new(ShuttleInitSerenity),
Framework::Poise => Box::new(ShuttleInitPoise),
Framework::Warp => Box::new(ShuttleInitWarp),
Framework::Thruster => Box::new(ShuttleInitThruster),
Framework::None => Box::new(ShuttleInitNoOp),
Expand Down Expand Up @@ -446,6 +448,106 @@ impl ShuttleInit for ShuttleInitSerenity {
}
}

pub struct ShuttleInitPoise;

impl ShuttleInit for ShuttleInitPoise {
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!["bot-poise".to_string()],
);

set_key_value_dependency_version(
"anyhow",
dependencies,
manifest_path,
url,
false,
get_dependency_version_fn,
);

set_key_value_dependency_version(
"poise",
dependencies,
manifest_path,
url,
false,
get_dependency_version_fn,
);

set_key_value_dependency_version(
"shuttle-secrets",
dependencies,
manifest_path,
url,
false,
get_dependency_version_fn,
);

set_key_value_dependency_version(
"tracing",
dependencies,
manifest_path,
url,
false,
get_dependency_version_fn,
);
}

fn get_boilerplate_code_for_framework(&self) -> &'static str {
indoc! {r#"
use anyhow::Context as _;
use poise::serenity_prelude as serenity;
use shuttle_secrets::SecretStore;
use shuttle_service::ShuttlePoise;

struct Data {} // User data, which is stored and accessible in all command invocations
type Error = Box<dyn std::error::Error + Send + Sync>;
type Context<'a> = poise::Context<'a, Data, Error>;

/// Responds with "world!"
#[poise::command(slash_command)]
async fn hello(ctx: Context<'_>) -> Result<(), Error> {
ctx.say("world!").await?;
Ok(())
}

#[shuttle_service::main]
async fn poise(#[shuttle_secrets::Secrets] secret_store: SecretStore) -> ShuttlePoise<Data, Error> {
// Get the discord token set in `Secrets.toml`
let discord_token = secret_store
.get("DISCORD_TOKEN")
.context("'DISCORD_TOKEN' was not found")?;

let framework = poise::Framework::builder()
.options(poise::FrameworkOptions {
commands: vec![hello()],
..Default::default()
})
.token(discord_token)
.intents(serenity::GatewayIntents::non_privileged())
.setup(|ctx, _ready, framework| {
Box::pin(async move {
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
Ok(Data {})
})
})
.build()
.await
.map_err(shuttle_service::error::CustomError::new)?;

Ok(framework)
}"#}
}
}

pub struct ShuttleInitTower;

impl ShuttleInit for ShuttleInitTower {
Expand Down Expand Up @@ -1125,6 +1227,41 @@ mod shuttle_init_tests {
assert_eq!(cargo_toml.to_string(), expected);
}

#[test]
fn test_set_cargo_dependencies_poise() {
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,
);

ShuttleInitPoise.set_cargo_dependencies(
dependencies,
&manifest_path,
&url,
mock_get_latest_dependency_version,
);

let expected = indoc! {r#"
[dependencies]
shuttle-service = { version = "1.0", features = ["bot-poise"] }
anyhow = "1.0"
poise = "1.0"
shuttle-secrets = "1.0"
tracing = "1.0"
"#};

assert_eq!(cargo_toml.to_string(), expected);
}

#[test]
fn test_set_cargo_dependencies_warp() {
let mut cargo_toml = cargo_toml_factory();
Expand Down
2 changes: 2 additions & 0 deletions service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ rocket = { version = "0.5.0-rc.2", optional = true }
salvo = { version = "0.37.5", optional = true }
serde_json = { workspace = true }
serenity = { version = "0.11.5", default-features = false, features = ["client", "gateway", "rustls_backend", "model"], optional = true }
poise = { version = "0.5.2", optional = true }
sync_wrapper = { version = "0.1.1", optional = true }
thiserror = { workspace = true }
thruster = { version = "1.3.0", optional = true }
Expand Down Expand Up @@ -76,4 +77,5 @@ web-poem = ["poem"]
web-salvo = ["salvo"]

bot-serenity = ["serenity"]
bot-poise = ["poise"]
web-warp = ["warp"]
18 changes: 18 additions & 0 deletions service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ extern crate shuttle_codegen;
/// | `ShuttlePoem` | web-poem | [poem](https://docs.rs/poem/1.3.35) | 1.3.35 | [GitHub](https://github.com/shuttle-hq/examples/tree/main/poem/hello-world) |
/// | `Result<T, shuttle_service::Error>` | web-tower | [tower](https://docs.rs/tower/0.4.12) | 0.14.12 | [GitHub](https://github.com/shuttle-hq/examples/tree/main/tower/hello-world) |
/// | `ShuttleSerenity` | bot-serenity | [serenity](https://docs.rs/serenity/0.11.5) | 0.11.5 | [GitHub](https://github.com/shuttle-hq/examples/tree/main/serenity/hello-world) |
/// | `ShuttlePoise` | bot-poise | [poise](https://docs.rs/poise/0.5.2) | 0.5.2 | [GitHub](https://github.com/shuttle-hq/examples/tree/main/poise/hello-world) |
/// | `ShuttleActixWeb` | web-actix-web| [actix-web](https://docs.rs/actix-web/4.2.1)| 4.2.1 | [GitHub](https://github.com/shuttle-hq/examples/tree/main/actix-web/hello-world) |

///
Expand Down Expand Up @@ -661,5 +662,22 @@ impl Service for serenity::Client {
#[cfg(feature = "bot-serenity")]
pub type ShuttleSerenity = Result<serenity::Client, Error>;

#[cfg(feature = "bot-poise")]
#[async_trait]
impl<T, E> Service for std::sync::Arc<poise::Framework<T, E>>
chesedo marked this conversation as resolved.
Show resolved Hide resolved
where
T: std::marker::Send + std::marker::Sync + 'static,
E: std::marker::Send + std::marker::Sync + 'static,
{
async fn bind(mut self: Box<Self>, _addr: SocketAddr) -> Result<(), error::Error> {
self.start().await.map_err(error::CustomError::new)?;

Ok(())
}
}

#[cfg(feature = "bot-poise")]
pub type ShuttlePoise<T, E> = Result<std::sync::Arc<poise::Framework<T, E>>, Error>;

pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub const NAME: &str = env!("CARGO_PKG_NAME");