From c339ec382daeef70314126f061f508a918d4ca30 Mon Sep 17 00:00:00 2001 From: Michael Krasnitski Date: Tue, 19 Nov 2024 11:49:07 -0500 Subject: [PATCH] Add `Token::from_env` function and swap Http constructor names --- examples/e01_basic_ping_bot/src/main.rs | 8 +--- .../src/main.rs | 8 +--- examples/e03_struct_utilities/src/main.rs | 8 +--- examples/e04_message_builder/src/main.rs | 8 +--- examples/e05_sample_bot_structure/src/main.rs | 6 +-- examples/e06_env_logging/src/main.rs | 8 +--- examples/e07_shard_manager/src/main.rs | 7 +--- .../e08_create_message_builder/src/main.rs | 8 +--- examples/e09_collectors/src/main.rs | 7 +--- examples/e10_gateway_intents/src/main.rs | 8 +--- examples/e11_global_data/src/main.rs | 7 +--- examples/e12_parallel_loops/src/main.rs | 7 +--- examples/e13_sqlite_database/src/main.rs | 6 +-- examples/e14_message_components/src/main.rs | 7 +--- examples/e15_webhook/src/main.rs | 2 +- examples/testing/src/main.rs | 6 +-- src/error.rs | 13 +++++++ src/gateway/client/mod.rs | 16 ++++---- src/gateway/sharding/mod.rs | 3 +- src/gateway/sharding/shard_manager.rs | 2 +- src/http/client.rs | 18 +++------ src/prelude.rs | 1 + src/secrets.rs | 37 ++++++++++++++----- 23 files changed, 90 insertions(+), 111 deletions(-) diff --git a/examples/e01_basic_ping_bot/src/main.rs b/examples/e01_basic_ping_bot/src/main.rs index cb5360ef71d..5954373c7d9 100644 --- a/examples/e01_basic_ping_bot/src/main.rs +++ b/examples/e01_basic_ping_bot/src/main.rs @@ -1,5 +1,3 @@ -use std::env; - use serenity::async_trait; use serenity::model::channel::Message; use serenity::model::gateway::Ready; @@ -37,10 +35,8 @@ impl EventHandler for Handler { #[tokio::main] async fn main() { // Configure the client with your Discord bot token in the environment. - let token = env::var("DISCORD_TOKEN") - .expect("Expected a token in the environment") - .parse() - .expect("Invalid token"); + let token = + Token::from_env("DISCORD_TOKEN").expect("Expected a valid token in the environment"); // Set gateway intents, which decides what events the bot will be notified about let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::DIRECT_MESSAGES diff --git a/examples/e02_transparent_guild_sharding/src/main.rs b/examples/e02_transparent_guild_sharding/src/main.rs index 319b0b645d5..96215d41817 100644 --- a/examples/e02_transparent_guild_sharding/src/main.rs +++ b/examples/e02_transparent_guild_sharding/src/main.rs @@ -1,5 +1,3 @@ -use std::env; - use serenity::async_trait; use serenity::model::channel::Message; use serenity::model::gateway::Ready; @@ -42,10 +40,8 @@ impl EventHandler for Handler { #[tokio::main] async fn main() { // Configure the client with your Discord bot token in the environment. - let token = env::var("DISCORD_TOKEN") - .expect("Expected a token in the environment") - .parse() - .expect("Invalid token"); + let token = + Token::from_env("DISCORD_TOKEN").expect("Expected a valid token in the environment"); let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::DIRECT_MESSAGES | GatewayIntents::MESSAGE_CONTENT; diff --git a/examples/e03_struct_utilities/src/main.rs b/examples/e03_struct_utilities/src/main.rs index 2d8741837f4..cdbcea2f90f 100644 --- a/examples/e03_struct_utilities/src/main.rs +++ b/examples/e03_struct_utilities/src/main.rs @@ -1,5 +1,3 @@ -use std::env; - use serenity::async_trait; use serenity::builder::CreateMessage; use serenity::model::channel::Message; @@ -35,10 +33,8 @@ impl EventHandler for Handler { #[tokio::main] async fn main() { // Configure the client with your Discord bot token in the environment. - let token = env::var("DISCORD_TOKEN") - .expect("Expected a token in the environment") - .parse() - .expect("Invalid token"); + let token = + Token::from_env("DISCORD_TOKEN").expect("Expected a valid token in the environment"); let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::DIRECT_MESSAGES | GatewayIntents::MESSAGE_CONTENT; diff --git a/examples/e04_message_builder/src/main.rs b/examples/e04_message_builder/src/main.rs index 80d0f8d059f..03ff0feb6b0 100644 --- a/examples/e04_message_builder/src/main.rs +++ b/examples/e04_message_builder/src/main.rs @@ -1,5 +1,3 @@ -use std::env; - use serenity::async_trait; use serenity::model::channel::Message; use serenity::model::gateway::Ready; @@ -46,10 +44,8 @@ impl EventHandler for Handler { #[tokio::main] async fn main() { // Configure the client with your Discord bot token in the environment. - let token = env::var("DISCORD_TOKEN") - .expect("Expected a token in the environment") - .parse() - .expect("Invalid token"); + let token = + Token::from_env("DISCORD_TOKEN").expect("Expected a valid token in the environment"); let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::DIRECT_MESSAGES | GatewayIntents::MESSAGE_CONTENT; diff --git a/examples/e05_sample_bot_structure/src/main.rs b/examples/e05_sample_bot_structure/src/main.rs index c1e5b841750..83c6ff8ebb7 100644 --- a/examples/e05_sample_bot_structure/src/main.rs +++ b/examples/e05_sample_bot_structure/src/main.rs @@ -72,10 +72,8 @@ impl EventHandler for Handler { #[tokio::main] async fn main() { // Configure the client with your Discord bot token in the environment. - let token = env::var("DISCORD_TOKEN") - .expect("Expected a token in the environment") - .parse() - .expect("Invalid token"); + let token = + Token::from_env("DISCORD_TOKEN").expect("Expected a valid token in the environment"); // Build our client. let mut client = Client::builder(token, GatewayIntents::empty()) diff --git a/examples/e06_env_logging/src/main.rs b/examples/e06_env_logging/src/main.rs index e8bf66514f8..2c70eae2f43 100644 --- a/examples/e06_env_logging/src/main.rs +++ b/examples/e06_env_logging/src/main.rs @@ -1,5 +1,3 @@ -use std::env; - use serenity::async_trait; use serenity::model::event::ResumedEvent; use serenity::model::gateway::Ready; @@ -42,10 +40,8 @@ async fn main() { tracing_subscriber::fmt::init(); // Configure the client with your Discord bot token in the environment. - let token = env::var("DISCORD_TOKEN") - .expect("Expected a token in the environment") - .parse() - .expect("Invalid token"); + let token = + Token::from_env("DISCORD_TOKEN").expect("Expected a valid token in the environment"); let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::DIRECT_MESSAGES diff --git a/examples/e07_shard_manager/src/main.rs b/examples/e07_shard_manager/src/main.rs index 29fd9445f95..9a3c8719706 100644 --- a/examples/e07_shard_manager/src/main.rs +++ b/examples/e07_shard_manager/src/main.rs @@ -19,7 +19,6 @@ //! //! Note that it may take a minute or more for a latency to be recorded or to update, depending on //! how often Discord tells the client to send a heartbeat. -use std::env; use std::time::Duration; use serenity::async_trait; @@ -44,10 +43,8 @@ impl EventHandler for Handler { #[tokio::main] async fn main() { // Configure the client with your Discord bot token in the environment. - let token = env::var("DISCORD_TOKEN") - .expect("Expected a token in the environment") - .parse() - .expect("Invalid token"); + let token = + Token::from_env("DISCORD_TOKEN").expect("Expected a valid token in the environment"); let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::DIRECT_MESSAGES diff --git a/examples/e08_create_message_builder/src/main.rs b/examples/e08_create_message_builder/src/main.rs index 65ee1d96de4..3432a42a5b9 100644 --- a/examples/e08_create_message_builder/src/main.rs +++ b/examples/e08_create_message_builder/src/main.rs @@ -1,5 +1,3 @@ -use std::env; - use serenity::async_trait; use serenity::builder::{CreateAttachment, CreateEmbed, CreateEmbedFooter, CreateMessage}; use serenity::model::channel::Message; @@ -51,10 +49,8 @@ impl EventHandler for Handler { #[tokio::main] async fn main() { // Configure the client with your Discord bot token in the environment. - let token = env::var("DISCORD_TOKEN") - .expect("Expected a token in the environment") - .parse() - .expect("Invalid token"); + let token = + Token::from_env("DISCORD_TOKEN").expect("Expected a valid token in the environment"); let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::DIRECT_MESSAGES | GatewayIntents::MESSAGE_CONTENT; diff --git a/examples/e09_collectors/src/main.rs b/examples/e09_collectors/src/main.rs index 9fe7e839bae..4803697bb51 100644 --- a/examples/e09_collectors/src/main.rs +++ b/examples/e09_collectors/src/main.rs @@ -1,7 +1,6 @@ //! This example will showcase the beauty of collectors. They allow to await messages or reactions //! from a user in the middle of a control flow, one being a command. use std::collections::HashSet; -use std::env; use std::time::Duration; use serenity::async_trait; @@ -136,10 +135,8 @@ impl EventHandler for Handler { #[tokio::main] async fn main() { // Configure the client with your Discord bot token in the environment. - let token = env::var("DISCORD_TOKEN") - .expect("Expected a token in the environment") - .parse() - .expect("Invalid token"); + let token = + Token::from_env("DISCORD_TOKEN").expect("Expected a valid token in the environment"); let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::DIRECT_MESSAGES diff --git a/examples/e10_gateway_intents/src/main.rs b/examples/e10_gateway_intents/src/main.rs index 8bdfd3112bb..7e3e0c8bcd2 100644 --- a/examples/e10_gateway_intents/src/main.rs +++ b/examples/e10_gateway_intents/src/main.rs @@ -1,5 +1,3 @@ -use std::env; - use serenity::async_trait; use serenity::model::channel::Message; use serenity::model::gateway::{Presence, Ready}; @@ -33,10 +31,8 @@ impl EventHandler for Handler { #[tokio::main] async fn main() { // Configure the client with your Discord bot token in the environment. - let token = env::var("DISCORD_TOKEN") - .expect("Expected a token in the environment") - .parse() - .expect("Invalid token"); + let token = + Token::from_env("DISCORD_TOKEN").expect("Expected a valid token in the environment"); // Intents are a bitflag, bitwise operations can be used to dictate which intents to use let intents = diff --git a/examples/e11_global_data/src/main.rs b/examples/e11_global_data/src/main.rs index 8a2d4473e6b..7de92c9e879 100644 --- a/examples/e11_global_data/src/main.rs +++ b/examples/e11_global_data/src/main.rs @@ -1,7 +1,6 @@ //! In this example, you will be shown how to share data between events. use std::borrow::Cow; -use std::env; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; @@ -61,10 +60,8 @@ impl EventHandler for Handler { #[tokio::main] async fn main() { - let token = env::var("DISCORD_TOKEN") - .expect("Expected a token in the environment") - .parse() - .expect("Invalid token"); + let token = + Token::from_env("DISCORD_TOKEN").expect("Expected a valid token in the environment"); // We setup the initial value for our user data, which we will use throughout the rest of our // program. diff --git a/examples/e12_parallel_loops/src/main.rs b/examples/e12_parallel_loops/src/main.rs index ea6c47eed11..73a17b67ecf 100644 --- a/examples/e12_parallel_loops/src/main.rs +++ b/examples/e12_parallel_loops/src/main.rs @@ -1,4 +1,3 @@ -use std::env; use std::sync::atomic::{AtomicBool, Ordering}; use std::time::Duration; @@ -100,10 +99,8 @@ fn set_activity_to_current_time(ctx: &Context) { #[tokio::main] async fn main() { - let token = env::var("DISCORD_TOKEN") - .expect("Expected a token in the environment") - .parse() - .expect("Invalid token"); + let token = + Token::from_env("DISCORD_TOKEN").expect("Expected a valid token in the environment"); let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::DIRECT_MESSAGES diff --git a/examples/e13_sqlite_database/src/main.rs b/examples/e13_sqlite_database/src/main.rs index efb3821eaba..d41cc479dc9 100644 --- a/examples/e13_sqlite_database/src/main.rs +++ b/examples/e13_sqlite_database/src/main.rs @@ -72,10 +72,8 @@ impl EventHandler for Bot { #[tokio::main] async fn main() { // Configure the client with your Discord bot token in the environment. - let token = std::env::var("DISCORD_TOKEN") - .expect("Expected a token in the environment") - .parse() - .expect("Invalid token"); + let token = + Token::from_env("DISCORD_TOKEN").expect("Expected a valid token in the environment"); // Initiate a connection to the database file, creating the file if required. let database = sqlx::sqlite::SqlitePoolOptions::new() diff --git a/examples/e14_message_components/src/main.rs b/examples/e14_message_components/src/main.rs index dc0396941dd..97413a52789 100644 --- a/examples/e14_message_components/src/main.rs +++ b/examples/e14_message_components/src/main.rs @@ -1,5 +1,4 @@ use std::borrow::Cow; -use std::env; use std::time::Duration; use dotenv::dotenv; @@ -140,10 +139,8 @@ impl EventHandler for Handler { async fn main() { dotenv().ok(); // Configure the client with your Discord bot token in the environment. - let token = env::var("DISCORD_TOKEN") - .expect("Expected a token in the environment") - .parse() - .expect("Invalid token"); + let token = + Token::from_env("DISCORD_TOKEN").expect("Expected a valid token in the environment"); // Build our client. let intents = GatewayIntents::GUILD_MESSAGES diff --git a/examples/e15_webhook/src/main.rs b/examples/e15_webhook/src/main.rs index eaaa811a60f..3ea35063e97 100644 --- a/examples/e15_webhook/src/main.rs +++ b/examples/e15_webhook/src/main.rs @@ -5,7 +5,7 @@ use serenity::model::webhook::Webhook; #[tokio::main] async fn main() { // You don't need a token when you are only dealing with webhooks. - let http = Http::new(); + let http = Http::without_token(); let webhook = Webhook::from_url(&http, "https://discord.com/api/webhooks/133742013374206969/hello-there-oPNtRN5UY5DVmBe7m1N0HE-replace-me-Dw9LRkgq3zI7LoW3Rb-k-q") .await .expect("Replace the webhook with your own"); diff --git a/examples/testing/src/main.rs b/examples/testing/src/main.rs index 04c7c68607e..cf0b88e32d5 100644 --- a/examples/testing/src/main.rs +++ b/examples/testing/src/main.rs @@ -415,10 +415,8 @@ async fn main() -> Result<(), serenity::Error> { } env_logger::init(); - let token = std::env::var("DISCORD_TOKEN") - .expect("Expected a token in the environment") - .parse() - .expect("Invalid token"); + let token = + Token::from_env("DISCORD_TOKEN").expect("Expected a valid token in the environment"); let intents = GatewayIntents::non_privileged() | GatewayIntents::MESSAGE_CONTENT; Client::builder(token, intents).event_handler(Handler).await?.start().await } diff --git a/src/error.rs b/src/error.rs index 5f944606a07..d090cb860e6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -13,6 +13,7 @@ use crate::gateway::GatewayError; use crate::http::HttpError; use crate::internal::prelude::*; use crate::model::ModelError; +use crate::secrets::TokenError; /// The common result type between most library functions. /// @@ -46,6 +47,10 @@ pub enum Error { /// An error from the `tungstenite` crate. #[cfg(feature = "gateway")] Tungstenite(Box), + /// An error from the [`secrets`] module. + /// + /// [`secrets`]: crate::secrets + Token(TokenError), } #[cfg(feature = "gateway")] @@ -87,6 +92,12 @@ impl From for Error { } } +impl From for Error { + fn from(e: TokenError) -> Error { + Error::Token(e) + } +} + #[cfg(feature = "http")] impl From for Error { fn from(e: InvalidHeaderValue) -> Error { @@ -113,6 +124,7 @@ impl fmt::Display for Error { Self::Http(inner) => fmt::Display::fmt(&inner, f), #[cfg(feature = "gateway")] Self::Tungstenite(inner) => fmt::Display::fmt(&inner, f), + Self::Token(inner) => fmt::Display::fmt(&inner, f), } } } @@ -130,6 +142,7 @@ impl StdError for Error { Self::Http(inner) => Some(inner), #[cfg(feature = "gateway")] Self::Tungstenite(inner) => Some(inner), + Self::Token(inner) => Some(inner), } } } diff --git a/src/gateway/client/mod.rs b/src/gateway/client/mod.rs index f1010fc09dd..138ad3f73af 100644 --- a/src/gateway/client/mod.rs +++ b/src/gateway/client/mod.rs @@ -95,7 +95,7 @@ impl ClientBuilder { /// framework via the [`Self::framework`] method, otherwise awaiting the builder will cause a /// panic. pub fn new(token: Token, intents: GatewayIntents) -> Self { - Self::new_with_http(token.clone(), Arc::new(Http::with_token(token)), intents) + Self::new_with_http(token.clone(), Arc::new(Http::new(token)), intents) } /// Construct a new builder with a [`Http`] instance to calls methods on for the client @@ -416,7 +416,7 @@ impl IntoFuture for ClientBuilder { /// } /// /// # async fn run() -> Result<(), Box> { -/// let token = std::env::var("DISCORD_TOKEN")?.parse()?; +/// let token = Token::from_env("DISCORD_TOKEN")?; /// let mut client = /// Client::builder(token, GatewayIntents::default()).event_handler(Handler).await?; /// @@ -544,7 +544,7 @@ impl Client { /// use serenity::Client; /// /// # async fn run() -> Result<(), Box> { - /// let token = std::env::var("DISCORD_TOKEN")?.parse()?; + /// let token = Token::from_env("DISCORD_TOKEN")?; /// let mut client = Client::builder(token, GatewayIntents::default()).await?; /// /// if let Err(why) = client.start().await { @@ -587,7 +587,7 @@ impl Client { /// use serenity::Client; /// /// # async fn run() -> Result<(), Box> { - /// let token = std::env::var("DISCORD_TOKEN")?.parse()?; + /// let token = Token::from_env("DISCORD_TOKEN")?; /// let mut client = Client::builder(token, GatewayIntents::default()).await?; /// /// if let Err(why) = client.start_autosharded().await { @@ -634,7 +634,7 @@ impl Client { /// use serenity::Client; /// /// # async fn run() -> Result<(), Box> { - /// let token = std::env::var("DISCORD_TOKEN")?.parse()?; + /// let token = Token::from_env("DISCORD_TOKEN")?; /// let mut client = Client::builder(token, GatewayIntents::default()).await?; /// /// if let Err(why) = client.start_shard(3, 5).await { @@ -653,7 +653,7 @@ impl Client { /// use serenity::Client; /// /// # async fn run() -> Result<(), Box> { - /// let token = std::env::var("DISCORD_TOKEN")?.parse()?; + /// let token = Token::from_env("DISCORD_TOKEN")?; /// let mut client = Client::builder(token, GatewayIntents::default()).await?; /// /// if let Err(why) = client.start_shard(0, 1).await { @@ -696,7 +696,7 @@ impl Client { /// use serenity::Client; /// /// # async fn run() -> Result<(), Box> { - /// let token = std::env::var("DISCORD_TOKEN")?.parse()?; + /// let token = Token::from_env("DISCORD_TOKEN")?; /// let mut client = Client::builder(token, GatewayIntents::default()).await?; /// /// if let Err(why) = client.start_shards(8).await { @@ -739,7 +739,7 @@ impl Client { /// use serenity::Client; /// /// # async fn run() -> Result<(), Box> { - /// let token = std::env::var("DISCORD_TOKEN")?.parse()?; + /// let token = Token::from_env("DISCORD_TOKEN")?; /// let mut client = Client::builder(token, GatewayIntents::default()).await?; /// /// if let Err(why) = client.start_shard_range(4..7, 10).await { diff --git a/src/gateway/sharding/mod.rs b/src/gateway/sharding/mod.rs index a9ff3ca4fc3..76b5547db16 100644 --- a/src/gateway/sharding/mod.rs +++ b/src/gateway/sharding/mod.rs @@ -136,13 +136,14 @@ impl Shard { /// use serenity::gateway::{Shard, TransportCompression}; /// use serenity::model::gateway::{GatewayIntents, ShardInfo}; /// use serenity::model::id::ShardId; + /// use serenity::secrets::Token; /// use tokio::sync::Mutex; /// # /// # use serenity::http::Http; /// # /// # async fn run() -> Result<(), Box> { /// # let http: Arc = unimplemented!(); - /// let token = std::env::var("DISCORD_BOT_TOKEN")?.parse()?; + /// let token = Token::from_env("DISCORD_TOKEN")?; /// let shard_info = ShardInfo { /// id: ShardId(0), /// total: NonZeroU16::MIN, diff --git a/src/gateway/sharding/shard_manager.rs b/src/gateway/sharding/shard_manager.rs index 2e277f5c41c..b0cc2e986fa 100644 --- a/src/gateway/sharding/shard_manager.rs +++ b/src/gateway/sharding/shard_manager.rs @@ -83,7 +83,7 @@ pub const DEFAULT_WAIT_BETWEEN_SHARD_START: Duration = Duration::from_secs(5); /// let ws_url = Arc::from(gateway_info.url); /// let event_handler = Arc::new(Handler); /// let max_concurrency = std::num::NonZeroU16::MIN; -/// let token = std::env::var("DISCORD_TOKEN")?.parse()?; +/// let token = Token::from_env("DISCORD_TOKEN")?; /// /// ShardManager::new(ShardManagerOptions { /// token, diff --git a/src/http/client.rs b/src/http/client.rs index 5be76f2305c..8591f818690 100644 --- a/src/http/client.rs +++ b/src/http/client.rs @@ -223,26 +223,20 @@ pub struct Http { pub default_allowed_mentions: Option>, } -impl Default for Http { - fn default() -> Self { - Self::new() +impl Http { + /// Construct an authorized HTTP client. + #[must_use] + pub fn new(token: Token) -> Self { + HttpBuilder::new().token(token).build() } -} -impl Http { /// Construct an unauthorized HTTP client, with no token. Few things will work, but webhooks /// are one exception. #[must_use] - pub fn new() -> Self { + pub fn without_token() -> Self { HttpBuilder::new().build() } - /// Construct an authorized HTTP client. - #[must_use] - pub fn with_token(token: Token) -> Self { - HttpBuilder::new().token(token).build() - } - pub fn application_id(&self) -> Option { let application_id = self.application_id.load(Ordering::Relaxed); if application_id == u64::MAX { diff --git a/src/prelude.rs b/src/prelude.rs index c8811c5748b..42aa5a70c89 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -27,3 +27,4 @@ pub use crate::http::HttpError; pub use crate::model::mention::Mentionable; #[cfg(feature = "model")] pub use crate::model::{gateway::GatewayIntents, ModelError}; +pub use crate::secrets::Token; diff --git a/src/secrets.rs b/src/secrets.rs index 223631756ac..8f8294d521b 100644 --- a/src/secrets.rs +++ b/src/secrets.rs @@ -1,3 +1,5 @@ +use std::env::{self, VarError}; +use std::ffi::OsStr; use std::fmt; use std::str::FromStr; use std::sync::Arc; @@ -50,6 +52,10 @@ impl typesize::TypeSize for SecretString { pub struct Token(SecretString); impl Token { + pub fn from_env>(key: K) -> Result { + env::var(key).map_err(TokenError::Env).and_then(|token| token.parse()) + } + #[must_use] pub fn expose_secret(&self) -> &str { self.0.expose_secret() @@ -82,10 +88,10 @@ impl Token { /// /// # Errors /// -/// Returns a [`InvalidToken`] when one of the above checks fail. The type of failure is not -/// specified. +/// Returns a [`TokenError::InvalidToken`] when one of the above checks fail. The type of failure is +/// not specified. impl FromStr for Token { - type Err = InvalidToken; + type Err = TokenError; fn from_str(s: &str) -> Result { let token = s.trim().trim_start_matches("Bot ").trim_start_matches("Bearer "); @@ -101,19 +107,32 @@ impl FromStr for Token { aformat!("Box {}", CapStr::<128>(token)).as_str(), )))) } else { - Err(InvalidToken) + Err(TokenError::InvalidToken) } } } -/// Error that can be returned by [`Token::from_str`]. +/// Error that can be returned by [`Token::from_str`] or [`Token::from_env`]. #[derive(Debug)] -pub struct InvalidToken; +pub enum TokenError { + Env(VarError), + InvalidToken, +} -impl std::error::Error for InvalidToken {} +impl std::error::Error for TokenError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::Env(inner) => Some(inner), + Self::InvalidToken => None, + } + } +} -impl fmt::Display for InvalidToken { +impl fmt::Display for TokenError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("The provided token was invalid") + match self { + Self::Env(inner) => fmt::Display::fmt(&inner, f), + Self::InvalidToken => f.write_str("The provided token was invalid"), + } } }