diff --git a/CHANGELOG.md b/CHANGELOG.md index fedd3a9f0b..684702612b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ ### Breaking changes +- Requests for helix endpoints have been converted to take `Cow`s. + This change means the `builder()` methods are harder to use, consider using the new methods on + each request which provide the same functionality but with better ergonomics. + See the top-level documentation for a endpoint for more examples. - Crate name changed: `twitch_api2` -> `twitch_api`, also changed to new org `twitch-rs` - All (most) types are now living in their own crate `twitch_types` - Features for clients are now named after the client, e.g feature `reqwest_client` is now simply `reqwest` diff --git a/Cargo.lock b/Cargo.lock index 4935ae0906..fe28d6e464 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2671,7 +2671,7 @@ dependencies = [ [[package]] name = "twitch_types" -version = "0.3.5" +version = "0.3.6" dependencies = [ "aliri_braid", "displaydoc", diff --git a/Cargo.toml b/Cargo.toml index c715d42a69..5dd58b76c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,7 @@ crypto_hmac = { package = "hmac", version = "0.12.1", optional = true } aliri_braid = "0.2.4" futures = { version = "0.3.25", optional = true } hyper = { version = "0.14.20", optional = true } -twitch_types = { version = "0.3.5", path = "./twitch_types" } +twitch_types = { version = "0.3.6", path = "./twitch_types" } [features] default = [] diff --git a/examples/automod_check.rs b/examples/automod_check.rs index 3735ac5e9e..26ecb30ff0 100644 --- a/examples/automod_check.rs +++ b/examples/automod_check.rs @@ -35,11 +35,11 @@ async fn run() -> Result<(), Box> let req = twitch_api::helix::moderation::CheckAutoModStatusRequest::broadcaster_id(broadcaster_id); - let data = - twitch_api::helix::moderation::CheckAutoModStatusBody::new("123", args.collect::()); + let text = args.collect::(); + let data = twitch_api::helix::moderation::CheckAutoModStatusBody::new("123", &text); println!("data: {:?}", data); - let response = client.req_post(req, vec![data], &token).await?; - println!("{:?}", response.data); + let response = client.req_post(req, &[&data], &token).await?.data; + println!("{response:?}"); Ok(()) } diff --git a/examples/channel_information.rs b/examples/channel_information.rs index 93c3a922ab..c1637dd502 100644 --- a/examples/channel_information.rs +++ b/examples/channel_information.rs @@ -26,12 +26,12 @@ async fn run() -> Result<(), Box> let token = UserToken::from_existing(&client, token, None, None).await?; let user = client - .get_user_from_login(args.next().unwrap(), &token) + .get_user_from_login(&*args.next().unwrap(), &token) .await? .expect("no user found"); let channel = client - .get_channel_from_id(user.id.clone(), &token) + .get_channel_from_id(&user.id, &token) .await? .expect("no channel found"); diff --git a/examples/channel_information_custom.rs b/examples/channel_information_custom.rs index fda8f5f897..3b6a9c6dab 100644 --- a/examples/channel_information_custom.rs +++ b/examples/channel_information_custom.rs @@ -24,11 +24,10 @@ async fn run() -> Result<(), Box> .map(AccessToken::new) .expect("Please set env: TWITCH_TOKEN or pass token as first argument"); let token = UserToken::from_existing(&client, token, None, None).await?; - let id = token.user_id.clone(); let resp = client .req_get_custom( - helix::channels::GetChannelInformationRequest::broadcaster_id(id), + helix::channels::GetChannelInformationRequest::broadcaster_id(&token.user_id), &token, ) .await diff --git a/examples/client.rs b/examples/client.rs index 3d653b9b93..c72924964e 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -1,4 +1,4 @@ -use twitch_api::{helix::streams::GetStreamsRequest, TwitchClient}; +use twitch_api::{helix::streams::GetStreamsRequest, types, TwitchClient}; use twitch_oauth2::{AccessToken, UserToken}; fn main() { use std::error::Error; @@ -36,9 +36,20 @@ async fn run() -> Result<(), Box> ) .await?; - let req = GetStreamsRequest::user_login(args.next().expect("please provide an username")); foo_client.client.helix.clone_client(); - let response = foo_client.client.helix.req_get(req, &token).await?; - println!("{:?}", response); + let response = foo_client + .client + .helix + .req_get( + GetStreamsRequest::user_logins( + &[types::UserNameRef::from_str( + &args.next().expect("please provide an username"), + )][..], + ), + &token, + ) + .await? + .data; + println!("{response:?}"); Ok(()) } diff --git a/examples/eventsub/src/main.rs b/examples/eventsub/src/main.rs index 913e61fbce..4b8a3ff866 100644 --- a/examples/eventsub/src/main.rs +++ b/examples/eventsub/src/main.rs @@ -74,7 +74,7 @@ pub async fn run(opts: &Opts) -> eyre::Result<()> { .await?; let broadcaster = client - .get_user_from_login(&*opts.broadcaster_login, &token) + .get_user_from_login(&opts.broadcaster_login, &token) .await? .ok_or_else(|| eyre::eyre!("broadcaster not found"))?; diff --git a/examples/eventsub/src/twitch.rs b/examples/eventsub/src/twitch.rs index 10bb12d11d..6e5548e0aa 100644 --- a/examples/eventsub/src/twitch.rs +++ b/examples/eventsub/src/twitch.rs @@ -264,7 +264,9 @@ pub async fn is_live<'a>( tracing::info!("checking if live"); if let Some(stream) = client .req_get( - helix::streams::get_streams::GetStreamsRequest::user_id(config.broadcaster.id.clone()), + helix::streams::get_streams::GetStreamsRequest::user_ids( + &[config.broadcaster.id.as_ref()][..], + ), token, ) .await diff --git a/examples/followed_streams.rs b/examples/followed_streams.rs index 5f29d1beed..00e81413e5 100644 --- a/examples/followed_streams.rs +++ b/examples/followed_streams.rs @@ -37,7 +37,13 @@ async fn run() -> Result<(), Box> .try_collect::>() .await?; let games = client - .get_games_by_id(streams.iter().map(|s| s.game_id.clone()), &token) + .get_games_by_id( + &streams + .iter() + .map(|s| s.game_id.as_ref()) + .collect::>(), + &token, + ) .await?; println!( diff --git a/examples/get_channel_status.rs b/examples/get_channel_status.rs index 7e3d520eff..2459c1f923 100644 --- a/examples/get_channel_status.rs +++ b/examples/get_channel_status.rs @@ -1,4 +1,4 @@ -use twitch_api::{helix::streams::GetStreamsRequest, HelixClient}; +use twitch_api::{helix::streams::GetStreamsRequest, types, HelixClient}; use twitch_oauth2::{AccessToken, UserToken}; fn main() { @@ -31,10 +31,17 @@ async fn run() -> Result<(), Box> .await .unwrap(); - let req = GetStreamsRequest::user_login(args.next().unwrap()); + let response = client + .req_get( + GetStreamsRequest::user_logins( + &[types::UserNameRef::from_str(&args.next().unwrap())][..], + ), + &token, + ) + .await + .unwrap() + .data; - let response = client.req_get(req, &token).await.unwrap(); - - println!("Stream information:\n\t{:?}", response.data); + println!("Stream information:\n\t{response:?}"); Ok(()) } diff --git a/examples/mock_api.rs b/examples/mock_api.rs index 00f3bae71a..9e5500501f 100644 --- a/examples/mock_api.rs +++ b/examples/mock_api.rs @@ -59,16 +59,16 @@ async fn run() -> Result<(), Box> .await?; let user = client - .get_user_from_id(&*user_id, &token) + .get_user_from_id(&user_id, &token) .await? .expect("no user found"); let _channel = client - .get_channel_from_id(&*user_id, &token) + .get_channel_from_id(&user_id, &token) .await? .expect("no channel found"); let _channel = client - .get_channel_from_id(user.id.clone(), &token) + .get_channel_from_id(&user.id, &token) .await? .expect("no channel found"); @@ -86,7 +86,7 @@ async fn run() -> Result<(), Box> .await?; dbg!(search.get(0)); let _total = client - .get_total_followers_from_id(search.get(0).unwrap().id.clone(), &token) + .get_total_followers_from_id(&search.get(0).unwrap().id, &token) .await?; dbg!(_total); let streams: Vec<_> = client.get_followed_streams(&token).try_collect().await?; diff --git a/examples/modify_channel.rs b/examples/modify_channel.rs index 234a37c975..09bb4a0f79 100644 --- a/examples/modify_channel.rs +++ b/examples/modify_channel.rs @@ -33,14 +33,14 @@ async fn run() -> Result<(), Box> let broadcaster_id = token.validate_token(&client).await?.user_id.unwrap(); let req = twitch_api::helix::channels::ModifyChannelInformationRequest::broadcaster_id( - broadcaster_id, + &broadcaster_id, ); - let mut data = twitch_api::helix::channels::ModifyChannelInformationBody::new(); - data.title("Hello World!"); + let mut body = twitch_api::helix::channels::ModifyChannelInformationBody::new(); + body.title("Hello World!"); println!("scopes: {:?}", token.scopes()); - let response = client.req_patch(req, data, &token).await?; + let response = client.req_patch(req, body, &token).await?; println!("{:?}", response); Ok(()) diff --git a/src/helix/client/client_ext.rs b/src/helix/client/client_ext.rs index eaceb051ab..1f64c2616e 100644 --- a/src/helix/client/client_ext.rs +++ b/src/helix/client/client_ext.rs @@ -1,5 +1,7 @@ //! Convenience functions for [HelixClient] +use std::borrow::Cow; + use crate::helix::{self, ClientRequestError, HelixClient}; use crate::types; use twitch_oauth2::TwitchToken; @@ -12,27 +14,30 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { /// Get [User](helix::users::User) from user login pub async fn get_user_from_login( &'a self, - login: impl Into, + login: impl Into<&types::UserNameRef>, token: &T, ) -> Result, ClientError<'a, C>> where T: TwitchToken + ?Sized, { - self.req_get(helix::users::GetUsersRequest::login(login), token) - .await - .map(|response| response.first()) + self.req_get( + helix::users::GetUsersRequest::logins(&[login.into()][..]), + token, + ) + .await + .map(|response| response.first()) } /// Get [User](helix::users::User) from user id pub async fn get_user_from_id( &'a self, - id: impl Into, + id: impl Into<&types::UserIdRef>, token: &T, ) -> Result, ClientError<'a, C>> where T: TwitchToken + ?Sized, { - self.req_get(helix::users::GetUsersRequest::id(id), token) + self.req_get(helix::users::GetUsersRequest::ids(&[id.into()][..]), token) .await .map(|response| response.first()) } @@ -40,23 +45,23 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { /// Get [ChannelInformation](helix::channels::ChannelInformation) from a broadcasters login pub async fn get_channel_from_login( &'a self, - login: impl Into, + login: impl Into<&types::UserNameRef>, token: &T, ) -> Result, ClientError<'a, C>> where T: TwitchToken + ?Sized, { - if let Some(user) = self.get_user_from_login(login, token).await? { - self.get_channel_from_id(user.id, token).await + if let Some(user) = self.get_user_from_login(login.into(), token).await? { + self.get_channel_from_id(&user.id, token).await } else { Ok(None) } } /// Get [ChannelInformation](helix::channels::ChannelInformation) from a broadcasters id - pub async fn get_channel_from_id( + pub async fn get_channel_from_id<'b, T>( &'a self, - id: impl Into, + id: impl types::IntoCow<'b, types::UserIdRef> + 'b, token: &T, ) -> Result, ClientError<'a, C>> where @@ -91,8 +96,8 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { /// ``` pub fn get_chatters( &'a self, - broadcaster_id: impl Into, - moderator_id: impl Into, + broadcaster_id: impl Into<&'a types::UserIdRef>, + moderator_id: impl Into<&'a types::UserIdRef>, batch_size: impl Into>, token: &'a T, ) -> std::pin::Pin< @@ -103,7 +108,7 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { { let req = helix::chat::GetChattersRequest { first: batch_size.into(), - ..helix::chat::GetChattersRequest::new(broadcaster_id, moderator_id) + ..helix::chat::GetChattersRequest::new(broadcaster_id.into(), moderator_id.into()) }; make_stream(req, token, self, std::collections::VecDeque::from) @@ -128,7 +133,7 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { /// ``` pub fn search_categories( &'a self, - query: impl Into, + query: impl Into<&'a str>, token: &'a T, ) -> std::pin::Pin< Box>> + 'a>, @@ -136,7 +141,7 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { where T: TwitchToken + Send + Sync + ?Sized, { - let req = helix::search::SearchCategoriesRequest::query(query).first(100); + let req = helix::search::SearchCategoriesRequest::query(query.into()).first(100); make_stream(req, token, self, std::collections::VecDeque::from) } @@ -159,7 +164,7 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { /// ``` pub fn search_channels( &'a self, - query: impl Into, + query: impl Into<&'a str>, live_only: bool, token: &'a T, ) -> std::pin::Pin< @@ -188,14 +193,14 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { /// use futures::TryStreamExt; /// /// // Get the followers of channel "1234" - /// let followers: Vec = client.get_follow_relationships(types::UserId::from("1234"), None, &token).try_collect().await?; + /// let followers: Vec = client.get_follow_relationships(Some("1234".into()), None, &token).try_collect().await?; /// /// # Ok(()) } /// ``` pub fn get_follow_relationships( &'a self, - to_id: impl Into>, - from_id: impl Into>, + to_id: impl Into>, + from_id: impl Into>, token: &'a T, ) -> std::pin::Pin< Box< @@ -208,8 +213,8 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { T: TwitchToken + Send + Sync + ?Sized, { let mut req = helix::users::GetUsersFollowsRequest::empty(); - req.to_id = to_id.into(); - req.from_id = from_id.into(); + req.to_id = to_id.into().map(Cow::Borrowed); + req.from_id = from_id.into().map(Cow::Borrowed); make_stream(req, token, self, |s| { std::collections::VecDeque::from(s.follow_relationships) @@ -320,9 +325,9 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { /// /// # Ok(()) } /// ``` - pub fn get_moderators_in_channel_from_id( + pub fn get_moderators_in_channel_from_id<'b: 'a, T>( &'a self, - broadcaster_id: impl Into, + broadcaster_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, token: &'a T, ) -> std::pin::Pin< Box< @@ -355,9 +360,9 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { /// /// # Ok(()) } /// ``` - pub fn get_banned_users_in_channel_from_id( + pub fn get_banned_users_in_channel_from_id<'b: 'a, T>( &'a self, - broadcaster_id: impl Into, + broadcaster_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, token: &'a T, ) -> std::pin::Pin< Box< @@ -374,16 +379,16 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { } /// Get a users, with login, follow count - pub async fn get_total_followers_from_login( + pub async fn get_total_followers_from_login<'b, T>( &'a self, - login: impl Into, + login: impl types::IntoCow<'b, types::UserNameRef> + 'b, token: &T, ) -> Result, ClientError<'a, C>> where T: TwitchToken + ?Sized, { - if let Some(user) = self.get_user_from_login(login, token).await? { - self.get_total_followers_from_id(user.id, token) + if let Some(user) = self.get_user_from_login(&*login.to_cow(), token).await? { + self.get_total_followers_from_id(&user.id, token) .await .map(Some) } else { @@ -396,9 +401,9 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { /// # Notes /// /// This returns zero if the user doesn't exist - pub async fn get_total_followers_from_id( + pub async fn get_total_followers_from_id<'b, T>( &'a self, - to_id: impl Into, + to_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, token: &T, ) -> Result> where @@ -417,13 +422,13 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { /// Get games by ID. Can only be at max 100 ids. pub async fn get_games_by_id( &'a self, - ids: impl IntoIterator>, + ids: impl AsRef<[&'a types::CategoryIdRef]>, token: &T, ) -> Result, ClientError<'a, C>> where T: TwitchToken + ?Sized, { - let ids: Vec<_> = ids.into_iter().take(101).map(Into::into).collect(); + let ids = ids.as_ref(); if ids.len() > 100 { return Err(ClientRequestError::Custom("too many IDs, max 100".into())); } @@ -440,9 +445,9 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { } /// Block a user - pub async fn block_user( + pub async fn block_user<'b, T>( &'a self, - target_user_id: impl Into, + target_user_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, token: &T, ) -> Result> where @@ -459,9 +464,9 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { } /// Unblock a user - pub async fn unblock_user( + pub async fn unblock_user<'b, T>( &'a self, - target_user_id: impl Into, + target_user_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, token: &T, ) -> Result> where @@ -477,13 +482,13 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { } /// Ban a user - pub async fn ban_user( + pub async fn ban_user<'b, T>( &'a self, - target_user_id: impl Into, - reason: impl std::fmt::Display, + target_user_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, + reason: impl Into<&'b str>, duration: impl Into>, - broadcaster_id: impl Into, - moderator_id: impl Into, + broadcaster_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, + moderator_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, token: &T, ) -> Result> where @@ -492,7 +497,7 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { Ok(self .req_post( helix::moderation::BanUserRequest::new(broadcaster_id, moderator_id), - helix::moderation::BanUserBody::new(target_user_id, reason.to_string(), duration), + helix::moderation::BanUserBody::new(target_user_id, reason.into(), duration), token, ) .await? @@ -500,11 +505,11 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { } /// Unban a user - pub async fn unban_user( + pub async fn unban_user<'b, T>( &'a self, - target_user_id: impl Into, - broadcaster_id: impl Into, - moderator_id: impl Into, + target_user_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, + broadcaster_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, + moderator_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, token: &T, ) -> Result> where @@ -552,9 +557,9 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { /// /// # Ok(()) } /// ``` - pub fn get_channel_schedule( + pub fn get_channel_schedule<'b: 'a, T>( &'a self, - broadcaster_id: impl Into, + broadcaster_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, token: &'a T, ) -> std::pin::Pin< Box>> + 'a>, @@ -580,9 +585,9 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { } /// Get channel emotes in channel with user id - pub async fn get_channel_emotes_from_id( + pub async fn get_channel_emotes_from_id<'b, T>( &'a self, - user_id: impl Into, + user_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, token: &T, ) -> Result, ClientError<'a, C>> where @@ -595,14 +600,17 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { /// Get channel emotes in channel with user login pub async fn get_channel_emotes_from_login( &'a self, - login: impl Into, + login: impl types::IntoCow<'a, types::UserNameRef> + 'a, token: &T, ) -> Result>, ClientError<'a, C>> where T: TwitchToken + ?Sized, { - if let Some(user) = self.get_user_from_login(login, token).await? { - self.get_channel_emotes_from_id(user.id, token) + if let Some(user) = self + .get_user_from_login(login.to_cow().as_ref(), token) + .await? + { + self.get_channel_emotes_from_id(&user.id, token) .await .map(Some) } else { @@ -613,21 +621,21 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { /// Get emotes in emote set pub async fn get_emote_sets( &'a self, - emote_sets: impl IntoIterator>, + emote_sets: impl AsRef<[&'a types::EmoteSetIdRef]>, token: &T, ) -> Result, ClientError<'a, C>> where T: TwitchToken + ?Sized, { - let req = helix::chat::GetEmoteSetsRequest::emote_set_ids(emote_sets); + let req = helix::chat::GetEmoteSetsRequest::emote_set_ids(emote_sets.as_ref()); Ok(self.req_get(req, token).await?.data) } /// Get a broadcaster's chat settings - pub async fn get_chat_settings( + pub async fn get_chat_settings<'b, T>( &'a self, - broadcaster_id: impl Into, - moderator_id: impl Into>, + broadcaster_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, + moderator_id: impl Into> + 'b, token: &T, ) -> Result> where @@ -641,11 +649,11 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { } /// Send a chat announcement - pub async fn send_chat_announcement( + pub async fn send_chat_announcement<'b, T, E>( &'a self, - broadcaster_id: impl Into, - moderator_id: impl Into, - message: impl std::fmt::Display, + broadcaster_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, + moderator_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, + message: impl Into<&'b str>, color: impl std::convert::TryInto, token: &T, ) -> Result> @@ -653,7 +661,7 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { T: TwitchToken + ?Sized, { let req = helix::chat::SendChatAnnouncementRequest::new(broadcaster_id, moderator_id); - let body = helix::chat::SendChatAnnouncementBody::new(message.to_string(), color)?; + let body = helix::chat::SendChatAnnouncementBody::new(message.into(), color)?; Ok(self .req_post(req, body, token) .await @@ -662,11 +670,11 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { } /// Delete a specific chat message - pub async fn delete_chat_message( + pub async fn delete_chat_message<'b, T>( &'a self, - broadcaster_id: impl Into, - moderator_id: impl Into, - message_id: impl Into, + broadcaster_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, + moderator_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, + message_id: impl types::IntoCow<'b, types::MsgIdRef> + 'b, token: &T, ) -> Result> where @@ -674,15 +682,14 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { { let req = helix::moderation::DeleteChatMessagesRequest::new(broadcaster_id, moderator_id) .message_id(message_id); - Ok(self.req_delete(req, token).await?.data) } /// Delete all chat messages in a broadcasters chat room - pub async fn delete_all_chat_message( + pub async fn delete_all_chat_message<'b, T>( &'a self, - broadcaster_id: impl Into, - moderator_id: impl Into, + broadcaster_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, + moderator_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, token: &T, ) -> Result> where @@ -693,10 +700,10 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { } /// Start a raid - pub async fn start_a_raid( + pub async fn start_a_raid<'b, T>( &'a self, - from_broadcaster_id: impl Into, - to_broadcaster_id: impl Into, + from_broadcaster_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, + to_broadcaster_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, token: &T, ) -> Result> where @@ -707,9 +714,9 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { } /// Cancel a raid - pub async fn cancel_a_raid( + pub async fn cancel_a_raid<'b, T>( &'a self, - broadcaster_id: impl Into, + broadcaster_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, token: &T, ) -> Result> where @@ -722,31 +729,33 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { /// Get a users chat color pub async fn get_user_chat_color( &'a self, - user_id: impl Into, + user_id: impl Into<&types::UserIdRef>, token: &T, ) -> Result, ClientError<'a, C>> where T: TwitchToken + ?Sized, { - let req = helix::chat::GetUserChatColorRequest { - user_id: vec![user_id.into()], - }; - - Ok(self.req_get(req, token).await?.first()) + Ok(self + .req_get( + helix::chat::GetUserChatColorRequest::user_ids(&[user_id.into()][..]), + token, + ) + .await? + .first()) } /// Get a users chat color - pub async fn update_user_chat_color( + pub async fn update_user_chat_color<'b, T>( &'a self, - user_id: impl Into, - color: impl Into>, + user_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, + color: impl Into> + 'b, token: &T, ) -> Result> where T: TwitchToken + ?Sized, { let req = helix::chat::UpdateUserChatColorRequest { - user_id: user_id.into(), + user_id: user_id.to_cow(), color: color.into(), }; @@ -756,57 +765,57 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { /// Get multiple users chat colors pub async fn get_users_chat_colors( &'a self, - user_ids: impl IntoIterator>, + user_ids: impl AsRef<[&'a types::UserIdRef]>, token: &T, ) -> Result, ClientError<'a, C>> where T: TwitchToken + ?Sized, { - let req = helix::chat::GetUserChatColorRequest::user_ids(user_ids); + let req = helix::chat::GetUserChatColorRequest::user_ids(user_ids.as_ref()); Ok(self.req_get(req, token).await?.data) } /// Add a channel moderator - pub async fn add_channel_moderator( + pub async fn add_channel_moderator<'b, T>( &'a self, - broadcaster_id: impl Into, - moderator_id: impl Into, + broadcaster_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, + moderator_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, token: &T, ) -> Result> where T: TwitchToken + ?Sized, { let req = helix::moderation::AddChannelModeratorRequest { - broadcaster_id: broadcaster_id.into(), - moderator_id: moderator_id.into(), + broadcaster_id: broadcaster_id.to_cow(), + moderator_id: moderator_id.to_cow(), }; Ok(self.req_post(req, helix::EmptyBody, token).await?.data) } /// Remove a channel moderator - pub async fn remove_channel_moderator( + pub async fn remove_channel_moderator<'b, T>( &'a self, - broadcaster_id: impl Into, - moderator_id: impl Into, + broadcaster_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, + moderator_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, token: &T, ) -> Result> where T: TwitchToken + ?Sized, { let req = helix::moderation::RemoveChannelModeratorRequest { - broadcaster_id: broadcaster_id.into(), - moderator_id: moderator_id.into(), + broadcaster_id: broadcaster_id.to_cow(), + moderator_id: moderator_id.to_cow(), }; Ok(self.req_delete(req, token).await?.data) } /// Get channel VIPs - pub fn get_vips_in_channel( + pub fn get_vips_in_channel<'b: 'a, T>( &'a self, - broadcaster_id: impl Into, + broadcaster_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, token: &'a T, ) -> std::pin::Pin< Box>> + 'a>, @@ -820,56 +829,60 @@ impl<'a, C: crate::HttpClient<'a> + Sync> HelixClient<'a, C> { } /// Add a channel vip - pub async fn add_channel_vip( + pub async fn add_channel_vip<'b, T>( &'a self, - broadcaster_id: impl Into, - user_id: impl Into, + broadcaster_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, + user_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, token: &T, ) -> Result> where T: TwitchToken + ?Sized, { let req = helix::channels::AddChannelVipRequest { - broadcaster_id: broadcaster_id.into(), - user_id: user_id.into(), + broadcaster_id: broadcaster_id.to_cow(), + user_id: user_id.to_cow(), }; Ok(self.req_post(req, helix::EmptyBody, token).await?.data) } /// Remove a channel vip - pub async fn remove_channel_vip( + pub async fn remove_channel_vip<'b, T>( &'a self, - broadcaster_id: impl Into, - user_id: impl Into, + broadcaster_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, + user_id: impl types::IntoCow<'b, types::UserIdRef> + 'b, token: &T, ) -> Result> where T: TwitchToken + ?Sized, { let req = helix::channels::RemoveChannelVipRequest { - broadcaster_id: broadcaster_id.into(), - user_id: user_id.into(), + broadcaster_id: broadcaster_id.to_cow(), + user_id: user_id.to_cow(), }; Ok(self.req_delete(req, token).await?.data) } /// Send a whisper - pub async fn send_whisper( + pub async fn send_whisper<'b, T>( &'a self, - from: impl Into, - to: impl Into, - message: impl std::fmt::Display, + from: impl types::IntoCow<'b, types::UserIdRef> + 'b, + to: impl types::IntoCow<'b, types::UserIdRef> + 'b, + message: impl Into<&'b str>, token: &T, ) -> Result> where T: TwitchToken + ?Sized, { - let req = helix::whispers::SendWhisperRequest::new(from, to); - let body = helix::whispers::SendWhisperBody::new(message); - - Ok(self.req_post(req, body, token).await?.data) + Ok(self + .req_post( + helix::whispers::SendWhisperRequest::new(from, to), + helix::whispers::SendWhisperBody::new(message.into()), + token, + ) + .await? + .data) } } diff --git a/src/helix/endpoints/bits/get_bits_leaderboard.rs b/src/helix/endpoints/bits/get_bits_leaderboard.rs index e17e653016..847e80149b 100644 --- a/src/helix/endpoints/bits/get_bits_leaderboard.rs +++ b/src/helix/endpoints/bits/get_bits_leaderboard.rs @@ -10,7 +10,7 @@ //! //! ```rust, no_run //! use twitch_api::helix::bits::get_bits_leaderboard; -//! let request = get_bits_leaderboard::GetBitsLeaderboardRequest::new().period("day".to_string()); +//! let request = get_bits_leaderboard::GetBitsLeaderboardRequest::new().period("day"); //! // Get leaderbord for the lifetime of the channel //! let request = get_bits_leaderboard::GetBitsLeaderboardRequest::new(); //! ``` @@ -44,7 +44,7 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug, Default)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetBitsLeaderboardRequest { +pub struct GetBitsLeaderboardRequest<'a> { /// Number of results to be returned. Maximum: 100. Default: 10. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub count: Option, @@ -57,16 +57,19 @@ pub struct GetBitsLeaderboardRequest { /// * "year" – 00:00:00 on the first day of the year specified in started_at, through 00:00:00 on the first day of the following year. /// * "all" – The lifetime of the broadcaster's channel. If this is specified (or used by default), started_at is ignored. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub period: Option, + #[serde(borrow)] + pub period: Option>, /// Timestamp for the period over which the returned data is aggregated. Must be in RFC 3339 format. If this is not provided, data is aggregated over the current period; e.g., the current day/week/month/year. This value is ignored if period is "all". #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub started_at: Option, + #[serde(borrow)] + pub started_at: Option>, /// ID of the user whose results are returned; i.e., the person who paid for the Bits. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub user_id: Option, + #[serde(borrow)] + pub user_id: Option>, } -impl GetBitsLeaderboardRequest { +impl<'a> GetBitsLeaderboardRequest<'a> { /// Number of results to be returned. Maximum: 100. Default: 10. pub fn count(self, count: i32) -> Self { Self { @@ -76,25 +79,25 @@ impl GetBitsLeaderboardRequest { } /// Get loaderboard for this period. Valid values: `"day"`, `"week"`, `"month"`, `"year"`, `"all"` - pub fn period(self, period: String) -> Self { + pub fn period(self, period: impl Into>) -> Self { Self { - period: Some(period), + period: Some(period.into()), ..self } } /// Get leaderboard starting at this timestamp - pub fn started_at(self, started_at: impl Into) -> Self { + pub fn started_at(self, started_at: impl types::IntoCow<'a, types::TimestampRef> + 'a) -> Self { Self { - started_at: Some(started_at.into()), + started_at: Some(started_at.to_cow()), ..self } } /// Get leaderboard where this user is included (if they are on the leaderboard) - pub fn user_id(self, user_id: impl Into) -> Self { + pub fn user_id(self, user_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - user_id: Some(user_id.into()), + user_id: Some(user_id.to_cow()), ..self } } @@ -146,7 +149,7 @@ pub struct LeaderboardUser { pub user_login: types::UserName, } -impl Request for GetBitsLeaderboardRequest { +impl Request for GetBitsLeaderboardRequest<'_> { type Response = BitsLeaderboard; const PATH: &'static str = "bits/leaderboard"; @@ -154,7 +157,7 @@ impl Request for GetBitsLeaderboardRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetBitsLeaderboardRequest { +impl RequestGet for GetBitsLeaderboardRequest<'_> { fn parse_inner_response( request: Option, uri: &http::Uri, diff --git a/src/helix/endpoints/bits/get_cheermotes.rs b/src/helix/endpoints/bits/get_cheermotes.rs index 24df38eb1d..5685569ad1 100644 --- a/src/helix/endpoints/bits/get_cheermotes.rs +++ b/src/helix/endpoints/bits/get_cheermotes.rs @@ -44,20 +44,21 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug, Default)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetCheermotesRequest { +pub struct GetCheermotesRequest<'a> { /// ID for the broadcaster who might own specialized Cheermotes. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub broadcaster_id: Option, + #[serde(borrow)] + pub broadcaster_id: Option>, } -impl GetCheermotesRequest { +impl<'a> GetCheermotesRequest<'a> { /// Get available Cheermotes. pub fn new() -> Self { Self::default() } /// Get Cheermotes in a specific broadcasters channel. - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: Some(broadcaster_id.into()), + broadcaster_id: Some(broadcaster_id.to_cow()), } } } @@ -176,7 +177,7 @@ pub struct CheermoteImageArray { #[serde(transparent)] pub struct Level(pub String); -impl Request for GetCheermotesRequest { +impl Request for GetCheermotesRequest<'_> { type Response = Vec; const PATH: &'static str = "bits/cheermotes"; @@ -184,7 +185,7 @@ impl Request for GetCheermotesRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetCheermotesRequest {} +impl RequestGet for GetCheermotesRequest<'_> {} #[cfg(test)] #[test] diff --git a/src/helix/endpoints/bits/mod.rs b/src/helix/endpoints/bits/mod.rs index ca5ffa5ee2..2ae3d728ae 100644 --- a/src/helix/endpoints/bits/mod.rs +++ b/src/helix/endpoints/bits/mod.rs @@ -22,6 +22,7 @@ use crate::{ types, }; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; pub mod get_bits_leaderboard; pub mod get_cheermotes; diff --git a/src/helix/endpoints/channels/add_channel_vip.rs b/src/helix/endpoints/channels/add_channel_vip.rs index e4f9c28554..3a7ee1b87a 100644 --- a/src/helix/endpoints/channels/add_channel_vip.rs +++ b/src/helix/endpoints/channels/add_channel_vip.rs @@ -41,24 +41,26 @@ use helix::RequestPost; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct AddChannelVipRequest { +pub struct AddChannelVipRequest<'a> { /// The ID of the broadcaster that’s granting VIP status to the user. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// The ID of the user to add as a VIP in the broadcaster’s chat room. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub user_id: types::UserId, + #[serde(borrow)] + pub user_id: Cow<'a, types::UserIdRef>, } -impl AddChannelVipRequest { +impl<'a> AddChannelVipRequest<'a> { /// Add a channel VIP pub fn new( - broadcaster_id: impl Into, - user_id: impl Into, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + user_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - user_id: user_id.into(), + broadcaster_id: broadcaster_id.to_cow(), + user_id: user_id.to_cow(), } } } @@ -74,7 +76,7 @@ pub enum AddChannelVipResponse { Success, } -impl Request for AddChannelVipRequest { +impl Request for AddChannelVipRequest<'_> { type Response = AddChannelVipResponse; const PATH: &'static str = "channels/vips"; @@ -82,7 +84,7 @@ impl Request for AddChannelVipRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelManageVips]; } -impl RequestPost for AddChannelVipRequest { +impl RequestPost for AddChannelVipRequest<'_> { type Body = helix::EmptyBody; fn parse_inner_response( diff --git a/src/helix/endpoints/channels/get_channel_editors.rs b/src/helix/endpoints/channels/get_channel_editors.rs index 93a4a5f85e..737d4e07a1 100644 --- a/src/helix/endpoints/channels/get_channel_editors.rs +++ b/src/helix/endpoints/channels/get_channel_editors.rs @@ -43,17 +43,18 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetChannelEditorsRequest { +pub struct GetChannelEditorsRequest<'a> { /// Broadcaster’s user ID associated with the channel. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, } -impl GetChannelEditorsRequest { +impl<'a> GetChannelEditorsRequest<'a> { /// Get specified broadcasters channel editors - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), } } } @@ -73,7 +74,7 @@ pub struct Editor { pub created_at: types::Timestamp, } -impl Request for GetChannelEditorsRequest { +impl Request for GetChannelEditorsRequest<'_> { type Response = Vec; const PATH: &'static str = "channels/editors"; @@ -81,13 +82,13 @@ impl Request for GetChannelEditorsRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelReadEditors]; } -impl RequestGet for GetChannelEditorsRequest {} +impl RequestGet for GetChannelEditorsRequest<'_> {} #[cfg(test)] #[test] fn test_request() { use helix::*; - let req = GetChannelEditorsRequest::broadcaster_id("44445592".to_string()); + let req = GetChannelEditorsRequest::broadcaster_id("44445592"); // From twitch docs let data = br#" diff --git a/src/helix/endpoints/channels/get_channel_information.rs b/src/helix/endpoints/channels/get_channel_information.rs index 041e516d84..05817bbf4e 100644 --- a/src/helix/endpoints/channels/get_channel_information.rs +++ b/src/helix/endpoints/channels/get_channel_information.rs @@ -43,17 +43,18 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetChannelInformationRequest { +pub struct GetChannelInformationRequest<'a> { /// ID of the channel #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, } -impl GetChannelInformationRequest { +impl<'a> GetChannelInformationRequest<'a> { /// Get channel information for a specific broadcaster. - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), } } } @@ -87,7 +88,7 @@ pub struct ChannelInformation { pub delay: i64, } -impl Request for GetChannelInformationRequest { +impl Request for GetChannelInformationRequest<'_> { type Response = Option; const PATH: &'static str = "channels"; @@ -95,7 +96,7 @@ impl Request for GetChannelInformationRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetChannelInformationRequest { +impl RequestGet for GetChannelInformationRequest<'_> { fn parse_inner_response( request: Option, uri: &http::Uri, @@ -128,7 +129,7 @@ impl RequestGet for GetChannelInformationRequest { #[test] fn test_request() { use helix::*; - let req = GetChannelInformationRequest::broadcaster_id("44445592".to_string()); + let req = GetChannelInformationRequest::broadcaster_id("44445592"); // From twitch docs let data = br#" diff --git a/src/helix/endpoints/channels/get_vips.rs b/src/helix/endpoints/channels/get_vips.rs index 81912b2f9c..5ae595024b 100644 --- a/src/helix/endpoints/channels/get_vips.rs +++ b/src/helix/endpoints/channels/get_vips.rs @@ -43,27 +43,29 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetVipsRequest { +pub struct GetVipsRequest<'a> { /// The ID of the broadcaster whose list of VIPs you want to get. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// Filters the list for specific VIPs. To specify more than one user, include the user_id parameter for each user to get. For example, &user_id=1234&user_id=5678. The maximum number of IDs that you may specify is 100. Ignores those users in the list that aren’t VIPs. #[cfg_attr(feature = "typed-builder", builder(default))] - pub user_id: Vec, + #[serde(borrow)] + pub user_id: Cow<'a, [&'a types::UserIdRef]>, /// The maximum number of items to return per page in the response. The minimum page size is 1 item per page and the maximum is 100. The default is 20. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub first: Option, /// The cursor used to get the next page of results. The Pagination object in the response contains the cursor’s value. Read more. #[cfg_attr(feature = "typed-builder", builder(default))] - pub after: Option, + pub after: Option>, } -impl GetVipsRequest { +impl<'a> GetVipsRequest<'a> { /// Get channel VIPs in channel - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - user_id: vec![], + broadcaster_id: broadcaster_id.to_cow(), + user_id: Cow::Borrowed(&[]), first: None, after: None, } @@ -75,18 +77,10 @@ impl GetVipsRequest { self } - /// Filter response with this ID - pub fn user_id(self, user_id: impl Into) -> Self { - Self { - user_id: vec![user_id.into()], - ..self - } - } - /// Filter response with these IDs - pub fn user_ids(self, user_ids: impl IntoIterator>) -> Self { + pub fn user_ids(self, user_ids: impl Into>) -> Self { Self { - user_id: user_ids.into_iter().map(Into::into).collect(), + user_id: user_ids.into(), ..self } } @@ -107,7 +101,7 @@ pub struct Vip { pub user_login: types::UserName, } -impl Request for GetVipsRequest { +impl Request for GetVipsRequest<'_> { type Response = Vec; const PATH: &'static str = "channels/vips"; @@ -115,11 +109,13 @@ impl Request for GetVipsRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelReadVips]; } -impl helix::Paginated for GetVipsRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor; } +impl helix::Paginated for GetVipsRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } -impl RequestGet for GetVipsRequest {} +impl RequestGet for GetVipsRequest<'_> {} #[cfg(test)] #[test] @@ -159,7 +155,9 @@ fn test_request_all() { #[test] fn test_request_multiple() { use helix::*; - let req = GetVipsRequest::broadcaster_id("123").user_ids(["456", "678"]); + + let ids: &[&types::UserIdRef] = &["456".into(), "678".into()]; + let req = GetVipsRequest::broadcaster_id("123").user_ids(ids); // From twitch docs // FIXME: Example has ... diff --git a/src/helix/endpoints/channels/mod.rs b/src/helix/endpoints/channels/mod.rs index 453736ca7e..b189294b11 100644 --- a/src/helix/endpoints/channels/mod.rs +++ b/src/helix/endpoints/channels/mod.rs @@ -24,6 +24,7 @@ use crate::{ types, }; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; pub mod add_channel_vip; pub mod get_channel_editors; diff --git a/src/helix/endpoints/channels/modify_channel_information.rs b/src/helix/endpoints/channels/modify_channel_information.rs index 9f2f78216d..502b1ad3eb 100644 --- a/src/helix/endpoints/channels/modify_channel_information.rs +++ b/src/helix/endpoints/channels/modify_channel_information.rs @@ -5,13 +5,12 @@ //! //! ## Request: [ModifyChannelInformationRequest] //! -//! To use this endpoint, construct a [`ModifyChannelInformationRequest`] with the [`ModifyChannelInformationRequest::builder()`] method. +//! To use this endpoint, construct a [`ModifyChannelInformationRequest`] with the [`ModifyChannelInformationRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::channels::modify_channel_information; -//! let request = modify_channel_information::ModifyChannelInformationRequest::builder() -//! .broadcaster_id("1234") -//! .build(); +//! let request = +//! modify_channel_information::ModifyChannelInformationRequest::broadcaster_id("1234"); //! ``` //! //! ## Body: [ModifyChannelInformationBody] @@ -20,9 +19,8 @@ //! //! ``` //! # use twitch_api::helix::channels::modify_channel_information; -//! let body = modify_channel_information::ModifyChannelInformationBody::builder() -//! .title("Hello World!".to_string()) -//! .build(); +//! let mut body = modify_channel_information::ModifyChannelInformationBody::new(); +//! body.title("Hello World!"); //! ``` //! //! ## Response: [ModifyChannelInformation] @@ -39,12 +37,9 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = modify_channel_information::ModifyChannelInformationRequest::builder() -//! .broadcaster_id("1234") -//! .build(); -//! let body = modify_channel_information::ModifyChannelInformationBody::builder() -//! .title("Hello World!".to_string()) -//! .build(); +//! let request = modify_channel_information::ModifyChannelInformationRequest::broadcaster_id("1234"); +//! let mut body = modify_channel_information::ModifyChannelInformationBody::new(); +//! body.title("Hello World!"); //! let response: modify_channel_information::ModifyChannelInformation = client.req_patch(request, body, &token).await?.data; //! # Ok(()) //! # } @@ -61,17 +56,18 @@ use helix::RequestPatch; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct ModifyChannelInformationRequest { +pub struct ModifyChannelInformationRequest<'a> { /// ID of the channel #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, } -impl ModifyChannelInformationRequest { +impl<'a> ModifyChannelInformationRequest<'a> { /// Modify specified broadcasters channel - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { ModifyChannelInformationRequest { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), } } } @@ -83,22 +79,22 @@ impl ModifyChannelInformationRequest { #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug, Default)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct ModifyChannelInformationBody { +pub struct ModifyChannelInformationBody<'a> { /// Current game ID being played on the channel. Use “0” or “” (an empty string) to unset the game. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - #[serde(skip_serializing_if = "Option::is_none")] - pub game_id: Option, + #[serde(skip_serializing_if = "Option::is_none", borrow)] + pub game_id: Option>, /// Language of the channel #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - #[serde(skip_serializing_if = "Option::is_none")] - pub broadcaster_language: Option, + #[serde(skip_serializing_if = "Option::is_none", borrow)] + pub broadcaster_language: Option>, /// Title of the stream. Value must not be an empty string. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - #[serde(skip_serializing_if = "Option::is_none")] - pub title: Option, + #[serde(skip_serializing_if = "Option::is_none", borrow)] + pub title: Option>, } -impl ModifyChannelInformationBody { +impl<'a> ModifyChannelInformationBody<'a> { /// Data to set on the stream. /// /// # Examples @@ -111,25 +107,31 @@ impl ModifyChannelInformationBody { pub fn new() -> Self { Default::default() } /// Current game ID being played on the channel. Use “0” or “” (an empty string) to unset the game. - pub fn game_id(&mut self, game_id: impl Into) -> &mut Self { - self.game_id = Some(game_id.into()); + pub fn game_id( + &mut self, + game_id: impl types::IntoCow<'a, types::CategoryIdRef> + 'a, + ) -> &mut Self { + self.game_id = Some(game_id.to_cow()); self } /// Language of the channel - pub fn broadcaster_language(&mut self, broadcaster_language: impl Into) -> &mut Self { + pub fn broadcaster_language( + &mut self, + broadcaster_language: impl Into>, + ) -> &mut Self { self.broadcaster_language = Some(broadcaster_language.into()); self } /// Title of the stream. Value must not be an empty string. - pub fn title(&mut self, title: impl Into) -> &mut ModifyChannelInformationBody { + pub fn title(&mut self, title: impl Into>) -> &mut Self { self.title = Some(title.into()); self } } -impl helix::private::SealedSerialize for ModifyChannelInformationBody {} +impl helix::private::SealedSerialize for ModifyChannelInformationBody<'_> {} /// Return Values for [Modify Channel Information](super::modify_channel_information) /// @@ -141,7 +143,7 @@ pub enum ModifyChannelInformation { Success, } -impl Request for ModifyChannelInformationRequest { +impl Request for ModifyChannelInformationRequest<'_> { type Response = ModifyChannelInformation; const PATH: &'static str = "channels"; @@ -149,8 +151,8 @@ impl Request for ModifyChannelInformationRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::UserEditBroadcast]; } -impl RequestPatch for ModifyChannelInformationRequest { - type Body = ModifyChannelInformationBody; +impl<'a> RequestPatch for ModifyChannelInformationRequest<'a> { + type Body = ModifyChannelInformationBody<'a>; fn parse_inner_response( request: Option, @@ -189,10 +191,8 @@ fn test_request() { use helix::*; let req = ModifyChannelInformationRequest::broadcaster_id("0"); - let body = ModifyChannelInformationBody { - title: Some("Hello World!".to_string()), - ..Default::default() - }; + let mut body = ModifyChannelInformationBody::new(); + body.title("Hello World!"); dbg!(req.create_request(body, "token", "clientid").unwrap()); diff --git a/src/helix/endpoints/channels/remove_channel_vip.rs b/src/helix/endpoints/channels/remove_channel_vip.rs index a4e5dcf198..6009e88ea4 100644 --- a/src/helix/endpoints/channels/remove_channel_vip.rs +++ b/src/helix/endpoints/channels/remove_channel_vip.rs @@ -5,14 +5,11 @@ //! //! ## Request: [RemoveChannelVipRequest] //! -//! To use this endpoint, construct a [`RemoveChannelVipRequest`] with the [`RemoveChannelVipRequest::builder()`] method. +//! To use this endpoint, construct a [`RemoveChannelVipRequest`] with the [`RemoveChannelVipRequest::new()`] method. //! //! ```rust //! use twitch_api::helix::channels::remove_channel_vip; -//! let request = remove_channel_vip::RemoveChannelVipRequest::builder() -//! .broadcaster_id("1234") -//! .user_id("1337") -//! .build(); +//! let request = remove_channel_vip::RemoveChannelVipRequest::new("1234", "1337"); //! ``` //! //! ## Response: [RemoveChannelVipResponse] @@ -27,10 +24,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = remove_channel_vip::RemoveChannelVipRequest::builder() -//! .broadcaster_id("1234") -//! .user_id("1337") -//! .build(); +//! let request = remove_channel_vip::RemoveChannelVipRequest::new("1234", "1337"); //! let response: remove_channel_vip::RemoveChannelVipResponse = client.req_delete(request, &token).await?.data; //! # Ok(()) //! # } @@ -48,24 +42,26 @@ use helix::RequestDelete; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct RemoveChannelVipRequest { +pub struct RemoveChannelVipRequest<'a> { /// The ID of the broadcaster that’s removing VIP status from the user. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// The ID of the user to remove as a VIP from the broadcaster’s chat room. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub user_id: types::UserId, + #[serde(borrow)] + pub user_id: Cow<'a, types::UserIdRef>, } -impl RemoveChannelVipRequest { +impl<'a> RemoveChannelVipRequest<'a> { /// Remove channel VIP pub fn new( - broadcaster_id: impl Into, - user_id: impl Into, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + user_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - user_id: user_id.into(), + broadcaster_id: broadcaster_id.to_cow(), + user_id: user_id.to_cow(), } } } @@ -80,7 +76,7 @@ pub enum RemoveChannelVipResponse { Success, } -impl Request for RemoveChannelVipRequest { +impl Request for RemoveChannelVipRequest<'_> { type Response = RemoveChannelVipResponse; const PATH: &'static str = "channels/vips"; @@ -88,7 +84,7 @@ impl Request for RemoveChannelVipRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelManageVips]; } -impl RequestDelete for RemoveChannelVipRequest { +impl RequestDelete for RemoveChannelVipRequest<'_> { fn parse_inner_response( request: Option, uri: &http::Uri, diff --git a/src/helix/endpoints/channels/start_commercial.rs b/src/helix/endpoints/channels/start_commercial.rs index f3ffa55c87..27ed7a8209 100644 --- a/src/helix/endpoints/channels/start_commercial.rs +++ b/src/helix/endpoints/channels/start_commercial.rs @@ -18,10 +18,10 @@ //! //! ``` //! # use twitch_api::helix::channels::start_commercial; -//! let body = start_commercial::StartCommercialBody::builder() -//! .broadcaster_id("1234".to_string()) -//! .length(twitch_api::types::CommercialLength::Length90) -//! .build(); +//! let body = start_commercial::StartCommercialBody::new( +//! "1234", +//! twitch_api::types::CommercialLength::Length90, +//! ); //! ``` //! //! ## Response: [StartCommercialRequest] @@ -37,10 +37,7 @@ //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; //! let request = start_commercial::StartCommercialRequest::new(); -//! let body = start_commercial::StartCommercialBody::builder() -//! .broadcaster_id("1234".to_string()) -//! .length(twitch_api::types::CommercialLength::Length90) -//! .build(); +//! let body = start_commercial::StartCommercialBody::new("1234", twitch_api::types::CommercialLength::Length90); //! let response: Vec = client.req_post(request, body, &token).await?.data; //! # Ok(()) //! # } @@ -48,6 +45,8 @@ //! //! You can also get the [`http::Request`] with [`request.create_request(body, &token, &client_id)`](helix::RequestPost::create_request) //! and parse the [`http::Response`] with [`StartCommercialRequest::parse_response(None, &request.get_uri(), response)`](StartCommercialRequest::parse_response) +use std::marker::PhantomData; + use super::*; use helix::RequestPost; @@ -55,17 +54,16 @@ use helix::RequestPost; /// Query Parameters for [Start Commercial](super::start_commercial) /// /// [`start-commercial`](https://dev.twitch.tv/docs/api/reference#start-commercial) -#[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] +#[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug, Default)] #[non_exhaustive] -pub struct StartCommercialRequest {} - -impl StartCommercialRequest { - /// Create a new [`StartCommercialRequest`] - pub fn new() -> Self { StartCommercialRequest {} } +pub struct StartCommercialRequest<'a> { + #[serde(skip)] + _marker: PhantomData<&'a ()>, } -impl Default for StartCommercialRequest { - fn default() -> Self { StartCommercialRequest::new() } +impl StartCommercialRequest<'_> { + /// Create a new [`StartCommercialRequest`] + pub fn new() -> Self { StartCommercialRequest::default() } } /// Body Parameters for [Start Commercial](super::start_commercial) @@ -74,22 +72,23 @@ impl Default for StartCommercialRequest { #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct StartCommercialBody { +pub struct StartCommercialBody<'a> { /// ID of the channel requesting a commercial #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// Desired length of the commercial in seconds. Valid options are 30, 60, 90, 120, 150, 180. pub length: types::CommercialLength, } -impl StartCommercialBody { +impl<'a> StartCommercialBody<'a> { /// Start a commercial in this broadcasters channel pub fn new( - broadcaster_id: impl Into, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, length: impl Into, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), length: length.into(), } } @@ -110,7 +109,7 @@ pub struct StartCommercial { pub retry_after: u64, } -impl Request for StartCommercialRequest { +impl Request for StartCommercialRequest<'_> { /// FIXME: Make non-vec type Response = Vec; @@ -119,17 +118,17 @@ impl Request for StartCommercialRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelEditCommercial]; } -impl RequestPost for StartCommercialRequest { - type Body = StartCommercialBody; +impl<'a> RequestPost for StartCommercialRequest<'a> { + type Body = StartCommercialBody<'a>; } -impl helix::private::SealedSerialize for StartCommercialBody {} +impl helix::private::SealedSerialize for StartCommercialBody<'_> {} #[cfg(test)] #[test] fn test_request() { use helix::*; - let req = StartCommercialRequest {}; + let req = StartCommercialRequest::default(); let body = StartCommercialBody::new("1234", types::CommercialLength::Length120); diff --git a/src/helix/endpoints/chat/get_channel_chat_badges.rs b/src/helix/endpoints/chat/get_channel_chat_badges.rs index 6e40f5c17a..5408a49581 100644 --- a/src/helix/endpoints/chat/get_channel_chat_badges.rs +++ b/src/helix/endpoints/chat/get_channel_chat_badges.rs @@ -5,13 +5,11 @@ //! //! ## Request: [GetChannelChatBadgesRequest] //! -//! To use this endpoint, construct a [`GetChannelChatBadgesRequest`] with the [`GetChannelChatBadgesRequest::builder()`] method. +//! To use this endpoint, construct a [`GetChannelChatBadgesRequest`] with the [`GetChannelChatBadgesRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::chat::get_channel_chat_badges; -//! let request = get_channel_chat_badges::GetChannelChatBadgesRequest::builder() -//! .broadcaster_id("1234".to_string()) -//! .build(); +//! let request = get_channel_chat_badges::GetChannelChatBadgesRequest::broadcaster_id("1234"); //! ``` //! //! ## Response: [BadgeSet] @@ -26,9 +24,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_channel_chat_badges::GetChannelChatBadgesRequest::builder() -//! .broadcaster_id("1234".to_string()) -//! .build(); +//! let request = get_channel_chat_badges::GetChannelChatBadgesRequest::broadcaster_id("1234"); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -46,17 +42,18 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetChannelChatBadgesRequest { +pub struct GetChannelChatBadgesRequest<'a> { /// The broadcaster whose chat badges are being requested. Provided broadcaster_id must match the user_id in the user OAuth token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, } -impl GetChannelChatBadgesRequest { +impl<'a> GetChannelChatBadgesRequest<'a> { /// Get chat badges for the specified broadcaster. - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), } } } @@ -66,7 +63,7 @@ impl GetChannelChatBadgesRequest { /// [`get-channel-chat-badges`](https://dev.twitch.tv/docs/api/reference#get-channel-chat-badges) pub type GetChannelChatBadgesResponse = BadgeSet; -impl Request for GetChannelChatBadgesRequest { +impl Request for GetChannelChatBadgesRequest<'_> { type Response = Vec; const PATH: &'static str = "chat/badges"; @@ -74,7 +71,7 @@ impl Request for GetChannelChatBadgesRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetChannelChatBadgesRequest {} +impl RequestGet for GetChannelChatBadgesRequest<'_> {} #[cfg(test)] #[test] diff --git a/src/helix/endpoints/chat/get_channel_emotes.rs b/src/helix/endpoints/chat/get_channel_emotes.rs index 8696ac2e47..e009dd3899 100644 --- a/src/helix/endpoints/chat/get_channel_emotes.rs +++ b/src/helix/endpoints/chat/get_channel_emotes.rs @@ -5,13 +5,11 @@ //! //! ## Request: [GetChannelEmotesRequest] //! -//! To use this endpoint, construct a [`GetChannelEmotesRequest`] with the [`GetChannelEmotesRequest::builder()`] method. +//! To use this endpoint, construct a [`GetChannelEmotesRequest`] with the [`GetChannelEmotesRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::chat::get_channel_emotes; -//! let request = get_channel_emotes::GetChannelEmotesRequest::builder() -//! .broadcaster_id("1234".to_string()) -//! .build(); +//! let request = get_channel_emotes::GetChannelEmotesRequest::broadcaster_id("1234"); //! ``` //! //! ## Response: [ChannelEmote] @@ -26,9 +24,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_channel_emotes::GetChannelEmotesRequest::builder() -//! .broadcaster_id("1234".to_string()) -//! .build(); +//! let request = get_channel_emotes::GetChannelEmotesRequest::broadcaster_id("1234"); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -46,17 +42,18 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetChannelEmotesRequest { +pub struct GetChannelEmotesRequest<'a> { /// The broadcaster whose emotes are being requested. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, } -impl GetChannelEmotesRequest { +impl<'a> GetChannelEmotesRequest<'a> { /// Get emotes in a specific broadcasters channel. - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), } } } @@ -66,7 +63,7 @@ impl GetChannelEmotesRequest { /// [`get-channel-emotes`](https://dev.twitch.tv/docs/api/reference#get-channel-emotes) pub type GetChannelEmotesResponse = ChannelEmote; -impl Request for GetChannelEmotesRequest { +impl Request for GetChannelEmotesRequest<'_> { type Response = Vec; const PATH: &'static str = "chat/emotes"; @@ -74,7 +71,7 @@ impl Request for GetChannelEmotesRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetChannelEmotesRequest {} +impl RequestGet for GetChannelEmotesRequest<'_> {} #[cfg(test)] #[test] diff --git a/src/helix/endpoints/chat/get_chat_settings.rs b/src/helix/endpoints/chat/get_chat_settings.rs index 786c24f530..d595e7eab7 100644 --- a/src/helix/endpoints/chat/get_chat_settings.rs +++ b/src/helix/endpoints/chat/get_chat_settings.rs @@ -13,18 +13,16 @@ //! //! ## Request: [GetChatSettingsRequest] //! -//! To use this endpoint, construct a [`GetChatSettingsRequest`] with the [`GetChatSettingsRequest::builder()`] method. +//! To use this endpoint, construct a [`GetChatSettingsRequest`] with the [`GetChatSettingsRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::{ //! helix::{self, chat::get_chat_settings}, //! types, //! }; -//! let request = get_chat_settings::GetChatSettingsRequest::builder() -//! .broadcaster_id("1234567".to_owned()) +//! let request = get_chat_settings::GetChatSettingsRequest::broadcaster_id("1234567") //! // optional -//! .moderator_id(types::UserId::from("9876543")) -//! .build(); +//! .moderator_id("9876543"); //! ``` //! //! ## Response: [ChatSettings] @@ -39,11 +37,9 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_chat_settings::GetChatSettingsRequest::builder() -//! .broadcaster_id("1234567".to_owned()) +//! let request = get_chat_settings::GetChatSettingsRequest::broadcaster_id("1234567") //! // optional -//! .moderator_id(types::UserId::from("9876543")) -//! .build(); +//! .moderator_id("9876543"); //! let response: helix::chat::ChatSettings = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -61,10 +57,11 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetChatSettingsRequest { +pub struct GetChatSettingsRequest<'a> { /// The ID of the broadcaster whose chat settings you want to get. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// Required only to access the [`non_moderator_chat_delay`](ChatSettings::non_moderator_chat_delay) /// or [`non_moderator_chat_delay_duration`](ChatSettings::non_moderator_chat_delay_duration) settings. /// If you want to access these settings, you need to provide a valid [`moderator_id`](Self::moderator_id) @@ -77,14 +74,15 @@ pub struct GetChatSettingsRequest { /// If the broadcaster wants to get their own settings (instead of having the moderator do it), /// set this parameter to the broadcaster’s ID, too. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub moderator_id: Option, + #[serde(borrow)] + pub moderator_id: Option>, } -impl GetChatSettingsRequest { +impl<'a> GetChatSettingsRequest<'a> { /// Get chat settings for broadcasters channel - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), moderator_id: None, } } @@ -93,13 +91,16 @@ impl GetChatSettingsRequest { /// /// Required only to access the [`non_moderator_chat_delay`](ChatSettings::non_moderator_chat_delay) /// or [`non_moderator_chat_delay_duration`](ChatSettings::non_moderator_chat_delay_duration) settings. - pub fn moderator_id(mut self, moderator_id: impl Into) -> Self { - self.moderator_id = Some(moderator_id.into()); + pub fn moderator_id( + mut self, + moderator_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + ) -> Self { + self.moderator_id = Some(moderator_id.to_cow()); self } } -impl Request for GetChatSettingsRequest { +impl Request for GetChatSettingsRequest<'_> { type Response = ChatSettings; #[cfg(feature = "twitch_oauth2")] @@ -110,7 +111,7 @@ impl Request for GetChatSettingsRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetChatSettingsRequest { +impl RequestGet for GetChatSettingsRequest<'_> { fn parse_inner_response( request: Option, uri: &http::Uri, diff --git a/src/helix/endpoints/chat/get_chatters.rs b/src/helix/endpoints/chat/get_chatters.rs index c06b989db9..943c4988f7 100644 --- a/src/helix/endpoints/chat/get_chatters.rs +++ b/src/helix/endpoints/chat/get_chatters.rs @@ -9,14 +9,11 @@ //! //! ## Request: [GetChattersRequest] //! -//! To use this endpoint, construct a [`GetChattersRequest`] with the [`GetChattersRequest::builder()`] method. +//! To use this endpoint, construct a [`GetChattersRequest`] with the [`GetChattersRequest::new()`] method. //! //! ```rust //! use twitch_api::helix::chat::get_chatters; -//! let request = get_chatters::GetChattersRequest::builder() -//! .broadcaster_id("1234".to_string()) -//! .moderator_id("4321".to_string()) -//! .build(); +//! let request = get_chatters::GetChattersRequest::new("1234", "4321"); //! ``` //! //! ## Response: [Chatter] @@ -31,10 +28,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_chatters::GetChattersRequest::builder() -//! .broadcaster_id("1234".to_string()) -//! .moderator_id("4321".to_string()) -//! .build(); +//! let request = get_chatters::GetChattersRequest::new("1234", "4321"); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -52,36 +46,38 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetChattersRequest { +pub struct GetChattersRequest<'a> { /// The ID of the broadcaster whose list of chatters you want to get. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// The ID of the moderator or the specified broadcaster that’s requesting the list of chatters. This ID must match the user ID associated with the user access token. /// /// The moderator must have permission to moderate the broadcaster’s chat room. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub moderator_id: types::UserId, + #[serde(borrow)] + pub moderator_id: Cow<'a, types::UserIdRef>, /// The maximum number of items to return per page in the response. The minimum page size is 1 item per page and the maximum is 1,000. The default is 100. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub first: Option, /// The cursor used to get the next page of results. The Pagination object in the response contains the cursor’s value. #[cfg_attr(feature = "typed-builder", builder(default))] - pub after: Option, + pub after: Option>, } -impl GetChattersRequest { +impl<'a> GetChattersRequest<'a> { /// Get chatters in broadcasters channel /// /// # Notes /// /// The moderator has to be the token owner and can moderate the chat pub fn new( - broadcaster_id: impl Into, - moderator_id: impl Into, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + moderator_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - moderator_id: moderator_id.into(), + broadcaster_id: broadcaster_id.to_cow(), + moderator_id: moderator_id.to_cow(), first: None, after: None, } @@ -94,8 +90,10 @@ impl GetChattersRequest { } } -impl helix::Paginated for GetChattersRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor } +impl helix::Paginated for GetChattersRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } /// Return Values for [Get Chatters](super::get_chatters) @@ -109,7 +107,7 @@ pub struct Chatter { pub user_login: types::UserName, } -impl Request for GetChattersRequest { +impl Request for GetChattersRequest<'_> { type Response = Vec; const PATH: &'static str = "chat/chatters"; @@ -117,7 +115,7 @@ impl Request for GetChattersRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetChattersRequest {} +impl RequestGet for GetChattersRequest<'_> {} #[cfg(test)] #[test] diff --git a/src/helix/endpoints/chat/get_emote_sets.rs b/src/helix/endpoints/chat/get_emote_sets.rs index b663ae7f25..a4f9636318 100644 --- a/src/helix/endpoints/chat/get_emote_sets.rs +++ b/src/helix/endpoints/chat/get_emote_sets.rs @@ -9,9 +9,8 @@ //! //! ```rust //! use twitch_api::helix::chat::get_emote_sets; -//! let request = get_emote_sets::GetEmoteSetsRequest::builder() -//! .emote_set_id(vec!["1234".into()]) -//! .build(); +//! let ids: &[&twitch_types::EmoteSetIdRef] = &["1234".into()]; +//! let request = get_emote_sets::GetEmoteSetsRequest::emote_set_ids(ids); //! ``` //! //! ## Response: [Emote] @@ -20,15 +19,14 @@ //! //! ```rust, no_run //! use twitch_api::helix::{self, chat::get_emote_sets}; -//! # use twitch_api::client; +//! # use twitch_api::{client, types}; //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_emote_sets::GetEmoteSetsRequest::builder() -//! .emote_set_id(vec!["1234".into()]) -//! .build(); +//! let ids: &[&twitch_types::EmoteSetIdRef] = &["1234".into()]; +//! let request = get_emote_sets::GetEmoteSetsRequest::emote_set_ids(ids); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -46,27 +44,22 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetEmoteSetsRequest { +pub struct GetEmoteSetsRequest<'a> { // FIXME: twitch doc specifies maximum as 25, but it actually is 10 /// The broadcaster whose emotes are being requested. Minimum: 1. Maximum: 10 - #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub emote_set_id: Vec, + #[cfg_attr( + feature = "typed-builder", + builder(default_code = "Cow::Borrowed(&[])", setter(into)) + )] + #[serde(borrow)] + pub emote_set_id: Cow<'a, [&'a types::EmoteSetIdRef]>, } -impl GetEmoteSetsRequest { - /// Get emotes in this set - pub fn emote_set_id(emote_set_id: impl Into) -> Self { - Self { - emote_set_id: vec![emote_set_id.into()], - } - } - +impl<'a> GetEmoteSetsRequest<'a> { /// Get emotes in these sets - pub fn emote_set_ids( - emote_set_ids: impl IntoIterator>, - ) -> Self { + pub fn emote_set_ids(emote_set_ids: impl Into>) -> Self { Self { - emote_set_id: emote_set_ids.into_iter().map(Into::into).collect(), + emote_set_id: emote_set_ids.into(), } } } @@ -106,13 +99,14 @@ impl Emote { /// /// ```rust, no_run /// use twitch_api::helix::{self, chat::get_channel_emotes}; - /// # use twitch_api::client; + /// # use twitch_api::{client, types}; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { /// # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); /// # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); /// # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; - /// let emotes = client.get_emote_sets(["301590448"], &token).await?; + /// let ids: &[&types::EmoteSetIdRef] = &["301590448".into()]; + /// let emotes = client.get_emote_sets(ids, &token).await?; /// assert_eq!(emotes[0].url().size_3x().dark_mode().render(), "https://static-cdn.jtvnw.net/emoticons/v2/emotesv2_dc24652ada1e4c84a5e3ceebae4de709/default/dark/3.0"); /// # Ok(()) /// # } @@ -120,7 +114,7 @@ impl Emote { pub fn url(&self) -> types::EmoteUrlBuilder<'_> { EmoteUrlBuilder::new(&self.id) } } -impl Request for GetEmoteSetsRequest { +impl Request for GetEmoteSetsRequest<'_> { type Response = Vec; const PATH: &'static str = "chat/emotes/set"; @@ -128,13 +122,15 @@ impl Request for GetEmoteSetsRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetEmoteSetsRequest {} +impl RequestGet for GetEmoteSetsRequest<'_> {} #[cfg(test)] #[test] fn test_request() { use helix::*; - let req = GetEmoteSetsRequest::emote_set_id("301590448"); + + let ids: &[&types::EmoteSetIdRef] = &["301590448".into()]; + let req = GetEmoteSetsRequest::emote_set_ids(ids); // From twitch docs // FIXME: Example has ... and is malformed, uses [] in images diff --git a/src/helix/endpoints/chat/get_user_chat_color.rs b/src/helix/endpoints/chat/get_user_chat_color.rs index da779abb9e..3b76bfe511 100644 --- a/src/helix/endpoints/chat/get_user_chat_color.rs +++ b/src/helix/endpoints/chat/get_user_chat_color.rs @@ -10,7 +10,7 @@ //! ```rust //! use twitch_api::helix::chat::get_user_chat_color; //! let request = get_user_chat_color::GetUserChatColorRequest::builder() -//! .user_id(vec!["4321".into()]) +//! .user_id(&["4321".into()][..]) //! .build(); //! ``` //! @@ -20,14 +20,15 @@ //! //! ```rust, no_run //! use twitch_api::helix::{self, chat::get_user_chat_color}; -//! # use twitch_api::client; +//! # use twitch_api::{client, types}; //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; +//! let ids: &[&types::UserIdRef] = &["4321".into()]; //! let request = get_user_chat_color::GetUserChatColorRequest::builder() -//! .user_id(vec!["4321".into()]) +//! .user_id(ids) //! .build(); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) @@ -46,23 +47,21 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetUserChatColorRequest { +pub struct GetUserChatColorRequest<'a> { /// The ID of the user whose color you want to get. - pub user_id: Vec, + #[cfg_attr( + feature = "typed-builder", + builder(default_code = "Cow::Borrowed(&[])", setter(into)) + )] + #[serde(borrow)] + pub user_id: Cow<'a, [&'a types::UserIdRef]>, } -impl GetUserChatColorRequest { - /// Get chat color of specified user - pub fn user_id(user_id: impl Into) -> Self { - Self { - user_id: vec![user_id.into()], - } - } - +impl<'a> GetUserChatColorRequest<'a> { /// Get chat colors of specified users - pub fn user_ids(user_ids: impl IntoIterator>) -> Self { + pub fn user_ids(user_ids: impl Into>) -> Self { Self { - user_id: user_ids.into_iter().map(Into::into).collect(), + user_id: user_ids.into(), } } } @@ -90,7 +89,7 @@ pub struct UserChatColor { pub color: Option, } -impl Request for GetUserChatColorRequest { +impl Request for GetUserChatColorRequest<'_> { type Response = Vec; const PATH: &'static str = "chat/color"; @@ -98,13 +97,14 @@ impl Request for GetUserChatColorRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetUserChatColorRequest {} +impl RequestGet for GetUserChatColorRequest<'_> {} #[cfg(test)] #[test] fn test_request() { use helix::*; - let req = GetUserChatColorRequest::user_ids(["11111", "44444"]); + let ids: &[&types::UserIdRef] = &["11111".into(), "44444".into()]; + let req = GetUserChatColorRequest::user_ids(ids); // From twitch docs // FIXME: Example has ... diff --git a/src/helix/endpoints/chat/mod.rs b/src/helix/endpoints/chat/mod.rs index 4556fa98b8..c3df5f1d04 100644 --- a/src/helix/endpoints/chat/mod.rs +++ b/src/helix/endpoints/chat/mod.rs @@ -5,6 +5,7 @@ use crate::{ types::{self, EmoteUrlBuilder}, }; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; pub mod get_channel_chat_badges; pub mod get_channel_emotes; @@ -114,7 +115,7 @@ impl ChannelEmote { /// # Examples /// /// ```rust, no_run - /// # use twitch_api::{client, helix}; + /// # use twitch_api::{client, helix, types}; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { /// # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); diff --git a/src/helix/endpoints/chat/send_chat_announcement.rs b/src/helix/endpoints/chat/send_chat_announcement.rs index a72c39888b..a1abbe14db 100644 --- a/src/helix/endpoints/chat/send_chat_announcement.rs +++ b/src/helix/endpoints/chat/send_chat_announcement.rs @@ -5,14 +5,11 @@ //! //! ## Request: [SendChatAnnouncementRequest] //! -//! To use this endpoint, construct a [`SendChatAnnouncementRequest`] with the [`SendChatAnnouncementRequest::builder()`] method. +//! To use this endpoint, construct a [`SendChatAnnouncementRequest`] with the [`SendChatAnnouncementRequest::new()`] method. //! //! ```rust //! use twitch_api::helix::chat::send_chat_announcement; -//! let request = send_chat_announcement::SendChatAnnouncementRequest::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .build(); +//! let request = send_chat_announcement::SendChatAnnouncementRequest::new("1234", "5678"); //! ``` //! //! ## Body: [SendChatAnnouncementBody] @@ -22,8 +19,7 @@ //! ``` //! # use twitch_api::helix::chat::send_chat_announcement; //! let body = -//! send_chat_announcement::SendChatAnnouncementBody::new("Hello chat!".to_owned(), "purple") -//! .unwrap(); +//! send_chat_announcement::SendChatAnnouncementBody::new("Hello chat!", "purple").unwrap(); //! ``` //! //! ## Response: [SendChatAnnouncementResponse] @@ -39,12 +35,9 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = send_chat_announcement::SendChatAnnouncementRequest::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .build(); +//! let request = send_chat_announcement::SendChatAnnouncementRequest::new("1234", "5678"); //! let body = send_chat_announcement::SendChatAnnouncementBody::new( -//! "Hello chat!".to_owned(), +//! "Hello chat!", //! "purple", //! ).unwrap(); //! let response: helix::chat::SendChatAnnouncementResponse = client.req_post(request, body, &token).await?.data; @@ -63,26 +56,28 @@ use helix::RequestPost; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct SendChatAnnouncementRequest { +pub struct SendChatAnnouncementRequest<'a> { /// The ID of the broadcaster that owns the chat room to send the announcement to. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// The ID of a user who has permission to moderate the broadcaster’s chat room. /// /// This ID must match the user ID in the OAuth token, which can be a moderator or the broadcaster. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub moderator_id: types::UserId, + #[serde(borrow)] + pub moderator_id: Cow<'a, types::UserIdRef>, } -impl SendChatAnnouncementRequest { +impl<'a> SendChatAnnouncementRequest<'a> { /// Send announcement in channel as this moderator pub fn new( - broadcaster_id: impl Into, - moderator_id: impl Into, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + moderator_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - moderator_id: moderator_id.into(), + broadcaster_id: broadcaster_id.to_cow(), + moderator_id: moderator_id.to_cow(), } } } @@ -93,9 +88,10 @@ impl SendChatAnnouncementRequest { #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct SendChatAnnouncementBody { +pub struct SendChatAnnouncementBody<'a> { /// The announcement to make in the broadcaster’s chat room. Announcements are limited to a maximum of 500 characters; announcements longer than 500 characters are truncated. - pub message: String, + #[serde(borrow)] + pub message: Cow<'a, str>, // FIXME: Enumify? /// The color used to highlight the announcement. Possible case-sensitive values are: /// @@ -110,26 +106,26 @@ pub struct SendChatAnnouncementBody { pub color: AnnouncementColor, } -impl SendChatAnnouncementBody { +impl<'a> SendChatAnnouncementBody<'a> { /// Create a new announcement with specified color pub fn new( - message: String, + message: impl Into>, color: impl std::convert::TryInto, ) -> Result { Ok(Self { - message, + message: message.into(), color: color.try_into()?, }) } } -impl helix::private::SealedSerialize for SendChatAnnouncementBody {} +impl helix::private::SealedSerialize for SendChatAnnouncementBody<'_> {} -impl helix::HelixRequestBody for Vec { +impl<'a> helix::HelixRequestBody for [SendChatAnnouncementBody<'a>] { fn try_to_body(&self) -> Result { #[derive(Serialize)] struct InnerBody<'a> { - data: &'a Vec, + data: &'a [SendChatAnnouncementBody<'a>], } serde_json::to_vec(&InnerBody { data: self }) @@ -149,7 +145,7 @@ pub enum SendChatAnnouncementResponse { Success, } -impl Request for SendChatAnnouncementRequest { +impl Request for SendChatAnnouncementRequest<'_> { // FIXME: this is a single entry type Response = SendChatAnnouncementResponse; @@ -159,8 +155,8 @@ impl Request for SendChatAnnouncementRequest { &[twitch_oauth2::Scope::ModeratorManageAnnouncements]; } -impl RequestPost for SendChatAnnouncementRequest { - type Body = SendChatAnnouncementBody; +impl<'a> RequestPost for SendChatAnnouncementRequest<'a> { + type Body = SendChatAnnouncementBody<'a>; fn parse_inner_response<'d>( request: Option, @@ -195,7 +191,7 @@ fn test_request() { use helix::*; let req = SendChatAnnouncementRequest::new("1234", "5678"); - let body = SendChatAnnouncementBody::new("hello chat!".to_owned(), "purple").unwrap(); + let body = SendChatAnnouncementBody::new("hello chat!", "purple").unwrap(); dbg!(req.create_request(body, "token", "clientid").unwrap()); diff --git a/src/helix/endpoints/chat/update_chat_settings.rs b/src/helix/endpoints/chat/update_chat_settings.rs index 6df7f3b6d3..89d6037c32 100644 --- a/src/helix/endpoints/chat/update_chat_settings.rs +++ b/src/helix/endpoints/chat/update_chat_settings.rs @@ -5,14 +5,11 @@ //! //! ## Request: [UpdateChatSettingsRequest] //! -//! To use this endpoint, construct an [`UpdateChatSettingsRequest`] with the [`UpdateChatSettingsRequest::builder()`] method. +//! To use this endpoint, construct an [`UpdateChatSettingsRequest`] with the [`UpdateChatSettingsRequest::new()`] method. //! //! ```rust //! use twitch_api::helix::chat::update_chat_settings; -//! let request = update_chat_settings::UpdateChatSettingsRequest::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .build(); +//! let request = update_chat_settings::UpdateChatSettingsRequest::new("1234", "5678"); //! ``` //! //! ## Body: [UpdateChatSettingsBody] @@ -41,10 +38,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = update_chat_settings::UpdateChatSettingsRequest::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .build(); +//! let request = update_chat_settings::UpdateChatSettingsRequest::new("1234", "5678"); //! let body = update_chat_settings::UpdateChatSettingsBody::builder() //! .slow_mode(true) //! .slow_mode_wait_time(10) @@ -67,28 +61,30 @@ use helix::RequestPatch; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct UpdateChatSettingsRequest { +pub struct UpdateChatSettingsRequest<'a> { /// The ID of the broadcaster whose chat settings you want to update. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// The ID of a user that has permission to moderate the broadcaster’s chat room. /// This ID must match the user ID associated with the user OAuth token. /// /// If the broadcaster is making the update, also set this parameter to the broadcaster’s ID. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub moderator_id: types::UserId, + #[serde(borrow)] + pub moderator_id: Cow<'a, types::UserIdRef>, } ///FIXME: The moderator_id parameter is redundant, we should make this a client ext function -impl UpdateChatSettingsRequest { +impl<'a> UpdateChatSettingsRequest<'a> { /// Update the chat settings for the specified broadcaster as the specified moderator pub fn new( - broadcaster_id: impl Into, - moderator_id: impl Into, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + moderator_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - moderator_id: moderator_id.into(), + broadcaster_id: broadcaster_id.to_cow(), + moderator_id: moderator_id.to_cow(), } } } @@ -175,7 +171,7 @@ impl helix::private::SealedSerialize for UpdateChatSettingsBody {} /// [`update-chat-settings`](https://dev.twitch.tv/docs/api/reference#update-chat-settings) pub type UpdateChatSettingsResponse = ChatSettings; -impl Request for UpdateChatSettingsRequest { +impl Request for UpdateChatSettingsRequest<'_> { type Response = ChatSettings; const PATH: &'static str = "chat/settings"; @@ -184,7 +180,7 @@ impl Request for UpdateChatSettingsRequest { &[twitch_oauth2::Scope::ModeratorManageChatSettings]; } -impl RequestPatch for UpdateChatSettingsRequest { +impl RequestPatch for UpdateChatSettingsRequest<'_> { type Body = UpdateChatSettingsBody; fn parse_inner_response( diff --git a/src/helix/endpoints/chat/update_user_chat_color.rs b/src/helix/endpoints/chat/update_user_chat_color.rs index 3a02535f2b..6c7545186b 100644 --- a/src/helix/endpoints/chat/update_user_chat_color.rs +++ b/src/helix/endpoints/chat/update_user_chat_color.rs @@ -10,10 +10,10 @@ //! //! ```rust //! use twitch_api::helix::chat::update_user_chat_color; -//! let request = update_user_chat_color::UpdateUserChatColorRequest::builder() -//! .user_id("123") -//! .color(twitch_types::NamedUserColor::Blue) -//! .build(); +//! let request = update_user_chat_color::UpdateUserChatColorRequest::new( +//! "123", +//! twitch_types::NamedUserColor::Blue, +//! ); //! ``` //! //! ## Response: [UpdateUserChatColorResponse] @@ -28,11 +28,10 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = update_user_chat_color::UpdateUserChatColorRequest::builder() -//! .user_id("123") -//! .color(twitch_types::NamedUserColor::Blue) -//! .build(); -//! +//! let request = update_user_chat_color::UpdateUserChatColorRequest::new( +//! "123", +//! twitch_types::NamedUserColor::Blue, +//! ); //! let response: helix::chat::UpdateUserChatColorResponse = client.req_put(request, helix::EmptyBody, &token).await?.data; //! # Ok(()) //! # } @@ -49,21 +48,25 @@ use helix::RequestPut; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct UpdateUserChatColorRequest { +pub struct UpdateUserChatColorRequest<'a> { /// The ID of the user whose chat color you want to update. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub user_id: types::UserId, + #[serde(borrow)] + pub user_id: Cow<'a, types::UserIdRef>, /// The color to use for the user’s name in chat. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - #[serde(borrow = "'static")] - pub color: types::NamedUserColor<'static>, + #[serde(borrow)] + pub color: types::NamedUserColor<'a>, } -impl UpdateUserChatColorRequest { +impl<'a> UpdateUserChatColorRequest<'a> { /// Update the users chat color - pub fn new(user_id: impl Into, color: types::NamedUserColor<'static>) -> Self { + pub fn new( + user_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + color: types::NamedUserColor<'static>, + ) -> Self { Self { - user_id: user_id.into(), + user_id: user_id.to_cow(), color, } } @@ -80,7 +83,7 @@ pub enum UpdateUserChatColorResponse { Success, } -impl Request for UpdateUserChatColorRequest { +impl Request for UpdateUserChatColorRequest<'_> { type Response = UpdateUserChatColorResponse; const PATH: &'static str = "chat/color"; @@ -88,7 +91,7 @@ impl Request for UpdateUserChatColorRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::UserManageChatColor]; } -impl RequestPut for UpdateUserChatColorRequest { +impl RequestPut for UpdateUserChatColorRequest<'_> { type Body = helix::EmptyBody; fn parse_inner_response<'d>( diff --git a/src/helix/endpoints/clips/get_clips.rs b/src/helix/endpoints/clips/get_clips.rs index 3d41d2d55f..9dc29ebfb8 100644 --- a/src/helix/endpoints/clips/get_clips.rs +++ b/src/helix/endpoints/clips/get_clips.rs @@ -5,13 +5,11 @@ //! //! ## Request: [GetClipsRequest] //! -//! To use this endpoint, construct a [`GetClipsRequest`] with the [`GetClipsRequest::builder()`] method. +//! To use this endpoint, construct a [`GetClipsRequest`] with the [`GetClipsRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::clips::get_clips; -//! let request = get_clips::GetClipsRequest::builder() -//! .broadcaster_id(Some("1234".into())) -//! .build(); +//! let request = get_clips::GetClipsRequest::broadcaster_id("1234"); //! ``` //! //! ## Response: [Clip] @@ -26,9 +24,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_clips::GetClipsRequest::builder() -//! .broadcaster_id(Some("1234".into())) -//! .build(); +//! let request = get_clips::GetClipsRequest::broadcaster_id("1234"); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -46,36 +42,42 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetClipsRequest { +pub struct GetClipsRequest<'a> { /// ID of the broadcaster for whom clips are returned. The number of clips returned is determined by the first query-string parameter (default: 20). Results are ordered by view count. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub broadcaster_id: Option, + #[serde(borrow)] + pub broadcaster_id: Option>, /// ID of the game for which clips are returned. The number of clips returned is determined by the first query-string parameter (default: 20). Results are ordered by view count. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub game_id: Option, + #[serde(borrow)] + pub game_id: Option>, // FIXME: add types::ClipId /// ID of the clip being queried. Limit: 100. #[cfg_attr(feature = "typed-builder", builder(default))] - pub id: Vec, + #[serde(borrow)] + pub id: Cow<'a, [&'a types::ClipIdRef]>, // one of above is needed. /// Cursor for forward pagination: tells the server where to start fetching the next set of results, in a multi-page response. This applies only to queries specifying broadcaster_id or game_id. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub after: Option, + pub after: Option>, /// Cursor for backward pagination: tells the server where to start fetching the next set of results, in a multi-page response. This applies only to queries specifying broadcaster_id or game_id. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub before: Option, + #[serde(borrow)] + pub before: Option>, /// Ending date/time for returned clips, in RFC3339 format. (Note that the seconds value is ignored.) If this is specified, started_at also must be specified; otherwise, the time period is ignored. #[cfg_attr(feature = "typed-builder", builder(default))] - pub ended_at: Option, + #[serde(borrow)] + pub ended_at: Option>, /// Maximum number of objects to return. Maximum: 100. Default: 20. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub first: Option, /// Starting date/time for returned clips, in RFC3339 format. (Note that the seconds value is ignored.) If this is specified, ended_at also should be specified; otherwise, the ended_at date/time will be 1 week after the started_at value. #[cfg_attr(feature = "typed-builder", builder(default))] - pub started_at: Option, + #[serde(borrow)] + pub started_at: Option>, } -impl GetClipsRequest { +impl<'a> GetClipsRequest<'a> { /// An empty request /// /// # Notes @@ -85,7 +87,7 @@ impl GetClipsRequest { Self { broadcaster_id: Default::default(), game_id: Default::default(), - id: Default::default(), + id: Cow::Borrowed(&[]), after: Default::default(), before: Default::default(), ended_at: Default::default(), @@ -95,46 +97,50 @@ impl GetClipsRequest { } /// Broadcaster for whom clips are returned. - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: Some(broadcaster_id.into()), + broadcaster_id: Some(broadcaster_id.to_cow()), ..Self::empty() } } /// Game for which clips are returned. - pub fn game_id(game_id: impl Into) -> Self { + pub fn game_id(game_id: impl types::IntoCow<'a, types::CategoryIdRef> + 'a) -> Self { Self { - game_id: Some(game_id.into()), - ..Self::empty() - } - } - - /// ID of clip being queried - pub fn clip_id(clip_id: impl Into) -> Self { - Self { - id: vec![clip_id.into()], + game_id: Some(game_id.to_cow()), ..Self::empty() } } /// IDs of clips being queried - pub fn clip_ids(clip_ids: impl IntoIterator>) -> Self { + pub fn clip_ids(clip_ids: impl Into>) -> Self { Self { - id: clip_ids.into_iter().map(Into::into).collect(), + id: clip_ids.into(), ..Self::empty() } } /// Ending date/time for the returned clips - pub fn started_at(&mut self, started_at: impl Into) -> &mut Self { - self.started_at = Some(started_at.into()); + pub fn started_at( + &mut self, + started_at: impl types::IntoCow<'a, types::TimestampRef> + 'a, + ) -> &mut Self { + self.started_at = Some(started_at.to_cow()); self } /// Ending date/time for the returned clips - pub fn ended_at(&mut self, ended_at: impl Into) -> &mut Self { - self.ended_at = Some(ended_at.into()); + pub fn ended_at( + &mut self, + ended_at: impl types::IntoCow<'a, types::TimestampRef> + 'a, + ) -> &mut Self { + self.ended_at = Some(ended_at.to_cow()); + self + } + + /// Set amount of results returned per page. + pub fn first(mut self, first: usize) -> Self { + self.first = Some(first); self } } @@ -182,7 +188,7 @@ pub struct Clip { pub vod_offset: Option, } -impl Request for GetClipsRequest { +impl Request for GetClipsRequest<'_> { type Response = Vec; const PATH: &'static str = "clips"; @@ -190,17 +196,20 @@ impl Request for GetClipsRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetClipsRequest {} +impl RequestGet for GetClipsRequest<'_> {} -impl helix::Paginated for GetClipsRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor } +impl helix::Paginated for GetClipsRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } #[cfg(test)] #[test] fn test_request() { use helix::*; - let req = GetClipsRequest::clip_id(String::from("AwkwardHelplessSalamanderSwiftRage")); + + let req = GetClipsRequest::clip_ids(vec!["AwkwardHelplessSalamanderSwiftRage".into()]); // From twitch docs let data = br#" diff --git a/src/helix/endpoints/clips/mod.rs b/src/helix/endpoints/clips/mod.rs index b05269dce3..8ae1e59d1c 100644 --- a/src/helix/endpoints/clips/mod.rs +++ b/src/helix/endpoints/clips/mod.rs @@ -10,10 +10,7 @@ //! # let _: &HelixClient = &client; //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let req = GetClipsRequest::builder() -//! .game_id(Some("1234".into())) -//! .first(100) // max 100, 20 if left unspecified -//! .build(); +//! let req = GetClipsRequest::game_id("1234").first(100); // max 100, 20 if left unspecified //! //! println!("{:?}", &client.req_get(req, &token).await?.data.get(0)); //! # Ok(()) @@ -24,6 +21,7 @@ use crate::{ types, }; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; pub mod get_clips; diff --git a/src/helix/endpoints/eventsub/create_eventsub_subscription.rs b/src/helix/endpoints/eventsub/create_eventsub_subscription.rs index 37ccd669b8..f45412e226 100644 --- a/src/helix/endpoints/eventsub/create_eventsub_subscription.rs +++ b/src/helix/endpoints/eventsub/create_eventsub_subscription.rs @@ -53,7 +53,7 @@ pub struct CreateEventSubSubscriptionBody { pub transport: Transport, } -impl helix::HelixRequestBody for CreateEventSubSubscriptionBody { +impl<'a, E: EventSubscription> helix::HelixRequestBody for CreateEventSubSubscriptionBody { fn try_to_body(&self) -> Result { #[derive(PartialEq, Serialize, Debug)] struct IEventSubRequestBody<'a> { @@ -76,8 +76,8 @@ impl helix::HelixRequestBody for CreateEventSubSubscriptio // FIXME: Builder? impl CreateEventSubSubscriptionBody { /// Create a new [`CreateEventSubSubscriptionBody`] - pub fn new(subscription: E, transport: Transport) -> CreateEventSubSubscriptionBody { - CreateEventSubSubscriptionBody { + pub fn new(subscription: E, transport: Transport) -> Self { + Self { subscription, transport, } @@ -194,14 +194,13 @@ fn test_request() { let req: CreateEventSubSubscriptionRequest = CreateEventSubSubscriptionRequest::default(); - let body = CreateEventSubSubscriptionBody::new( - UserUpdateV1::new("1234"), - eventsub::Transport { - method: eventsub::TransportMethod::Webhook, - callback: "example.com".to_string(), - secret: "heyhey13".to_string(), - }, - ); + let sub = UserUpdateV1::new("1234"); + let transport = eventsub::Transport { + method: eventsub::TransportMethod::Webhook, + callback: "example.com".to_string(), + secret: "heyhey13".to_string(), + }; + let body = CreateEventSubSubscriptionBody::new(sub, transport); dbg!(req.create_request(body, "token", "clientid").unwrap()); diff --git a/src/helix/endpoints/eventsub/delete_eventsub_subscription.rs b/src/helix/endpoints/eventsub/delete_eventsub_subscription.rs index 5dec9c4b96..97c8f1729f 100644 --- a/src/helix/endpoints/eventsub/delete_eventsub_subscription.rs +++ b/src/helix/endpoints/eventsub/delete_eventsub_subscription.rs @@ -9,18 +9,21 @@ use helix::RequestDelete; #[derive(PartialEq, Eq, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct DeleteEventSubSubscriptionRequest { +pub struct DeleteEventSubSubscriptionRequest<'a> { /// The subscription ID for the subscription you want to delete. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub id: types::EventSubId, + #[serde(borrow)] + pub id: Cow<'a, types::EventSubIdRef>, } -impl DeleteEventSubSubscriptionRequest { +impl<'a> DeleteEventSubSubscriptionRequest<'a> { /// Delete this eventsub subscription. - pub fn id(id: impl Into) -> Self { Self { id: id.into() } } + pub fn id(id: impl types::IntoCow<'a, types::EventSubIdRef> + 'a) -> Self { + Self { id: id.to_cow() } + } } -impl Request for DeleteEventSubSubscriptionRequest { +impl Request for DeleteEventSubSubscriptionRequest<'_> { type Response = DeleteEventSubSubscription; const PATH: &'static str = "eventsub/subscriptions"; @@ -38,7 +41,7 @@ pub enum DeleteEventSubSubscription { Success, } -impl RequestDelete for DeleteEventSubSubscriptionRequest { +impl RequestDelete for DeleteEventSubSubscriptionRequest<'_> { fn parse_inner_response( request: Option, uri: &http::Uri, diff --git a/src/helix/endpoints/eventsub/get_eventsub_subscriptions.rs b/src/helix/endpoints/eventsub/get_eventsub_subscriptions.rs index 0feb687906..9c5c335032 100644 --- a/src/helix/endpoints/eventsub/get_eventsub_subscriptions.rs +++ b/src/helix/endpoints/eventsub/get_eventsub_subscriptions.rs @@ -10,7 +10,7 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Serialize, Clone, Debug, Default)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetEventSubSubscriptionsRequest { +pub struct GetEventSubSubscriptionsRequest<'a> { /// Include this parameter to filter subscriptions by their status. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub status: Option, @@ -22,18 +22,19 @@ pub struct GetEventSubSubscriptionsRequest { /// The response contains subscriptions where the user ID /// matches a user ID that you specified in the Condition object when you created the subscription. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub user_id: Option, + #[serde(borrow)] + pub user_id: Option>, // FIXME: https://github.com/twitchdev/issues/issues/272 /// Cursor for forward pagination #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub after: Option, + pub after: Option>, // FIXME: https://github.com/twitchdev/issues/issues/271 /// Maximum number of objects to return. Maximum: 100. Default: 20. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub first: Option, } -impl GetEventSubSubscriptionsRequest { +impl GetEventSubSubscriptionsRequest<'_> { /// Get eventsub subscriptions by this status pub fn status(status: impl Into) -> Self { Self { @@ -51,7 +52,7 @@ impl GetEventSubSubscriptionsRequest { } } -impl Request for GetEventSubSubscriptionsRequest { +impl Request for GetEventSubSubscriptionsRequest<'_> { type Response = EventSubSubscriptions; const PATH: &'static str = "eventsub/subscriptions"; @@ -76,7 +77,7 @@ pub struct EventSubSubscriptions { pub subscriptions: Vec, } -impl RequestGet for GetEventSubSubscriptionsRequest { +impl RequestGet for GetEventSubSubscriptionsRequest<'_> { fn parse_inner_response( request: Option, uri: &http::Uri, @@ -122,8 +123,10 @@ impl RequestGet for GetEventSubSubscriptionsRequest { } } -impl helix::Paginated for GetEventSubSubscriptionsRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor } +impl helix::Paginated for GetEventSubSubscriptionsRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } #[cfg(test)] diff --git a/src/helix/endpoints/eventsub/mod.rs b/src/helix/endpoints/eventsub/mod.rs index 30fee3f20e..065da66c58 100644 --- a/src/helix/endpoints/eventsub/mod.rs +++ b/src/helix/endpoints/eventsub/mod.rs @@ -5,6 +5,7 @@ use crate::{ types, }; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; pub mod create_eventsub_subscription; pub mod delete_eventsub_subscription; diff --git a/src/helix/endpoints/games/get_games.rs b/src/helix/endpoints/games/get_games.rs index ccd8d2f66d..e5ed6e484b 100644 --- a/src/helix/endpoints/games/get_games.rs +++ b/src/helix/endpoints/games/get_games.rs @@ -9,7 +9,7 @@ //! //! ```rust //! use twitch_api::helix::games::get_games; -//! let request = get_games::GetGamesRequest::id("4321"); +//! let request = get_games::GetGamesRequest::ids(&["4321".into()][..]); //! ``` //! //! ## Response: [Game](types::TwitchCategory) @@ -18,13 +18,14 @@ //! //! ```rust, no_run //! use twitch_api::helix::{self, games::get_games}; -//! # use twitch_api::client; +//! # use twitch_api::{client, types}; //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_games::GetGamesRequest::id("4321"); +//! let ids: &[&types::CategoryIdRef] = &["4321".into()]; +//! let request = get_games::GetGamesRequest::ids(ids); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -42,44 +43,36 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetGamesRequest { +pub struct GetGamesRequest<'a> { /// Game ID. At most 100 id values can be specified. - #[cfg_attr(feature = "typed-builder", builder(default))] - pub id: Vec, + #[cfg_attr( + feature = "typed-builder", + builder(default_code = "Cow::Borrowed(&[])", setter(into)) + )] + #[serde(borrow)] + pub id: Cow<'a, [&'a types::CategoryIdRef]>, /// Game name. The name must be an exact match. For instance, “Pokemon” will not return a list of Pokemon games; instead, query the specific Pokemon game(s) in which you are interested. At most 100 name values can be specified. - #[cfg_attr(feature = "typed-builder", builder(default))] - pub name: Vec, + #[cfg_attr( + feature = "typed-builder", + builder(default_code = "Cow::Borrowed(&[])", setter(into)) + )] + #[serde(borrow)] + pub name: Cow<'a, [&'a str]>, } -impl GetGamesRequest { - /// Get game with specific exact name match. - pub fn name(name: impl Into) -> Self { - Self { - name: vec![name.into()], - ..Self::empty() - } - } - +impl<'a> GetGamesRequest<'a> { /// Get games with specific exact name match. - pub fn names(names: impl IntoIterator) -> Self { - Self { - name: names.into_iter().collect(), - ..Self::empty() - } - } - - /// Get game with specific exact id match. - pub fn id(id: impl Into) -> Self { + pub fn names(names: impl Into>) -> Self { Self { - id: vec![id.into()], + name: names.into(), ..Self::empty() } } /// Get games with specific exact id match. - pub fn ids(ids: impl IntoIterator>) -> Self { + pub fn ids(ids: impl Into>) -> Self { Self { - id: ids.into_iter().map(Into::into).collect(), + id: ids.into(), ..Self::empty() } } @@ -87,8 +80,8 @@ impl GetGamesRequest { /// Returns an empty [`GetGamesRequest`] fn empty() -> Self { Self { - id: Default::default(), - name: Default::default(), + id: Cow::Borrowed(&[]), + name: Cow::Borrowed(&[]), } } } @@ -98,7 +91,7 @@ impl GetGamesRequest { /// [`get-games`](https://dev.twitch.tv/docs/api/reference#get-games) pub type Game = types::TwitchCategory; -impl Request for GetGamesRequest { +impl Request for GetGamesRequest<'_> { type Response = Vec; const PATH: &'static str = "games"; @@ -106,13 +99,13 @@ impl Request for GetGamesRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetGamesRequest {} +impl RequestGet for GetGamesRequest<'_> {} #[cfg(test)] #[test] fn test_request() { use helix::*; - let req = GetGamesRequest::id("493057"); + let req = GetGamesRequest::ids(vec!["493057".into()]); // From twitch docs let data = br#" diff --git a/src/helix/endpoints/games/get_top_games.rs b/src/helix/endpoints/games/get_top_games.rs index 6d2feb5627..9c4b4ee1e9 100644 --- a/src/helix/endpoints/games/get_top_games.rs +++ b/src/helix/endpoints/games/get_top_games.rs @@ -45,19 +45,20 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug, Default)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetTopGamesRequest { +pub struct GetTopGamesRequest<'a> { /// Cursor for forward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub after: Option, + pub after: Option>, /// Cursor for backward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub before: Option, + #[serde(borrow)] + pub before: Option>, /// Maximum number of objects to return. Maximum: 100. Default: 20. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub first: Option, } -impl GetTopGamesRequest { +impl GetTopGamesRequest<'_> { /// Set amount of results returned per page. pub fn first(mut self, first: usize) -> Self { self.first = Some(first); @@ -70,7 +71,7 @@ impl GetTopGamesRequest { /// [`get-top-games`](https://dev.twitch.tv/docs/api/reference#get-top-games) pub type Game = types::TwitchCategory; -impl Request for GetTopGamesRequest { +impl Request for GetTopGamesRequest<'_> { type Response = Vec; const PATH: &'static str = "games/top"; @@ -78,10 +79,12 @@ impl Request for GetTopGamesRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetTopGamesRequest {} +impl RequestGet for GetTopGamesRequest<'_> {} -impl helix::Paginated for GetTopGamesRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor } +impl helix::Paginated for GetTopGamesRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } #[cfg(test)] diff --git a/src/helix/endpoints/games/mod.rs b/src/helix/endpoints/games/mod.rs index a4a675d390..8eb8900916 100644 --- a/src/helix/endpoints/games/mod.rs +++ b/src/helix/endpoints/games/mod.rs @@ -3,8 +3,8 @@ use crate::{ helix::{self, Request}, types, }; - use serde::{Deserialize, Serialize}; +use std::borrow::Cow; pub mod get_games; pub mod get_top_games; diff --git a/src/helix/endpoints/goals/get_creator_goals.rs b/src/helix/endpoints/goals/get_creator_goals.rs index fc8587a5dc..baa0e2981c 100644 --- a/src/helix/endpoints/goals/get_creator_goals.rs +++ b/src/helix/endpoints/goals/get_creator_goals.rs @@ -5,13 +5,11 @@ //! //! ## Request: [GetCreatorGoalsRequest] //! -//! To use this endpoint, construct a [`GetCreatorGoalsRequest`] with the [`GetCreatorGoalsRequest::builder()`] method. +//! To use this endpoint, construct a [`GetCreatorGoalsRequest`] with the [`GetCreatorGoalsRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::goals::get_creator_goals; -//! let request = get_creator_goals::GetCreatorGoalsRequest::builder() -//! .broadcaster_id("4321".to_string()) -//! .build(); +//! let request = get_creator_goals::GetCreatorGoalsRequest::broadcaster_id("4321"); //! ``` //! //! ## Response: [CreatorGoal](types::TwitchCategory) @@ -26,9 +24,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_creator_goals::GetCreatorGoalsRequest::builder() -//! .broadcaster_id("4321".to_string()) -//! .build(); +//! let request = get_creator_goals::GetCreatorGoalsRequest::broadcaster_id("4321"); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -46,10 +42,11 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetCreatorGoalsRequest { +pub struct GetCreatorGoalsRequest<'a> { /// Must match the User ID in the Bearer token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// Cursor for forward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] pub cursor: Option, @@ -58,14 +55,15 @@ pub struct GetCreatorGoalsRequest { pub first: Option, /// Retreive a single event by event ID #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub id: Option, + #[serde(borrow)] + pub id: Option>, } -impl GetCreatorGoalsRequest { +impl<'a> GetCreatorGoalsRequest<'a> { /// Gets the broadcaster’s list of active goals. - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), cursor: Default::default(), first: Default::default(), id: Default::default(), @@ -107,7 +105,7 @@ pub struct CreatorGoal { pub created_at: types::Timestamp, } -impl Request for GetCreatorGoalsRequest { +impl Request for GetCreatorGoalsRequest<'_> { type Response = Vec; const PATH: &'static str = "goals"; @@ -115,7 +113,7 @@ impl Request for GetCreatorGoalsRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelReadGoals]; } -impl RequestGet for GetCreatorGoalsRequest {} +impl RequestGet for GetCreatorGoalsRequest<'_> {} #[cfg(test)] #[test] diff --git a/src/helix/endpoints/goals/mod.rs b/src/helix/endpoints/goals/mod.rs index d3afd17579..768067b13d 100644 --- a/src/helix/endpoints/goals/mod.rs +++ b/src/helix/endpoints/goals/mod.rs @@ -5,8 +5,8 @@ use crate::{ helix::{self, Request}, types, }; - use serde::{Deserialize, Serialize}; +use std::borrow::Cow; pub mod get_creator_goals; diff --git a/src/helix/endpoints/hypetrain/get_hypetrain_events.rs b/src/helix/endpoints/hypetrain/get_hypetrain_events.rs index 7595cc7655..823b412dea 100644 --- a/src/helix/endpoints/hypetrain/get_hypetrain_events.rs +++ b/src/helix/endpoints/hypetrain/get_hypetrain_events.rs @@ -6,13 +6,11 @@ //! //! ## Request: [GetHypeTrainEventsRequest] //! -//! To use this endpoint, construct a [`GetHypeTrainEventsRequest`] with the [`GetHypeTrainEventsRequest::builder()`] method. +//! To use this endpoint, construct a [`GetHypeTrainEventsRequest`] with the [`GetHypeTrainEventsRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::hypetrain::get_hypetrain_events; -//! let request = get_hypetrain_events::GetHypeTrainEventsRequest::builder() -//! .broadcaster_id("4321".to_string()) -//! .build(); +//! let request = get_hypetrain_events::GetHypeTrainEventsRequest::broadcaster_id("4321"); //! ``` //! //! ## Response: [HypeTrainEvent](types::TwitchCategory) @@ -27,9 +25,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_hypetrain_events::GetHypeTrainEventsRequest::builder() -//! .broadcaster_id("4321".to_string()) -//! .build(); +//! let request = get_hypetrain_events::GetHypeTrainEventsRequest::broadcaster_id("4321"); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -47,10 +43,11 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetHypeTrainEventsRequest { +pub struct GetHypeTrainEventsRequest<'a> { /// Must match the User ID in the Bearer token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// Cursor for forward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] pub cursor: Option, @@ -63,14 +60,15 @@ pub struct GetHypeTrainEventsRequest { note = "this does nothing, see https://discuss.dev.twitch.tv/t/get-hype-train-events-api-endpoint-id-query-parameter-deprecation/37613" )] #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub id: Option, + #[serde(borrow)] + pub id: Option>, } -impl GetHypeTrainEventsRequest { +impl<'a> GetHypeTrainEventsRequest<'a> { /// Get hypetrain evens - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), cursor: Default::default(), first: Default::default(), id: Default::default(), @@ -134,7 +132,7 @@ pub struct HypeTrainEventData { pub id: types::HypeTrainId, } -impl Request for GetHypeTrainEventsRequest { +impl Request for GetHypeTrainEventsRequest<'_> { type Response = Vec; const PATH: &'static str = "hypetrain/events"; @@ -142,9 +140,9 @@ impl Request for GetHypeTrainEventsRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetHypeTrainEventsRequest {} +impl RequestGet for GetHypeTrainEventsRequest<'_> {} -impl helix::Paginated for GetHypeTrainEventsRequest { +impl helix::Paginated for GetHypeTrainEventsRequest<'_> { fn set_pagination(&mut self, cursor: Option) { self.cursor = cursor } } diff --git a/src/helix/endpoints/hypetrain/mod.rs b/src/helix/endpoints/hypetrain/mod.rs index ac9510f343..9dd8d971d6 100644 --- a/src/helix/endpoints/hypetrain/mod.rs +++ b/src/helix/endpoints/hypetrain/mod.rs @@ -5,8 +5,8 @@ use crate::{ helix::{self, Request}, types, }; - use serde::{Deserialize, Serialize}; +use std::borrow::Cow; pub mod get_hypetrain_events; diff --git a/src/helix/endpoints/moderation/add_blocked_term.rs b/src/helix/endpoints/moderation/add_blocked_term.rs index e2a1314710..df4a97cb28 100644 --- a/src/helix/endpoints/moderation/add_blocked_term.rs +++ b/src/helix/endpoints/moderation/add_blocked_term.rs @@ -5,14 +5,11 @@ //! //! ## Request: [AddBlockedTermRequest] //! -//! To use this endpoint, construct a [`AddBlockedTermRequest`] with the [`AddBlockedTermRequest::builder()`] method. +//! To use this endpoint, construct a [`AddBlockedTermRequest`] with the [`AddBlockedTermRequest::new()`] method. //! //! ```rust //! use twitch_api::helix::moderation::add_blocked_term; -//! let request = add_blocked_term::AddBlockedTermRequest::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .build(); +//! let request = add_blocked_term::AddBlockedTermRequest::new("1234", "5678"); //! ``` //! //! ## Body: [AddBlockedTermBody] @@ -21,7 +18,7 @@ //! //! ``` //! # use twitch_api::helix::moderation::add_blocked_term; -//! let body = add_blocked_term::AddBlockedTermBody::new("A phrase I'm not fond of".to_string()); +//! let body = add_blocked_term::AddBlockedTermBody::new("A phrase I'm not fond of"); //! ``` //! //! ## Response: [BlockedTerm] @@ -38,11 +35,8 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = add_blocked_term::AddBlockedTermRequest::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .build(); -//! let body = add_blocked_term::AddBlockedTermBody::new("A phrase I'm not fond of".to_string()); +//! let request = add_blocked_term::AddBlockedTermRequest::new("1234", "5678"); +//! let body = add_blocked_term::AddBlockedTermBody::new("A phrase I'm not fond of"); //! let response: &helix::moderation::BlockedTerm = client.req_post(request, body, &token).await?.data.first().unwrap(); //! # Ok(()) //! # } @@ -59,26 +53,28 @@ use helix::RequestPost; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct AddBlockedTermRequest { +pub struct AddBlockedTermRequest<'a> { /// The ID of the broadcaster that owns the list of blocked terms. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// The ID of a user that has permission to moderate the broadcaster’s chat room. This ID must match the user ID associated with the user OAuth token. /// /// If the broadcaster wants to add the blocked term (instead of having the moderator do it), set this parameter to the broadcaster’s ID, too. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub moderator_id: types::UserId, + #[serde(borrow)] + pub moderator_id: Cow<'a, types::UserIdRef>, } -impl AddBlockedTermRequest { +impl<'a> AddBlockedTermRequest<'a> { /// Where to add blocked term pub fn new( - broadcaster_id: impl Into, - moderator_id: impl Into, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + moderator_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - moderator_id: moderator_id.into(), + broadcaster_id: broadcaster_id.to_cow(), + moderator_id: moderator_id.to_cow(), } } } @@ -89,26 +85,27 @@ impl AddBlockedTermRequest { #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct AddBlockedTermBody { +pub struct AddBlockedTermBody<'a> { ///The word or phrase to block from being used in the broadcaster’s chat room. /// /// The term must contain a minimum of 2 characters and may contain up to a maximum of 500 characters. /// Terms can use a wildcard character (*). The wildcard character must appear at the beginning or end of a word, or set of characters. For example, *foo or foo*. - pub text: String, + #[serde(borrow)] + pub text: Cow<'a, str>, } -impl AddBlockedTermBody { +impl<'a> AddBlockedTermBody<'a> { /// Create a new [`AddBlockedTermBody`] - pub fn new(text: String) -> Self { Self { text } } + pub fn new(text: impl Into>) -> Self { Self { text: text.into() } } } -impl helix::private::SealedSerialize for AddBlockedTermBody {} +impl helix::private::SealedSerialize for AddBlockedTermBody<'_> {} -impl helix::HelixRequestBody for Vec { +impl<'a> helix::HelixRequestBody for [AddBlockedTermBody<'a>] { fn try_to_body(&self) -> Result { #[derive(Serialize)] struct InnerBody<'a> { - data: &'a Vec, + data: &'a [AddBlockedTermBody<'a>], } serde_json::to_vec(&InnerBody { data: self }) @@ -122,7 +119,7 @@ impl helix::HelixRequestBody for Vec { /// [`add-blocked-term`](https://dev.twitch.tv/docs/api/reference#add-blocked-term) pub type AddBlockedTermResponse = BlockedTerm; -impl Request for AddBlockedTermRequest { +impl Request for AddBlockedTermRequest<'_> { // FIXME: this is a single entry type Response = Vec; @@ -132,8 +129,8 @@ impl Request for AddBlockedTermRequest { &[twitch_oauth2::Scope::ModeratorManageBlockedTerms]; } -impl RequestPost for AddBlockedTermRequest { - type Body = AddBlockedTermBody; +impl<'a> RequestPost for AddBlockedTermRequest<'a> { + type Body = AddBlockedTermBody<'a>; } #[cfg(test)] @@ -142,7 +139,7 @@ fn test_request() { use helix::*; let req = AddBlockedTermRequest::new("1234", "5678"); - let body = AddBlockedTermBody::new("A phrase I'm not fond of".to_string()); + let body = AddBlockedTermBody::new("A phrase I'm not fond of"); dbg!(req.create_request(body, "token", "clientid").unwrap()); diff --git a/src/helix/endpoints/moderation/add_channel_moderator.rs b/src/helix/endpoints/moderation/add_channel_moderator.rs index 73563c42fc..136a8d9106 100644 --- a/src/helix/endpoints/moderation/add_channel_moderator.rs +++ b/src/helix/endpoints/moderation/add_channel_moderator.rs @@ -9,10 +9,7 @@ //! //! ```rust //! use twitch_api::helix::moderation::add_channel_moderator; -//! let request = add_channel_moderator::AddChannelModeratorRequest::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .build(); +//! let request = add_channel_moderator::AddChannelModeratorRequest::new("1234", "5678"); //! ``` //! //! ## Response: [AddChannelModeratorResponse] @@ -28,10 +25,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = add_channel_moderator::AddChannelModeratorRequest::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .build(); +//! let request = add_channel_moderator::AddChannelModeratorRequest::new("1234", "5678"); //! let response: helix::moderation::AddChannelModeratorResponse = client.req_post(request, helix::EmptyBody, &token).await?.data; //! # Ok(()) //! # } @@ -49,24 +43,26 @@ use helix::RequestPost; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct AddChannelModeratorRequest { +pub struct AddChannelModeratorRequest<'a> { /// The ID of the broadcaster that owns the chat room. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// The ID of the user to add as a moderator in the broadcaster’s chat room. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub moderator_id: types::UserId, + #[serde(borrow)] + pub moderator_id: Cow<'a, types::UserIdRef>, } -impl AddChannelModeratorRequest { +impl<'a> AddChannelModeratorRequest<'a> { /// Add moderator on channel pub fn new( - broadcaster_id: impl Into, - moderator_id: impl Into, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + moderator_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - moderator_id: moderator_id.into(), + broadcaster_id: broadcaster_id.to_cow(), + moderator_id: moderator_id.to_cow(), } } } @@ -82,7 +78,7 @@ pub enum AddChannelModeratorResponse { Success, } -impl Request for AddChannelModeratorRequest { +impl Request for AddChannelModeratorRequest<'_> { type Response = AddChannelModeratorResponse; const PATH: &'static str = "moderation/moderators"; @@ -90,7 +86,7 @@ impl Request for AddChannelModeratorRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelManageModerators]; } -impl RequestPost for AddChannelModeratorRequest { +impl RequestPost for AddChannelModeratorRequest<'_> { type Body = helix::EmptyBody; fn parse_inner_response<'d>( diff --git a/src/helix/endpoints/moderation/ban_user.rs b/src/helix/endpoints/moderation/ban_user.rs index 883ee5b28f..1cff7333d6 100644 --- a/src/helix/endpoints/moderation/ban_user.rs +++ b/src/helix/endpoints/moderation/ban_user.rs @@ -5,14 +5,11 @@ //! //! ## Request: [BanUserRequest] //! -//! To use this endpoint, construct a [`BanUserRequest`] with the [`BanUserRequest::builder()`] method. +//! To use this endpoint, construct a [`BanUserRequest`] with the [`BanUserRequest::new()`] method. //! //! ```rust //! use twitch_api::helix::moderation::ban_user; -//! let request = ban_user::BanUserRequest::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .build(); +//! let request = ban_user::BanUserRequest::new("1234", "5678"); //! ``` //! //! ## Body: [BanUserBody] @@ -21,7 +18,7 @@ //! //! ``` //! # use twitch_api::helix::moderation::ban_user; -//! let body = ban_user::BanUserBody::new("9876", "no reason".to_string(), 120); +//! let body = ban_user::BanUserBody::new("9876", "no reason", 120); //! ``` //! //! ## Response: [BanUser] @@ -38,11 +35,8 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = ban_user::BanUserRequest::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .build(); -//! let body = ban_user::BanUserBody::new("9876", "no reason".to_string(), 120); +//! let request = ban_user::BanUserRequest::new("1234", "5678"); +//! let body = ban_user::BanUserBody::new("9876", "no reason", 120); //! let response: ban_user::BanUser = client.req_post(request, body, &token).await?.data; //! # Ok(()) //! # } @@ -59,28 +53,30 @@ use helix::RequestPost; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct BanUserRequest { +pub struct BanUserRequest<'a> { /// The ID of the broadcaster whose chat room the user is being banned from. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// The ID of a user that has permission to moderate the broadcaster’s chat room. /// This ID must match the user ID associated with the user OAuth token. /// /// If the broadcaster wants to ban the user (instead of having the moderator do it), /// set this parameter to the broadcaster’s ID, too. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub moderator_id: types::UserId, + #[serde(borrow)] + pub moderator_id: Cow<'a, types::UserIdRef>, } -impl BanUserRequest { +impl<'a> BanUserRequest<'a> { /// Ban a user on this channel pub fn new( - broadcaster_id: impl Into, - moderator_id: impl Into, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + moderator_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - moderator_id: moderator_id.into(), + broadcaster_id: broadcaster_id.to_cow(), + moderator_id: moderator_id.to_cow(), } } } @@ -91,7 +87,7 @@ impl BanUserRequest { #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct BanUserBody { +pub struct BanUserBody<'a> { /// Duration of the (optional) timeout in seconds. /// /// To ban a user indefinitely, don’t include this field. @@ -104,32 +100,34 @@ pub struct BanUserBody { pub duration: Option, /// The reason the user is being banned or put in a timeout. The text is user defined and limited to a maximum of 500 characters. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub reason: String, + #[serde(borrow)] + pub reason: Cow<'a, str>, /// The ID of the user to ban or put in a timeout. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub user_id: types::UserId, + #[serde(borrow)] + pub user_id: Cow<'a, types::UserIdRef>, } -impl BanUserBody { +impl<'a> BanUserBody<'a> { /// Create a new [`BanUserBody`] pub fn new( - user_id: impl Into, - reason: String, + user_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + reason: impl Into>, duration: impl Into>, ) -> Self { Self { duration: duration.into(), - reason, - user_id: user_id.into(), + reason: reason.into(), + user_id: user_id.to_cow(), } } } -impl helix::HelixRequestBody for BanUserBody { +impl helix::HelixRequestBody for BanUserBody<'_> { fn try_to_body(&self) -> Result { #[derive(Serialize)] struct InnerBody<'a> { - data: &'a BanUserBody, + data: &'a BanUserBody<'a>, } serde_json::to_vec(&InnerBody { data: self }) .map_err(Into::into) @@ -156,7 +154,7 @@ pub struct BanUser { pub user_id: types::UserId, } -impl Request for BanUserRequest { +impl Request for BanUserRequest<'_> { type Response = BanUser; const PATH: &'static str = "moderation/bans"; @@ -165,8 +163,8 @@ impl Request for BanUserRequest { &[twitch_oauth2::Scope::ModeratorManageBannedUsers]; } -impl RequestPost for BanUserRequest { - type Body = BanUserBody; +impl<'a> RequestPost for BanUserRequest<'a> { + type Body = BanUserBody<'a>; fn parse_inner_response( request: Option, @@ -212,7 +210,7 @@ fn test_request() { use helix::*; let req = BanUserRequest::new("1234", "5678"); - let body = BanUserBody::new("9876", "no reason".to_string(), 300); + let body = BanUserBody::new("9876", "no reason", 300); dbg!(req.create_request(body, "token", "clientid").unwrap()); @@ -249,7 +247,7 @@ fn test_request_error() { use helix::*; let req = BanUserRequest::new("1234", "5678"); - let body = BanUserBody::new("9876", "no reason".to_string(), 300); + let body = BanUserBody::new("9876", "no reason", 300); dbg!(req.create_request(body, "token", "clientid").unwrap()); diff --git a/src/helix/endpoints/moderation/check_automod_status.rs b/src/helix/endpoints/moderation/check_automod_status.rs index 50ea62d9d0..483eeaf90f 100644 --- a/src/helix/endpoints/moderation/check_automod_status.rs +++ b/src/helix/endpoints/moderation/check_automod_status.rs @@ -6,13 +6,11 @@ //! //! ## Request: [CheckAutoModStatusRequest] //! -//! To use this endpoint, construct a [`CheckAutoModStatusRequest`] with the [`CheckAutoModStatusRequest::builder()`] method. +//! To use this endpoint, construct a [`CheckAutoModStatusRequest`] with the [`CheckAutoModStatusRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::moderation::check_automod_status; -//! let request = check_automod_status::CheckAutoModStatusRequest::builder() -//! .broadcaster_id("1234") -//! .build(); +//! let request = check_automod_status::CheckAutoModStatusRequest::broadcaster_id("1234"); //! ``` //! //! ## Body: [CheckAutoModStatusBody] @@ -21,10 +19,8 @@ //! //! ``` //! # use twitch_api::helix::moderation::check_automod_status; -//! let body = check_automod_status::CheckAutoModStatusBody::builder() -//! .msg_id("test1") -//! .msg_text("automod please approve this!") -//! .build(); +//! let body = +//! check_automod_status::CheckAutoModStatusBody::new("test1", "automod please approve this!"); //! ``` //! //! ## Response: [CheckAutoModStatus] @@ -41,14 +37,10 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = check_automod_status::CheckAutoModStatusRequest::builder() -//! .broadcaster_id("1234") -//! .build(); -//! let body = vec![check_automod_status::CheckAutoModStatusBody::builder() -//! .msg_id("test1") -//! .msg_text("automod please approve this!") -//! .build()]; -//! let response: Vec = client.req_post(request, body, &token).await?.data; +//! let request = check_automod_status::CheckAutoModStatusRequest::broadcaster_id("1234"); +//! let body = +//! check_automod_status::CheckAutoModStatusBody::new("test1", "automod please approve this!"); +//! let response: Vec = client.req_post(request, &[&body].as_slice(), &token).await?.data; //! # Ok(()) //! # } //! ``` @@ -58,23 +50,25 @@ use super::*; use helix::RequestPost; + /// Query Parameters for [Check AutoMod Status](super::check_automod_status) /// /// [`check-automod-status`](https://dev.twitch.tv/docs/api/reference#check-automod-status) #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct CheckAutoModStatusRequest { +pub struct CheckAutoModStatusRequest<'a> { /// Must match the User ID in the Bearer token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, } -impl CheckAutoModStatusRequest { +impl<'a> CheckAutoModStatusRequest<'a> { /// Check automod status in this broadcasters channel. - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), } } } @@ -85,39 +79,44 @@ impl CheckAutoModStatusRequest { #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct CheckAutoModStatusBody { +pub struct CheckAutoModStatusBody<'a> { /// Developer-generated identifier for mapping messages to results. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub msg_id: types::MsgId, + #[serde(borrow)] + pub msg_id: Cow<'a, types::MsgIdRef>, /// Message text. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub msg_text: String, + #[serde(borrow)] + pub msg_text: Cow<'a, str>, /// User ID of the sender. #[deprecated(since = "0.7.0", note = "user_id in automod check is no longer read")] #[cfg_attr( feature = "typed-builder", builder(setter(into, strip_option), default) )] - #[serde(skip_serializing_if = "Option::is_none")] - pub user_id: Option, + #[serde(skip_serializing_if = "Option::is_none", borrow)] + pub user_id: Option>, } -impl CheckAutoModStatusBody { +impl<'a> CheckAutoModStatusBody<'a> { /// Create a new [`CheckAutoModStatusBody`] - pub fn new(msg_id: impl Into, msg_text: String) -> Self { + pub fn new( + msg_id: impl types::IntoCow<'a, types::MsgIdRef> + 'a, + msg_text: impl Into>, + ) -> Self { Self { - msg_id: msg_id.into(), - msg_text, + msg_id: msg_id.to_cow(), + msg_text: msg_text.into(), user_id: None, } } } -impl helix::HelixRequestBody for Vec { +impl<'a> helix::HelixRequestBody for [CheckAutoModStatusBody<'a>] { fn try_to_body(&self) -> Result { #[derive(Serialize)] struct InnerBody<'a> { - data: &'a Vec, + data: &'a [CheckAutoModStatusBody<'a>], } serde_json::to_vec(&InnerBody { data: self }) @@ -139,7 +138,7 @@ pub struct CheckAutoModStatus { pub is_permitted: bool, } -impl Request for CheckAutoModStatusRequest { +impl Request for CheckAutoModStatusRequest<'_> { type Response = Vec; const PATH: &'static str = "moderation/enforcements/status"; @@ -147,22 +146,28 @@ impl Request for CheckAutoModStatusRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ModerationRead]; } -impl RequestPost for CheckAutoModStatusRequest { - type Body = Vec; +impl<'a> RequestPost for CheckAutoModStatusRequest<'a> { + type Body = &'a [&'a CheckAutoModStatusBody<'a>]; } +impl<'a> helix::private::SealedSerialize for &'a [&'a CheckAutoModStatusBody<'a>] {} + #[cfg(test)] #[test] fn test_request() { use helix::*; let req = CheckAutoModStatusRequest::broadcaster_id("198704263"); - let body = vec![ - CheckAutoModStatusBody::new("123", "hello world".to_string()), - CheckAutoModStatusBody::new("393", "automoded word".to_string()), - ]; - - dbg!(req.create_request(body, "token", "clientid").unwrap()); + dbg!(req + .create_request( + &[ + &CheckAutoModStatusBody::new("123", "hello world"), + &CheckAutoModStatusBody::new("393", "automoded word"), + ], + "token", + "clientid" + ) + .unwrap()); // From twitch docs let data = br#" diff --git a/src/helix/endpoints/moderation/delete_chat_messages.rs b/src/helix/endpoints/moderation/delete_chat_messages.rs index e1cb573fbd..88c684e535 100644 --- a/src/helix/endpoints/moderation/delete_chat_messages.rs +++ b/src/helix/endpoints/moderation/delete_chat_messages.rs @@ -5,15 +5,12 @@ //! //! ## Request: [DeleteChatMessagesRequest] //! -//! To use this endpoint, construct a [`DeleteChatMessagesRequest`] with the [`DeleteChatMessagesRequest::builder()`] method. +//! To use this endpoint, construct a [`DeleteChatMessagesRequest`] with the [`DeleteChatMessagesRequest::new()`] method. //! //! ```rust //! use twitch_api::helix::moderation::delete_chat_messages; -//! let request = delete_chat_messages::DeleteChatMessagesRequest::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .message_id(Some("abc-123-def".into())) -//! .build(); +//! let request = delete_chat_messages::DeleteChatMessagesRequest::new("1234", "5678") +//! .message_id("abc-123-def"); //! ``` //! //! ## Response: [DeleteChatMessagesResponse] @@ -28,11 +25,8 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = delete_chat_messages::DeleteChatMessagesRequest::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .message_id(Some("abc-123-def".into())) -//! .build(); +//! let request = delete_chat_messages::DeleteChatMessagesRequest::new("1234", "5678") +//! .message_id("abc-123-def"); //! let response: delete_chat_messages::DeleteChatMessagesResponse = client.req_delete(request, &token).await?.data; //! # Ok(()) //! # } @@ -49,15 +43,17 @@ use helix::RequestDelete; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct DeleteChatMessagesRequest { +pub struct DeleteChatMessagesRequest<'a> { /// The ID of the broadcaster that owns the chat room to remove messages from. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// The ID of a user that has permission to moderate the broadcaster’s chat room. /// /// This ID must match the user ID in the OAuth token. If the broadcaster wants to remove messages themselves, set this parameter to the broadcaster’s ID, too. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub moderator_id: types::UserId, + #[serde(borrow)] + pub moderator_id: Cow<'a, types::UserIdRef>, /// The ID of the message to remove. /// /// The id tag in the PRIVMSG contains the message’s ID (see [PRIVMSG Tags](https://dev.twitch.tv/docs/irc/tags#privmsg-tags)). @@ -70,25 +66,26 @@ pub struct DeleteChatMessagesRequest { /// /// If not specified, the request removes all messages in the broadcaster’s chat room. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub message_id: Option, + #[serde(borrow)] + pub message_id: Option>, } -impl DeleteChatMessagesRequest { +impl<'a> DeleteChatMessagesRequest<'a> { /// Remove chat message(s) pub fn new( - broadcaster_id: impl Into, - moderator_id: impl Into, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + moderator_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - moderator_id: moderator_id.into(), + broadcaster_id: broadcaster_id.to_cow(), + moderator_id: moderator_id.to_cow(), message_id: None, } } /// A specific message to remove - pub fn message_id(mut self, message_id: impl Into) -> Self { - self.message_id = Some(message_id.into()); + pub fn message_id(mut self, message_id: impl types::IntoCow<'a, types::MsgIdRef> + 'a) -> Self { + self.message_id = Some(message_id.to_cow()); self } } @@ -103,7 +100,7 @@ pub enum DeleteChatMessagesResponse { Success, } -impl Request for DeleteChatMessagesRequest { +impl Request for DeleteChatMessagesRequest<'_> { type Response = DeleteChatMessagesResponse; #[cfg(feature = "twitch_oauth2")] @@ -114,7 +111,7 @@ impl Request for DeleteChatMessagesRequest { &[twitch_oauth2::Scope::ModeratorManageChatMessages]; } -impl RequestDelete for DeleteChatMessagesRequest { +impl RequestDelete for DeleteChatMessagesRequest<'_> { fn parse_inner_response( request: Option, uri: &http::Uri, diff --git a/src/helix/endpoints/moderation/get_banned_users.rs b/src/helix/endpoints/moderation/get_banned_users.rs index 4f699bfd71..e9aecc0ef8 100644 --- a/src/helix/endpoints/moderation/get_banned_users.rs +++ b/src/helix/endpoints/moderation/get_banned_users.rs @@ -5,13 +5,11 @@ //! //! ## Request: [GetBannedUsersRequest] //! -//! To use this endpoint, construct a [`GetBannedUsersRequest`] with the [`GetBannedUsersRequest::builder()`] method. +//! To use this endpoint, construct a [`GetBannedUsersRequest`] with the [`GetBannedUsersRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::moderation::get_banned_users; -//! let request = get_banned_users::GetBannedUsersRequest::builder() -//! .broadcaster_id("1234") -//! .build(); +//! let request = get_banned_users::GetBannedUsersRequest::broadcaster_id("1234"); //! ``` //! //! ## Response: [BannedUser] @@ -26,9 +24,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_banned_users::GetBannedUsersRequest::builder() -//! .broadcaster_id("1234") -//! .build(); +//! let request = get_banned_users::GetBannedUsersRequest::broadcaster_id("1234"); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -46,47 +42,44 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetBannedUsersRequest { +pub struct GetBannedUsersRequest<'a> { /// Must match the User ID in the Bearer token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// Filters the results and only returns a status object for users who are banned in this channel and have a matching user_id. /// Format: Repeated Query Parameter, eg. /moderation/banned?broadcaster_id=1&user_id=2&user_id=3 /// Maximum: 100 #[cfg_attr(feature = "typed-builder", builder(default))] - pub user_id: Vec, + #[serde(borrow)] + pub user_id: Cow<'a, [&'a types::UserIdRef]>, /// Cursor for forward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub after: Option, + pub after: Option>, /// Cursor for backward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub before: Option, + #[serde(borrow)] + pub before: Option>, /// Number of values to be returned per page. Limit: 100. Default: 20. #[cfg_attr(feature = "typed-builder", builder(setter(into), default))] pub first: Option, } -impl GetBannedUsersRequest { +impl<'a> GetBannedUsersRequest<'a> { /// Get banned users in a broadcasters channel. - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - user_id: Default::default(), + broadcaster_id: broadcaster_id.to_cow(), + user_id: Cow::Borrowed(&[]), after: Default::default(), before: Default::default(), first: Default::default(), } } - /// Check if supplied user is banned. - pub fn user(mut self, user_id: impl Into) -> Self { - self.user_id = vec![user_id.into()]; - self - } - /// Check if supplied users are banned. - pub fn users(mut self, user_ids: impl IntoIterator>) -> Self { - self.user_id = user_ids.into_iter().map(Into::into).collect(); + pub fn users(mut self, user_ids: impl Into>) -> Self { + self.user_id = user_ids.into(); self } @@ -124,7 +117,7 @@ pub struct BannedUser { pub moderator_name: types::DisplayName, } -impl Request for GetBannedUsersRequest { +impl Request for GetBannedUsersRequest<'_> { type Response = Vec; const PATH: &'static str = "moderation/banned"; @@ -132,10 +125,12 @@ impl Request for GetBannedUsersRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ModerationRead]; } -impl RequestGet for GetBannedUsersRequest {} +impl RequestGet for GetBannedUsersRequest<'_> {} -impl helix::Paginated for GetBannedUsersRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor } +impl helix::Paginated for GetBannedUsersRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } #[cfg(test)] diff --git a/src/helix/endpoints/moderation/get_blocked_terms.rs b/src/helix/endpoints/moderation/get_blocked_terms.rs index 3978212edc..3454a38164 100644 --- a/src/helix/endpoints/moderation/get_blocked_terms.rs +++ b/src/helix/endpoints/moderation/get_blocked_terms.rs @@ -5,14 +5,11 @@ //! //! ## Request: [GetBlockedTerms] //! -//! To use this endpoint, construct a [`GetBlockedTerms`] with the [`GetBlockedTerms::builder()`] method. +//! To use this endpoint, construct a [`GetBlockedTerms`] with the [`GetBlockedTerms::new()`] method. //! //! ```rust //! use twitch_api::helix::moderation::get_blocked_terms; -//! let request = get_blocked_terms::GetBlockedTerms::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .build(); +//! let request = get_blocked_terms::GetBlockedTerms::new("1234", "5678"); //! ``` //! //! ## Response: [BlockedTerm] @@ -27,10 +24,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_blocked_terms::GetBlockedTerms::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .build(); +//! let request = get_blocked_terms::GetBlockedTerms::new("1234", "5678"); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -48,31 +42,33 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetBlockedTerms { +pub struct GetBlockedTerms<'a> { /// The ID of the broadcaster whose blocked terms you’re getting. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// The ID of a user that has permission to moderate the broadcaster’s chat room. This ID must match the user ID associated with the user OAuth token. /// If the broadcaster wants to get their own block terms (instead of having the moderator do it), set this parameter to the broadcaster’s ID, too. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub moderator_id: types::UserId, + #[serde(borrow)] + pub moderator_id: Cow<'a, types::UserIdRef>, /// The maximum number of blocked terms to return per page in the response. The minimum page size is 1 blocked term per page and the maximum is 100. The default is 20. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub first: Option, /// The cursor used to get the next page of results. The Pagination object in the response contains the cursor’s value. #[cfg_attr(feature = "typed-builder", builder(default))] - pub after: Option, + pub after: Option>, } -impl GetBlockedTerms { +impl<'a> GetBlockedTerms<'a> { /// Get blocked terms in a broadcasters channel as specified moderator pub fn new( - broadcaster_id: impl Into, - moderator_id: impl Into, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + moderator_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - moderator_id: moderator_id.into(), + broadcaster_id: broadcaster_id.to_cow(), + moderator_id: moderator_id.to_cow(), after: Default::default(), first: Default::default(), } @@ -90,7 +86,7 @@ impl GetBlockedTerms { /// [`get-blocked-terms`](https://dev.twitch.tv/docs/api/reference#get-blocked-terms) pub type GetBlockedTermsResponse = BlockedTerm; -impl Request for GetBlockedTerms { +impl Request for GetBlockedTerms<'_> { type Response = Vec; const PATH: &'static str = "moderation/blocked_terms"; @@ -99,10 +95,12 @@ impl Request for GetBlockedTerms { &[twitch_oauth2::Scope::ModeratorReadBlockedTerms]; } -impl RequestGet for GetBlockedTerms {} +impl RequestGet for GetBlockedTerms<'_> {} -impl helix::Paginated for GetBlockedTerms { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor } +impl helix::Paginated for GetBlockedTerms<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } #[cfg(test)] diff --git a/src/helix/endpoints/moderation/get_moderators.rs b/src/helix/endpoints/moderation/get_moderators.rs index e2f2209048..fa5c245ec1 100644 --- a/src/helix/endpoints/moderation/get_moderators.rs +++ b/src/helix/endpoints/moderation/get_moderators.rs @@ -9,9 +9,7 @@ //! //! ```rust //! use twitch_api::helix::moderation::get_moderators; -//! let request = get_moderators::GetModeratorsRequest::builder() -//! .broadcaster_id("1234") -//! .build(); +//! let request = get_moderators::GetModeratorsRequest::broadcaster_id("1234"); //! ``` //! //! ## Response: [Moderator] @@ -26,9 +24,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_moderators::GetModeratorsRequest::builder() -//! .broadcaster_id("1234") -//! .build(); +//! let request = get_moderators::GetModeratorsRequest::broadcaster_id("1234"); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -47,44 +43,37 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetModeratorsRequest { +pub struct GetModeratorsRequest<'a> { /// Must match the User ID in the Bearer token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// Filters the results and only returns a status object for users who are moderators in this channel and have a matching user_id. #[cfg_attr(feature = "typed-builder", builder(setter(into), default))] - pub user_id: Vec, + #[serde(borrow)] + pub user_id: Cow<'a, [&'a types::UserIdRef]>, /// Cursor for forward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub after: Option, + pub after: Option>, /// Number of values to be returned per page. Limit: 100. Default: 20. #[cfg_attr(feature = "typed-builder", builder(setter(into), default))] pub first: Option, } -impl GetModeratorsRequest { +impl<'a> GetModeratorsRequest<'a> { /// Get moderators in a broadcasters channel. - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - user_id: Default::default(), + broadcaster_id: broadcaster_id.to_cow(), + user_id: Cow::Borrowed(&[]), after: Default::default(), first: Default::default(), } } - /// Check if supplied user is a moderator. - pub fn user_id(mut self, user_id: impl Into) -> Self { - self.user_id = vec![user_id.into()]; - self - } - /// Filter the results for specific users. - pub fn user_ids( - mut self, - user_ids: impl IntoIterator>, - ) -> Self { - self.user_id = user_ids.into_iter().map(Into::into).collect(); + pub fn user_ids(mut self, user_ids: impl Into>) -> Self { + self.user_id = user_ids.into(); self } @@ -110,7 +99,7 @@ pub struct Moderator { pub user_login: types::UserName, } -impl Request for GetModeratorsRequest { +impl Request for GetModeratorsRequest<'_> { type Response = Vec; const PATH: &'static str = "moderation/moderators"; @@ -118,10 +107,12 @@ impl Request for GetModeratorsRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ModerationRead]; } -impl RequestGet for GetModeratorsRequest {} +impl RequestGet for GetModeratorsRequest<'_> {} -impl helix::Paginated for GetModeratorsRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor } +impl helix::Paginated for GetModeratorsRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } #[cfg(test)] diff --git a/src/helix/endpoints/moderation/manage_held_automod_messages.rs b/src/helix/endpoints/moderation/manage_held_automod_messages.rs index d47b1fc477..8a0ca64bb9 100644 --- a/src/helix/endpoints/moderation/manage_held_automod_messages.rs +++ b/src/helix/endpoints/moderation/manage_held_automod_messages.rs @@ -5,7 +5,7 @@ //! //! ## Request: [ManageHeldAutoModMessagesRequest] //! -//! To use this endpoint, construct a [`ManageHeldAutoModMessagesRequest`] with the [`ManageHeldAutoModMessagesRequest::builder()`] method. +//! To use this endpoint, construct a [`ManageHeldAutoModMessagesRequest`] with the [`ManageHeldAutoModMessagesRequest::new()`] method. //! //! ```rust //! use twitch_api::helix::moderation::manage_held_automod_messages; @@ -18,11 +18,11 @@ //! //! ``` //! # use twitch_api::helix::moderation::manage_held_automod_messages; -//! let body = manage_held_automod_messages::ManageHeldAutoModMessagesBody::builder() -//! .action(true) -//! .user_id("9327994") -//! .msg_id("836013710") -//! .build(); +//! let body = manage_held_automod_messages::ManageHeldAutoModMessagesBody::new( +//! "9327994", +//! "836013710", +//! true, +//! ); //! ``` //! //! ## Response: [ManageHeldAutoModMessages] @@ -40,11 +40,11 @@ //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; //! let request = manage_held_automod_messages::ManageHeldAutoModMessagesRequest::new(); -//! let body = manage_held_automod_messages::ManageHeldAutoModMessagesBody::builder() -//! .action(true) -//! .user_id("9327994") -//! .msg_id("836013710") -//! .build(); +//! let body = manage_held_automod_messages::ManageHeldAutoModMessagesBody::new( +//! "9327994", +//! "836013710", +//! true, +//! ); //! let response: manage_held_automod_messages::ManageHeldAutoModMessages = client.req_post(request, body, &token).await?.data; //! # Ok(()) //! # } @@ -53,23 +53,24 @@ //! You can also get the [`http::Request`] with [`request.create_request(&token, &client_id)`](helix::RequestPost::create_request) //! and parse the [`http::Response`] with [`ManageHeldAutoModMessagesRequest::parse_response(None, &request.get_uri(), response)`](ManageHeldAutoModMessagesRequest::parse_response) +use std::marker::PhantomData; + use super::*; use helix::RequestPost; /// Query Parameters for [Manage Held AutoMod Messages](super::manage_held_automod_messages) /// /// [`manage-held-automod-messages`](https://dev.twitch.tv/docs/api/reference#manage-held-automod-messages) -#[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] +#[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug, Default)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct ManageHeldAutoModMessagesRequest {} - -impl ManageHeldAutoModMessagesRequest { - /// Create a new [`ManageHeldAutoModMessagesRequest`] - pub fn new() -> Self { Self {} } +pub struct ManageHeldAutoModMessagesRequest<'a> { + #[serde(skip)] + _marker: PhantomData<&'a ()>, } -impl Default for ManageHeldAutoModMessagesRequest { - fn default() -> Self { Self::new() } +impl ManageHeldAutoModMessagesRequest<'_> { + /// Create a new [`ManageHeldAutoModMessagesRequest`] + pub fn new() -> Self { Self::default() } } /// Body Parameters for [Manage Held AutoMod Messages](super::manage_held_automod_messages) @@ -78,19 +79,21 @@ impl Default for ManageHeldAutoModMessagesRequest { #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct ManageHeldAutoModMessagesBody { +pub struct ManageHeldAutoModMessagesBody<'a> { /// The moderator who is approving or rejecting the held message. Must match the user_id in the user OAuth token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub user_id: types::UserId, + #[serde(borrow)] + pub user_id: Cow<'a, types::UserIdRef>, /// ID of the message to be allowed or denied. These message IDs are retrieved from IRC or PubSub. Only one message ID can be provided. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub msg_id: types::MsgId, + #[serde(borrow)] + pub msg_id: Cow<'a, types::MsgIdRef>, /// The action to take for the message. Must be "ALLOW" or "DENY". #[cfg_attr(feature = "typed-builder", builder(setter(into)))] pub action: AutoModAction, } -impl ManageHeldAutoModMessagesBody { +impl<'a> ManageHeldAutoModMessagesBody<'a> { /// Create a new [`ManageHeldAutoModMessagesBody`] /// /// # Examples @@ -101,20 +104,20 @@ impl ManageHeldAutoModMessagesBody { /// let body = ManageHeldAutoModMessagesBody::new("1234", "5678", true); /// ``` pub fn new( - user_id: impl Into, - msg_id: impl Into, + user_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + msg_id: impl types::IntoCow<'a, types::MsgIdRef> + 'a, action: impl Into, ) -> Self { Self { - user_id: user_id.into(), - msg_id: msg_id.into(), + user_id: user_id.to_cow(), + msg_id: msg_id.to_cow(), action: action.into(), } } } /// Action to take for a message. -#[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] +#[derive(PartialEq, Eq, Deserialize, Serialize, Copy, Clone, Debug)] #[serde(rename_all = "UPPERCASE")] #[non_exhaustive] pub enum AutoModAction { @@ -133,7 +136,7 @@ impl From for AutoModAction { } } -impl helix::private::SealedSerialize for ManageHeldAutoModMessagesBody {} +impl helix::private::SealedSerialize for ManageHeldAutoModMessagesBody<'_> {} /// Return Values for [Manage Held AutoMod Messages](super::manage_held_automod_messages) /// @@ -146,7 +149,7 @@ pub enum ManageHeldAutoModMessages { Success, } -impl Request for ManageHeldAutoModMessagesRequest { +impl Request for ManageHeldAutoModMessagesRequest<'_> { type Response = ManageHeldAutoModMessages; const PATH: &'static str = "moderation/automod/message"; @@ -154,8 +157,8 @@ impl Request for ManageHeldAutoModMessagesRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ModerationRead]; } -impl RequestPost for ManageHeldAutoModMessagesRequest { - type Body = ManageHeldAutoModMessagesBody; +impl<'a> RequestPost for ManageHeldAutoModMessagesRequest<'a> { + type Body = ManageHeldAutoModMessagesBody<'a>; fn parse_inner_response<'d>( request: Option, diff --git a/src/helix/endpoints/moderation/mod.rs b/src/helix/endpoints/moderation/mod.rs index 572167bc2b..04b7e49b39 100644 --- a/src/helix/endpoints/moderation/mod.rs +++ b/src/helix/endpoints/moderation/mod.rs @@ -6,6 +6,7 @@ use crate::{ types, }; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; pub mod add_blocked_term; pub mod add_channel_moderator; diff --git a/src/helix/endpoints/moderation/remove_blocked_term.rs b/src/helix/endpoints/moderation/remove_blocked_term.rs index 452e6016ff..e91842e282 100644 --- a/src/helix/endpoints/moderation/remove_blocked_term.rs +++ b/src/helix/endpoints/moderation/remove_blocked_term.rs @@ -5,15 +5,11 @@ //! //! ## Request: [RemoveBlockedTermRequest] //! -//! To use this endpoint, construct a [`RemoveBlockedTermRequest`] with the [`RemoveBlockedTermRequest::builder()`] method. +//! To use this endpoint, construct a [`RemoveBlockedTermRequest`] with the [`RemoveBlockedTermRequest::new()`] method. //! //! ```rust //! use twitch_api::helix::moderation::remove_blocked_term; -//! let request = remove_blocked_term::RemoveBlockedTermRequest::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .id("DEADBEEF") -//! .build(); +//! let request = remove_blocked_term::RemoveBlockedTermRequest::new("1234", "5678", "DEADBEEF"); //! ``` //! //! ## Response: [RemoveBlockedTerm] @@ -28,11 +24,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = remove_blocked_term::RemoveBlockedTermRequest::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .id("DEADBEEF") -//! .build(); +//! let request = remove_blocked_term::RemoveBlockedTermRequest::new("1234", "5678", "DEADBEEF"); //! let response: remove_blocked_term::RemoveBlockedTerm = client.req_delete(request, &token).await?.data; //! # Ok(()) //! # } @@ -49,30 +41,33 @@ use helix::RequestDelete; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct RemoveBlockedTermRequest { +pub struct RemoveBlockedTermRequest<'a> { /// The ID of the broadcaster that owns the list of blocked terms. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// The ID of a user that has permission to moderate the broadcaster’s chat room. This ID must match the user ID associated with the user OAuth token. /// If the broadcaster wants to delete the blocked term (instead of having the moderator do it), set this parameter to the broadcaster’s ID, too. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub moderator_id: types::UserId, + #[serde(borrow)] + pub moderator_id: Cow<'a, types::UserIdRef>, /// The ID of the blocked term you want to delete. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub id: types::BlockedTermId, + #[serde(borrow)] + pub id: Cow<'a, types::BlockedTermIdRef>, } -impl RemoveBlockedTermRequest { +impl<'a> RemoveBlockedTermRequest<'a> { /// Remove blocked term pub fn new( - broadcaster_id: impl Into, - moderator_id: impl Into, - id: impl Into, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + moderator_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + id: impl types::IntoCow<'a, types::BlockedTermIdRef> + 'a, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - moderator_id: moderator_id.into(), - id: id.into(), + broadcaster_id: broadcaster_id.to_cow(), + moderator_id: moderator_id.to_cow(), + id: id.to_cow(), } } } @@ -87,7 +82,7 @@ pub enum RemoveBlockedTerm { Success, } -impl Request for RemoveBlockedTermRequest { +impl Request for RemoveBlockedTermRequest<'_> { type Response = RemoveBlockedTerm; #[cfg(feature = "twitch_oauth2")] @@ -98,7 +93,7 @@ impl Request for RemoveBlockedTermRequest { &[twitch_oauth2::Scope::ModeratorManageBlockedTerms]; } -impl RequestDelete for RemoveBlockedTermRequest { +impl RequestDelete for RemoveBlockedTermRequest<'_> { fn parse_inner_response( request: Option, uri: &http::Uri, diff --git a/src/helix/endpoints/moderation/remove_channel_moderator.rs b/src/helix/endpoints/moderation/remove_channel_moderator.rs index 7a51974fc7..5c0be34e40 100644 --- a/src/helix/endpoints/moderation/remove_channel_moderator.rs +++ b/src/helix/endpoints/moderation/remove_channel_moderator.rs @@ -5,14 +5,11 @@ //! //! ## Request: [RemoveChannelModeratorRequest] //! -//! To use this endpoint, construct a [`RemoveChannelModeratorRequest`] with the [`RemoveChannelModeratorRequest::builder()`] method. +//! To use this endpoint, construct a [`RemoveChannelModeratorRequest`] with the [`RemoveChannelModeratorRequest::new()`] method. //! //! ```rust //! use twitch_api::helix::moderation::remove_channel_moderator; -//! let request = remove_channel_moderator::RemoveChannelModeratorRequest::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .build(); +//! let request = remove_channel_moderator::RemoveChannelModeratorRequest::new("1234", "5678"); //! ``` //! //! ## Response: [RemoveChannelModeratorResponse] @@ -28,10 +25,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = remove_channel_moderator::RemoveChannelModeratorRequest::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .build(); +//! let request = remove_channel_moderator::RemoveChannelModeratorRequest::new("1234", "5678"); //! let response: helix::moderation::RemoveChannelModeratorResponse = client.req_delete(request, &token).await?.data; //! # Ok(()) //! # } @@ -49,24 +43,26 @@ use helix::RequestDelete; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct RemoveChannelModeratorRequest { +pub struct RemoveChannelModeratorRequest<'a> { /// The ID of the broadcaster that owns the chat room. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// The ID of the user to remove as a moderator from the broadcaster’s chat room. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub moderator_id: types::UserId, + #[serde(borrow)] + pub moderator_id: Cow<'a, types::UserIdRef>, } -impl RemoveChannelModeratorRequest { +impl<'a> RemoveChannelModeratorRequest<'a> { /// Remove moderator pub fn new( - broadcaster_id: impl Into, - moderator_id: impl Into, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + moderator_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - moderator_id: moderator_id.into(), + broadcaster_id: broadcaster_id.to_cow(), + moderator_id: moderator_id.to_cow(), } } } @@ -82,7 +78,7 @@ pub enum RemoveChannelModeratorResponse { Success, } -impl Request for RemoveChannelModeratorRequest { +impl Request for RemoveChannelModeratorRequest<'_> { type Response = RemoveChannelModeratorResponse; const PATH: &'static str = "moderation/moderators"; @@ -90,7 +86,7 @@ impl Request for RemoveChannelModeratorRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelManageModerators]; } -impl RequestDelete for RemoveChannelModeratorRequest { +impl RequestDelete for RemoveChannelModeratorRequest<'_> { fn parse_inner_response<'d>( request: Option, uri: &http::Uri, diff --git a/src/helix/endpoints/moderation/unban_user.rs b/src/helix/endpoints/moderation/unban_user.rs index 76f77bf020..fafceea366 100644 --- a/src/helix/endpoints/moderation/unban_user.rs +++ b/src/helix/endpoints/moderation/unban_user.rs @@ -5,15 +5,11 @@ //! //! ## Request: [UnbanUserRequest] //! -//! To use this endpoint, construct a [`UnbanUserRequest`] with the [`UnbanUserRequest::builder()`] method. +//! To use this endpoint, construct a [`UnbanUserRequest`] with the [`UnbanUserRequest::new()`] method. //! //! ```rust //! use twitch_api::helix::moderation::unban_user; -//! let request = unban_user::UnbanUserRequest::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .user_id("1337") -//! .build(); +//! let request = unban_user::UnbanUserRequest::new("1234", "5678", "1337"); //! ``` //! //! ## Response: [UnbanUserResponse] @@ -30,11 +26,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = unban_user::UnbanUserRequest::builder() -//! .broadcaster_id("1234") -//! .moderator_id("5678") -//! .user_id("1337") -//! .build(); +//! let request = unban_user::UnbanUserRequest::new("1234", "5678", "1337"); //! let response: unban_user::UnbanUserResponse = client.req_delete(request, &token).await?.data; //! # Ok(()) //! # } @@ -51,29 +43,32 @@ use helix::RequestDelete; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct UnbanUserRequest { +pub struct UnbanUserRequest<'a> { /// The ID of the broadcaster whose chat room the user is banned from chatting in. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// The ID of a user that has permission to moderate the broadcaster’s chat room. This ID must match the user ID associated with the user OAuth token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub moderator_id: types::UserId, + #[serde(borrow)] + pub moderator_id: Cow<'a, types::UserIdRef>, /// The ID of the user to remove the ban or timeout from. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub user_id: types::UserId, + #[serde(borrow)] + pub user_id: Cow<'a, types::UserIdRef>, } -impl UnbanUserRequest { +impl<'a> UnbanUserRequest<'a> { /// Remove the ban or timeout that was placed on the specified user. pub fn new( - broadcaster_id: impl Into, - moderator_id: impl Into, - user_id: impl Into, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + moderator_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + user_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - moderator_id: moderator_id.into(), - user_id: user_id.into(), + broadcaster_id: broadcaster_id.to_cow(), + moderator_id: moderator_id.to_cow(), + user_id: user_id.to_cow(), } } } @@ -88,7 +83,7 @@ pub enum UnbanUserResponse { Success, } -impl Request for UnbanUserRequest { +impl Request for UnbanUserRequest<'_> { type Response = UnbanUserResponse; const PATH: &'static str = "moderation/bans"; @@ -97,7 +92,7 @@ impl Request for UnbanUserRequest { &[twitch_oauth2::Scope::ModeratorManageBannedUsers]; } -impl RequestDelete for UnbanUserRequest { +impl RequestDelete for UnbanUserRequest<'_> { fn parse_inner_response( request: Option, uri: &http::Uri, diff --git a/src/helix/endpoints/points/create_custom_rewards.rs b/src/helix/endpoints/points/create_custom_rewards.rs index 3d343e82ff..af646cc26c 100644 --- a/src/helix/endpoints/points/create_custom_rewards.rs +++ b/src/helix/endpoints/points/create_custom_rewards.rs @@ -5,13 +5,11 @@ //! //! ## Request: [CreateCustomRewardRequest] //! -//! To use this endpoint, construct a [`CreateCustomRewardRequest`] with the [`CreateCustomRewardRequest::builder()`] method. +//! To use this endpoint, construct a [`CreateCustomRewardRequest`] with the [`CreateCustomRewardRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::points::create_custom_rewards; -//! let request = create_custom_rewards::CreateCustomRewardRequest::builder() -//! .broadcaster_id("274637212") -//! .build(); +//! let request = create_custom_rewards::CreateCustomRewardRequest::broadcaster_id("274637212"); //! ``` //! //! ## Body: [CreateCustomRewardBody] @@ -20,10 +18,7 @@ //! //! ``` //! # use twitch_api::helix::points::create_custom_rewards; -//! let body = create_custom_rewards::CreateCustomRewardBody::builder() -//! .cost(500) -//! .title("hydrate!") -//! .build(); +//! let mut body = create_custom_rewards::CreateCustomRewardBody::new("hydrate", 500); //! ``` //! //! ## Response: [CreateCustomRewardResponse] @@ -40,13 +35,8 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = create_custom_rewards::CreateCustomRewardRequest::builder() -//! .broadcaster_id("274637212") -//! .build(); -//! let body = create_custom_rewards::CreateCustomRewardBody::builder() -//! .cost(500) -//! .title("hydrate!") -//! .build(); +//! let request = create_custom_rewards::CreateCustomRewardRequest::broadcaster_id("274637212"); +//! let mut body = create_custom_rewards::CreateCustomRewardBody::new("hydrate", 500); //! let response: create_custom_rewards::CreateCustomRewardResponse = client.req_post(request, body, &token).await?.data; //! # Ok(()) //! # } @@ -63,17 +53,18 @@ use helix::RequestPost; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct CreateCustomRewardRequest { +pub struct CreateCustomRewardRequest<'a> { /// Provided broadcaster_id must match the user_id in the auth token #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, } -impl CreateCustomRewardRequest { +impl<'a> CreateCustomRewardRequest<'a> { /// Channel to create reward on - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), } } } @@ -84,14 +75,15 @@ impl CreateCustomRewardRequest { #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct CreateCustomRewardBody { +pub struct CreateCustomRewardBody<'a> { /// The title of the reward #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub title: String, + #[serde(borrow)] + pub title: Cow<'a, str>, /// The prompt for the viewer when they are redeeming the reward #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - #[serde(skip_serializing_if = "Option::is_none")] - pub prompt: Option, + #[serde(skip_serializing_if = "Option::is_none", borrow)] + pub prompt: Option>, /// The cost of the reward pub cost: usize, /// Is the reward currently enabled, if false the reward won’t show up to viewers. Defaults true @@ -100,8 +92,8 @@ pub struct CreateCustomRewardBody { pub is_enabled: Option, /// Custom background color for the reward. Format: Hex with # prefix. Example: #00E5CB. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - #[serde(skip_serializing_if = "Option::is_none")] - pub background_color: Option, + #[serde(skip_serializing_if = "Option::is_none", borrow)] + pub background_color: Option>, /// Does the user need to enter information when redeeming the reward. Defaults false #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] #[serde(skip_serializing_if = "Option::is_none")] @@ -136,12 +128,12 @@ pub struct CreateCustomRewardBody { pub should_redemptions_skip_request_queue: Option, } -impl CreateCustomRewardBody { +impl<'a> CreateCustomRewardBody<'a> { // FIXME: need to add more here /// Reward to create with title. - pub fn new(title: String, cost: usize) -> Self { + pub fn new(title: impl Into>, cost: usize) -> Self { Self { - title, + title: title.into(), prompt: Default::default(), cost, is_enabled: Default::default(), @@ -158,14 +150,14 @@ impl CreateCustomRewardBody { } } -impl helix::private::SealedSerialize for CreateCustomRewardBody {} +impl helix::private::SealedSerialize for CreateCustomRewardBody<'_> {} /// Return Values for [Create Custom Rewards](super::create_custom_rewards) /// /// [`create-custom-rewards`](https://dev.twitch.tv/docs/api/reference#create-custom-rewards) pub type CreateCustomRewardResponse = super::CustomReward; -impl Request for CreateCustomRewardRequest { +impl Request for CreateCustomRewardRequest<'_> { type Response = CreateCustomRewardResponse; const PATH: &'static str = "channel_points/custom_rewards"; @@ -174,8 +166,8 @@ impl Request for CreateCustomRewardRequest { &[twitch_oauth2::Scope::ChannelManageRedemptions]; } -impl RequestPost for CreateCustomRewardRequest { - type Body = CreateCustomRewardBody; +impl<'a> RequestPost for CreateCustomRewardRequest<'a> { + type Body = CreateCustomRewardBody<'a>; fn parse_inner_response( request: Option, @@ -219,7 +211,7 @@ fn test_request() { use helix::*; let req = CreateCustomRewardRequest::broadcaster_id("274637212"); - let body = CreateCustomRewardBody::new("game analysis 1v1".to_owned(), 50000); + let body = CreateCustomRewardBody::new("game analysis 1v1", 50000); dbg!(req.create_request(body, "token", "clientid").unwrap()); diff --git a/src/helix/endpoints/points/delete_custom_reward.rs b/src/helix/endpoints/points/delete_custom_reward.rs index 7a997dbf4b..986eb08918 100644 --- a/src/helix/endpoints/points/delete_custom_reward.rs +++ b/src/helix/endpoints/points/delete_custom_reward.rs @@ -5,14 +5,11 @@ //! //! ## Request: [DeleteCustomRewardRequest] //! -//! To use this endpoint, construct a [`DeleteCustomRewardRequest`] with the [`DeleteCustomRewardRequest::builder()`] method. +//! To use this endpoint, construct a [`DeleteCustomRewardRequest`] with the [`DeleteCustomRewardRequest::new()`] method. //! //! ```rust //! use twitch_api::helix::points::delete_custom_reward; -//! let request = delete_custom_reward::DeleteCustomRewardRequest::builder() -//! .broadcaster_id("274637212") -//! .id("1234") -//! .build(); +//! let request = delete_custom_reward::DeleteCustomRewardRequest::new("274637212", "1234"); //! ``` //! //! ## Response: [DeleteCustomReward] @@ -27,10 +24,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = delete_custom_reward::DeleteCustomRewardRequest::builder() -//! .broadcaster_id("274637212") -//! .id("1234") -//! .build(); +//! let request = delete_custom_reward::DeleteCustomRewardRequest::new("274637212", "1234"); //! let response: delete_custom_reward::DeleteCustomReward = client.req_delete(request, &token).await?.data; //! # Ok(()) //! # } @@ -48,21 +42,26 @@ use helix::RequestDelete; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct DeleteCustomRewardRequest { +pub struct DeleteCustomRewardRequest<'a> { /// Provided broadcaster_id must match the user_id in the auth token #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// ID of the Custom Reward to delete, must match a Custom Reward on broadcaster_id’s channel. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub id: types::RewardId, + #[serde(borrow)] + pub id: Cow<'a, types::RewardIdRef>, } -impl DeleteCustomRewardRequest { +impl<'a> DeleteCustomRewardRequest<'a> { /// Reward to delete - pub fn new(broadcaster_id: impl Into, id: impl Into) -> Self { + pub fn new( + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + id: impl types::IntoCow<'a, types::RewardIdRef> + 'a, + ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - id: id.into(), + broadcaster_id: broadcaster_id.to_cow(), + id: id.to_cow(), } } } @@ -79,7 +78,7 @@ pub enum DeleteCustomReward { Success, } -impl Request for DeleteCustomRewardRequest { +impl Request for DeleteCustomRewardRequest<'_> { type Response = DeleteCustomReward; const PATH: &'static str = "channel_points/custom_rewards"; @@ -88,7 +87,7 @@ impl Request for DeleteCustomRewardRequest { &[twitch_oauth2::Scope::ChannelManageRedemptions]; } -impl RequestDelete for DeleteCustomRewardRequest { +impl RequestDelete for DeleteCustomRewardRequest<'_> { fn parse_inner_response( request: Option, uri: &http::Uri, diff --git a/src/helix/endpoints/points/get_custom_reward.rs b/src/helix/endpoints/points/get_custom_reward.rs index e4466d8a09..12251a7564 100644 --- a/src/helix/endpoints/points/get_custom_reward.rs +++ b/src/helix/endpoints/points/get_custom_reward.rs @@ -7,13 +7,11 @@ //! //! ## Request: [GetCustomRewardRequest] //! -//! To use this endpoint, construct a [`GetCustomRewardRequest`] with the [`GetCustomRewardRequest::builder()`] method. +//! To use this endpoint, construct a [`GetCustomRewardRequest`] with the [`GetCustomRewardRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::points::GetCustomRewardRequest; -//! let request = GetCustomRewardRequest::builder() -//! .broadcaster_id("274637212".to_string()) -//! .build(); +//! let request = GetCustomRewardRequest::broadcaster_id("274637212"); //! ``` //! //! ## Response: [CustomReward] @@ -29,9 +27,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = GetCustomRewardRequest::builder() -//! .broadcaster_id("274637212".to_string()) -//! .build(); +//! let request = GetCustomRewardRequest::broadcaster_id("274637212"); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -49,37 +45,33 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetCustomRewardRequest { +pub struct GetCustomRewardRequest<'a> { /// Provided broadcaster_id must match the user_id in the auth token #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// When used, this parameter filters the results and only returns reward objects for the Custom Rewards with matching ID. Maximum: 50 #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub id: Vec, + #[serde(borrow)] + pub id: Cow<'a, [&'a types::RewardIdRef]>, /// When set to true, only returns custom rewards that the calling client_id can manage. Defaults false. #[cfg_attr(feature = "typed-builder", builder(default))] pub only_manageable_rewards: Option, } -impl GetCustomRewardRequest { +impl<'a> GetCustomRewardRequest<'a> { /// Rewards on this broadcasters channel - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - id: Default::default(), + broadcaster_id: broadcaster_id.to_cow(), + id: Cow::Borrowed(&[]), only_manageable_rewards: Default::default(), } } - /// Get reward with this id - pub fn id(mut self, id: impl Into) -> Self { - self.id = vec![id.into()]; - self - } - /// Get rewards with these ids - pub fn ids(mut self, id: impl IntoIterator>) -> Self { - self.id = id.into_iter().map(Into::into).collect(); + pub fn ids(mut self, id: impl Into>) -> Self { + self.id = id.into(); self } @@ -139,7 +131,7 @@ pub struct CustomReward { pub cooldown_expires_at: Option, } -impl Request for GetCustomRewardRequest { +impl Request for GetCustomRewardRequest<'_> { type Response = Vec; const PATH: &'static str = "channel_points/custom_rewards"; @@ -148,13 +140,13 @@ impl Request for GetCustomRewardRequest { &[twitch_oauth2::scopes::Scope::ChannelReadRedemptions]; } -impl RequestGet for GetCustomRewardRequest {} +impl RequestGet for GetCustomRewardRequest<'_> {} #[cfg(test)] #[test] fn test_request() { use helix::*; - let req = GetCustomRewardRequest::broadcaster_id("274637212".to_string()); + let req = GetCustomRewardRequest::broadcaster_id("274637212"); // From twitch docs let data = br##" diff --git a/src/helix/endpoints/points/get_custom_reward_redemption.rs b/src/helix/endpoints/points/get_custom_reward_redemption.rs index ff15a493c5..1011399f65 100644 --- a/src/helix/endpoints/points/get_custom_reward_redemption.rs +++ b/src/helix/endpoints/points/get_custom_reward_redemption.rs @@ -51,14 +51,16 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetCustomRewardRedemptionRequest { +pub struct GetCustomRewardRedemptionRequest<'a> { /// Provided broadcaster_id must match the user_id in the auth token #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// When ID is not provided, this parameter returns paginated Custom Reward Redemption objects for redemptions of the Custom Reward with ID reward_id #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub reward_id: Option, + #[serde(borrow)] + pub reward_id: Option>, /// When id is not provided, this param is required and filters the paginated Custom Reward Redemption objects for redemptions with the matching status. Can be one of UNFULFILLED, FULFILLED or CANCELED #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] @@ -66,18 +68,18 @@ pub struct GetCustomRewardRedemptionRequest { /// Cursor for forward pagination: tells the server where to start fetching the next set of results, in a multi-page response. This applies only to queries without ID. If an ID is specified, it supersedes any cursor/offset combinations. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub after: Option, + pub after: Option>, /// Number of results to be returned when getting the paginated Custom Reward Redemption objects for a reward. Limit: 50. Default: 20. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub first: Option, } -impl GetCustomRewardRedemptionRequest { +impl<'a> GetCustomRewardRedemptionRequest<'a> { /// Reward to fetch - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), reward_id: None, status: Default::default(), after: Default::default(), @@ -86,8 +88,11 @@ impl GetCustomRewardRedemptionRequest { } /// Specific reward to query - pub fn reward_id(mut self, reward_id: impl Into) -> Self { - self.reward_id = Some(reward_id.into()); + pub fn reward_id( + mut self, + reward_id: impl types::IntoCow<'a, types::RewardIdRef> + 'a, + ) -> Self { + self.reward_id = Some(reward_id.to_cow()); self } @@ -157,7 +162,7 @@ pub struct Reward { pub cost: i64, } -impl Request for GetCustomRewardRedemptionRequest { +impl Request for GetCustomRewardRedemptionRequest<'_> { type Response = Vec; const PATH: &'static str = "channel_points/custom_rewards/redemptions"; @@ -166,10 +171,12 @@ impl Request for GetCustomRewardRedemptionRequest { &[twitch_oauth2::scopes::Scope::ChannelReadRedemptions]; } -impl RequestGet for GetCustomRewardRedemptionRequest {} +impl RequestGet for GetCustomRewardRedemptionRequest<'_> {} -impl helix::Paginated for GetCustomRewardRedemptionRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor } +impl helix::Paginated for GetCustomRewardRedemptionRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } #[cfg(test)] diff --git a/src/helix/endpoints/points/mod.rs b/src/helix/endpoints/points/mod.rs index ebad1639bc..891500019d 100644 --- a/src/helix/endpoints/points/mod.rs +++ b/src/helix/endpoints/points/mod.rs @@ -25,6 +25,7 @@ use crate::{ types, }; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; pub mod create_custom_rewards; pub mod delete_custom_reward; @@ -50,7 +51,7 @@ pub use update_redemption_status::{ UpdateRedemptionStatusBody, UpdateRedemptionStatusInformation, UpdateRedemptionStatusRequest, }; /// Custom reward redemption statuses: UNFULFILLED, FULFILLED or CANCELED -#[derive(PartialEq, Eq, serde::Serialize, serde::Deserialize, Clone, Debug)] +#[derive(PartialEq, Eq, serde::Serialize, serde::Deserialize, Copy, Clone, Debug)] pub enum CustomRewardRedemptionStatus { /// Unfulfilled reward - the user has claimed it but it is still pending. #[serde(rename = "UNFULFILLED")] diff --git a/src/helix/endpoints/points/update_custom_reward.rs b/src/helix/endpoints/points/update_custom_reward.rs index 241a55428a..dee54c4259 100644 --- a/src/helix/endpoints/points/update_custom_reward.rs +++ b/src/helix/endpoints/points/update_custom_reward.rs @@ -7,14 +7,11 @@ //! //! ## Request: [UpdateCustomRewardRequest] //! -//! To use this endpoint, construct an [`UpdateCustomRewardRequest`] with the [`UpdateCustomRewardRequest::builder()`] method. +//! To use this endpoint, construct an [`UpdateCustomRewardRequest`] with the [`UpdateCustomRewardRequest::new()`] method. //! //! ```rust //! use twitch_api::helix::points::update_custom_reward; -//! let request = update_custom_reward::UpdateCustomRewardRequest::builder() -//! .broadcaster_id("274637212") -//! .id("reward-id") -//! .build(); +//! let request = update_custom_reward::UpdateCustomRewardRequest::new("274637212", "reward-id"); //! ``` //! //! ## Body: [UpdateCustomRewardBody] @@ -23,10 +20,9 @@ //! //! ``` //! # use twitch_api::helix::points::update_custom_reward; -//! let body = update_custom_reward::UpdateCustomRewardBody::builder() -//! .cost(501) -//! .title("hydrate but differently now!".to_string()) -//! .build(); +//! let mut body = update_custom_reward::UpdateCustomRewardBody::default(); +//! body.cost = Some(501); +//! body.title = Some("hydrate but differently now!".into()); //! ``` //! //! ## Response: [UpdateCustomReward] @@ -43,14 +39,10 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = update_custom_reward::UpdateCustomRewardRequest::builder() -//! .broadcaster_id("274637212") -//! .id("reward-id") -//! .build(); -//! let body = update_custom_reward::UpdateCustomRewardBody::builder() -//! .cost(501) -//! .title("hydrate but differently now!".to_string()) -//! .build(); +//! let request = update_custom_reward::UpdateCustomRewardRequest::new("274637212", "reward-id"); +//! let mut body = update_custom_reward::UpdateCustomRewardBody::default(); +//! body.cost = Some(501); +//! body.title = Some("hydrate but differently now!".into()); //! let response: update_custom_reward::UpdateCustomReward = client.req_patch(request, body, &token).await?.data; //! # Ok(()) //! # } @@ -69,21 +61,26 @@ use helix::RequestPatch; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct UpdateCustomRewardRequest { +pub struct UpdateCustomRewardRequest<'a> { /// Provided broadcaster_id must match the user_id in the auth token #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// ID of the Custom Reward to update, must match a Custom Reward on broadcaster_id’s channel. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub id: types::RewardId, + #[serde(borrow)] + pub id: Cow<'a, types::RewardIdRef>, } -impl UpdateCustomRewardRequest { +impl<'a> UpdateCustomRewardRequest<'a> { /// Update a Custom Reward created on the broadcaster's channel - pub fn new(broadcaster_id: impl Into, id: impl Into) -> Self { + pub fn new( + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + id: impl types::IntoCow<'a, types::RewardIdRef> + 'a, + ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - id: id.into(), + broadcaster_id: broadcaster_id.to_cow(), + id: id.to_cow(), } } } @@ -94,23 +91,23 @@ impl UpdateCustomRewardRequest { #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug, Default)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct UpdateCustomRewardBody { +pub struct UpdateCustomRewardBody<'a> { /// The title of the reward #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - #[serde(skip_serializing_if = "Option::is_none")] - pub title: Option, + #[serde(skip_serializing_if = "Option::is_none", borrow)] + pub title: Option>, /// The prompt for the viewer when they are redeeming the reward #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - #[serde(skip_serializing_if = "Option::is_none")] - pub prompt: Option, + #[serde(skip_serializing_if = "Option::is_none", borrow)] + pub prompt: Option>, /// The cost of the reward #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] #[serde(skip_serializing_if = "Option::is_none")] pub cost: Option, /// Custom background color for the reward. Format: Hex with # prefix. Example: #00E5CB. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - #[serde(skip_serializing_if = "Option::is_none")] - pub background_color: Option, + #[serde(skip_serializing_if = "Option::is_none", borrow)] + pub background_color: Option>, /// Is the reward currently enabled, if false the reward won’t show up to viewers #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] #[serde(skip_serializing_if = "Option::is_none")] @@ -153,7 +150,7 @@ pub struct UpdateCustomRewardBody { pub should_redemptions_skip_request_queue: Option, } -impl helix::private::SealedSerialize for UpdateCustomRewardBody {} +impl helix::private::SealedSerialize for UpdateCustomRewardBody<'_> {} /// Return Values for [Update CustomReward](super::update_custom_reward) /// @@ -166,7 +163,7 @@ pub enum UpdateCustomReward { Success(CustomReward), } -impl Request for UpdateCustomRewardRequest { +impl Request for UpdateCustomRewardRequest<'_> { type Response = UpdateCustomReward; const PATH: &'static str = "channel_points/custom_rewards"; @@ -175,8 +172,8 @@ impl Request for UpdateCustomRewardRequest { &[twitch_oauth2::Scope::ChannelManageRedemptions]; } -impl RequestPatch for UpdateCustomRewardRequest { - type Body = UpdateCustomRewardBody; +impl<'a> RequestPatch for UpdateCustomRewardRequest<'a> { + type Body = UpdateCustomRewardBody<'a>; fn parse_inner_response( request: Option, diff --git a/src/helix/endpoints/points/update_redemption_status.rs b/src/helix/endpoints/points/update_redemption_status.rs index a787b79c70..867f9384f5 100644 --- a/src/helix/endpoints/points/update_redemption_status.rs +++ b/src/helix/endpoints/points/update_redemption_status.rs @@ -7,15 +7,15 @@ //! //! ## Request: [UpdateRedemptionStatusRequest] //! -//! To use this endpoint, construct a [`UpdateRedemptionStatusRequest`] with the [`UpdateRedemptionStatusRequest::builder()`] method. +//! To use this endpoint, construct a [`UpdateRedemptionStatusRequest`] with the [`UpdateRedemptionStatusRequest::new()`] method. //! //! ```rust //! use twitch_api::helix::points::UpdateRedemptionStatusRequest; -//! let request = UpdateRedemptionStatusRequest::builder() -//! .broadcaster_id("274637212".to_string()) -//! .reward_id("92af127c-7326-4483-a52b-b0da0be61c01".to_string()) -//! .id("17fa2df1-ad76-4804-bfa5-a40ef63efe63".to_string()) -//! .build(); +//! let request = UpdateRedemptionStatusRequest::new( +//! "274637212", +//! "92af127c-7326-4483-a52b-b0da0be61c01", +//! "17fa2df1-ad76-4804-bfa5-a40ef63efe63", +//! ); //! ``` //! //! ## Body: [UpdateRedemptionStatusBody] @@ -24,9 +24,7 @@ //! //! ``` //! use twitch_api::helix::points::{CustomRewardRedemptionStatus, UpdateRedemptionStatusBody}; -//! let body = UpdateRedemptionStatusBody::builder() -//! .status(CustomRewardRedemptionStatus::Canceled) -//! .build(); +//! let body = UpdateRedemptionStatusBody::status(CustomRewardRedemptionStatus::Canceled); //! ``` //! //! ## Response: [UpdateRedemptionStatusInformation] @@ -46,14 +44,12 @@ //! helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = UpdateRedemptionStatusRequest::builder() -//! .broadcaster_id("274637212".to_string()) -//! .reward_id("92af127c-7326-4483-a52b-b0da0be61c01".to_string()) -//! .id("17fa2df1-ad76-4804-bfa5-a40ef63efe63".to_string()) -//! .build(); -//! let body = UpdateRedemptionStatusBody::builder() -//! .status(CustomRewardRedemptionStatus::Canceled) -//! .build(); +//! let request = UpdateRedemptionStatusRequest::new( +//! "274637212", +//! "92af127c-7326-4483-a52b-b0da0be61c01", +//! "17fa2df1-ad76-4804-bfa5-a40ef63efe63", +//! ); +//! let body = UpdateRedemptionStatusBody::status(CustomRewardRedemptionStatus::Canceled); //! let response: UpdateRedemptionStatusInformation = //! client.req_patch(request, body, &token).await?.data; //! # Ok(()) @@ -75,31 +71,34 @@ use helix::RequestPatch; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct UpdateRedemptionStatusRequest { +pub struct UpdateRedemptionStatusRequest<'a> { /// Provided broadcaster_id must match the user_id in the auth token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// ID of the Custom Reward the redemptions to be updated are for. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub reward_id: types::RewardId, + #[serde(borrow)] + pub reward_id: Cow<'a, types::RewardIdRef>, /// ID of the Custom Reward Redemption to update, must match a Custom Reward Redemption on broadcaster_id’s channel #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub id: types::RedemptionId, + #[serde(borrow)] + pub id: Cow<'a, types::RedemptionIdRef>, } -impl UpdateRedemptionStatusRequest { +impl<'a> UpdateRedemptionStatusRequest<'a> { /// Update the status of Custom Reward Redemption object on a channel that are in the UNFULFILLED status. pub fn new( - broadcaster_id: impl Into, - reward_id: impl Into, - id: impl Into, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + reward_id: impl types::IntoCow<'a, types::RewardIdRef> + 'a, + id: impl types::IntoCow<'a, types::RedemptionIdRef> + 'a, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - reward_id: reward_id.into(), - id: id.into(), + broadcaster_id: broadcaster_id.to_cow(), + reward_id: reward_id.to_cow(), + id: id.to_cow(), } } } @@ -132,7 +131,7 @@ pub enum UpdateRedemptionStatusInformation { Success(CustomRewardRedemption), } -impl Request for UpdateRedemptionStatusRequest { +impl Request for UpdateRedemptionStatusRequest<'_> { type Response = UpdateRedemptionStatusInformation; const PATH: &'static str = "channel_points/custom_rewards/redemptions"; @@ -141,7 +140,7 @@ impl Request for UpdateRedemptionStatusRequest { &[twitch_oauth2::scopes::Scope::ChannelManageBroadcast]; } -impl RequestPatch for UpdateRedemptionStatusRequest { +impl RequestPatch for UpdateRedemptionStatusRequest<'_> { type Body = UpdateRedemptionStatusBody; fn parse_inner_response( diff --git a/src/helix/endpoints/polls/create_poll.rs b/src/helix/endpoints/polls/create_poll.rs index 232b3c5290..645c85c2b1 100644 --- a/src/helix/endpoints/polls/create_poll.rs +++ b/src/helix/endpoints/polls/create_poll.rs @@ -17,18 +17,14 @@ //! We also need to provide a body to the request containing what we want to change. //! //! ``` -//! # use twitch_api::helix::polls::create_poll; -//! let body = create_poll::CreatePollBody::builder() -//! .broadcaster_id("141981764") -//! .title("Heads or Tails?") -//! .choices(vec![ -//! create_poll::NewPollChoice::new("Heads"), -//! create_poll::NewPollChoice::new("Tails"), -//! ]) -//! .channel_points_voting_enabled(true) -//! .channel_points_per_vote(100) -//! .duration(1800) -//! .build(); +//! use twitch_api::helix::polls::create_poll; +//! let choices: &[create_poll::NewPollChoice] = &[ +//! create_poll::NewPollChoice::new("Heads"), +//! create_poll::NewPollChoice::new("Tails"), +//! ]; +//! let mut body = create_poll::CreatePollBody::new("141981764", "Heads or Tails?", 1800, choices); +//! body.channel_points_voting_enabled = Some(true); +//! body.channel_points_per_vote = Some(100); //! ``` //! //! ## Response: [CreatePollResponse] @@ -45,16 +41,14 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = create_poll::CreatePollRequest::builder() -//! .build(); -//! let body = create_poll::CreatePollBody::builder() -//! .broadcaster_id("141981764") -//! .title("Heads or Tails?") -//! .choices(vec![create_poll::NewPollChoice::new("Heads"), create_poll::NewPollChoice::new("Tails")]) -//! .channel_points_voting_enabled(true) -//! .channel_points_per_vote(100) -//! .duration(1800) -//! .build(); +//! let request = create_poll::CreatePollRequest::new(); +//! let choices: &[create_poll::NewPollChoice] = &[ +//! create_poll::NewPollChoice::new("Heads"), +//! create_poll::NewPollChoice::new("Tails"), +//! ]; +//! let mut body = create_poll::CreatePollBody::new("141981764", "Heads or Tails?", 1800, choices); +//! body.channel_points_voting_enabled = Some(true); +//! body.channel_points_per_vote = Some(100); //! let response: create_poll::CreatePollResponse = client.req_post(request, body, &token).await?.data; //! # Ok(()) //! # } @@ -65,17 +59,23 @@ use super::*; use helix::RequestPost; +use std::marker::PhantomData; + /// Query Parameters for [Create Poll](super::create_poll) /// /// [`create-poll`](https://dev.twitch.tv/docs/api/reference#create-poll) #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug, Default)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct CreatePollRequest {} +pub struct CreatePollRequest<'a> { + #[cfg_attr(feature = "typed-builder", builder(default))] + #[serde(skip)] + _marker: PhantomData<&'a ()>, +} -impl CreatePollRequest { +impl CreatePollRequest<'_> { /// Create a new [`CreatePollRequest`] - pub fn new() -> Self { Self {} } + pub fn new() -> Self { Self::default() } } /// Body Parameters for [Create Poll](super::create_poll) @@ -84,17 +84,24 @@ impl CreatePollRequest { #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct CreatePollBody { +pub struct CreatePollBody<'a> { /// The broadcaster running polls. Provided broadcaster_id must match the user_id in the user OAuth token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// Question displayed for the poll. Maximum: 60 characters. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub title: String, + #[serde(borrow)] + pub title: Cow<'a, str>, /// Total duration for the poll (in seconds). Minimum: 15. Maximum: 1800. pub duration: i64, /// Array of the poll choices. Minimum: 2 choices. Maximum: 5 choices. - pub choices: Vec, + #[cfg_attr( + feature = "typed-builder", + builder(default_code = "Cow::Borrowed(&[])", setter(into)) + )] + #[serde(borrow)] + pub choices: Cow<'a, [NewPollChoice<'a>]>, /// Indicates if Bits can be used for voting. Default: false #[deprecated(since = "0.7.0", note = "bit options for polls has been removed")] #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] @@ -113,7 +120,7 @@ pub struct CreatePollBody { pub channel_points_per_vote: Option, } -impl CreatePollBody { +impl<'a> CreatePollBody<'a> { /// Set if Channel Points voting is enabled pub fn channel_points_voting_enabled(mut self, enabled: bool) -> Self { self.channel_points_voting_enabled = Some(enabled); @@ -128,16 +135,16 @@ impl CreatePollBody { /// Poll settings pub fn new( - broadcaster_id: impl Into, - title: String, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + title: impl Into>, duration: i64, - choices: impl IntoIterator>, + choices: impl Into]>>, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - title, + broadcaster_id: broadcaster_id.to_cow(), + title: title.into(), duration, - choices: choices.into_iter().map(|s| s.into()).collect(), + choices: choices.into(), bits_voting_enabled: Default::default(), bits_per_vote: Default::default(), channel_points_voting_enabled: Default::default(), @@ -146,20 +153,21 @@ impl CreatePollBody { } } -impl helix::private::SealedSerialize for CreatePollBody {} +impl helix::private::SealedSerialize for CreatePollBody<'_> {} /// Choice settings for a poll #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct NewPollChoice { +pub struct NewPollChoice<'a> { /// Text displayed for the choice. Maximum: 25 characters. - pub title: String, + #[serde(borrow)] + pub title: Cow<'a, str>, } -impl NewPollChoice { +impl<'a> NewPollChoice<'a> { /// Create a new [`NewPollChoice`] - pub fn new(title: impl Into) -> Self { + pub fn new(title: impl Into>) -> Self { Self { title: title.into(), } @@ -171,7 +179,7 @@ impl NewPollChoice { /// [`create-poll`](https://dev.twitch.tv/docs/api/reference#create-poll) pub type CreatePollResponse = super::Poll; -impl Request for CreatePollRequest { +impl Request for CreatePollRequest<'_> { type Response = CreatePollResponse; const PATH: &'static str = "polls"; @@ -179,8 +187,8 @@ impl Request for CreatePollRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelManagePolls]; } -impl RequestPost for CreatePollRequest { - type Body = CreatePollBody; +impl<'a> RequestPost for CreatePollRequest<'a> { + type Body = CreatePollBody<'a>; fn parse_inner_response( request: Option, @@ -224,14 +232,10 @@ fn test_request() { use helix::*; let req = CreatePollRequest::new(); - let body = CreatePollBody::new( - "141981764", - "Heads or Tails?".to_owned(), - 1800, - [NewPollChoice::new("Heads"), NewPollChoice::new("Tails")], - ) - .channel_points_per_vote(100) - .channel_points_voting_enabled(true); + let choices: &[NewPollChoice] = &[NewPollChoice::new("Heads"), NewPollChoice::new("Tails")]; + let body = CreatePollBody::new("141981764", "Heads or Tails?", 1800, choices) + .channel_points_per_vote(100) + .channel_points_voting_enabled(true); dbg!(req.create_request(body, "token", "clientid").unwrap()); diff --git a/src/helix/endpoints/polls/end_poll.rs b/src/helix/endpoints/polls/end_poll.rs index 31643abf2f..b3b1bad791 100644 --- a/src/helix/endpoints/polls/end_poll.rs +++ b/src/helix/endpoints/polls/end_poll.rs @@ -19,11 +19,11 @@ //! //! ``` //! # use twitch_api::helix::polls::end_poll; -//! let body = end_poll::EndPollBody::builder() -//! .broadcaster_id("274637212") -//! .id("92af127c-7326-4483-a52b-b0da0be61c01") -//! .status(end_poll::PollStatus::Terminated) -//! .build(); +//! let body = end_poll::EndPollBody::new( +//! "274637212", +//! "92af127c-7326-4483-a52b-b0da0be61c01", +//! end_poll::PollStatus::Terminated, +//! ); //! ``` //! //! ## Response: [EndPoll] @@ -41,11 +41,11 @@ //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; //! let request = end_poll::EndPollRequest::new(); -//! let body = end_poll::EndPollBody::builder() -//! .broadcaster_id("274637212") -//! .id("92af127c-7326-4483-a52b-b0da0be61c01") -//! .status(end_poll::PollStatus::Terminated) -//! .build(); +//! let body = end_poll::EndPollBody::new( +//! "274637212", +//! "92af127c-7326-4483-a52b-b0da0be61c01", +//! end_poll::PollStatus::Terminated, +//! ); //! let response: end_poll::EndPoll = client.req_patch(request, body, &token).await?.data; //! # Ok(()) //! # } @@ -54,6 +54,8 @@ //! You can also get the [`http::Request`] with [`request.create_request(&token, &client_id)`](helix::RequestPost::create_request) //! and parse the [`http::Response`] with [`EndPollRequest::parse_response(None, &request.get_uri(), response)`](EndPollRequest::parse_response) +use std::marker::PhantomData; + use crate::helix::{parse_json, HelixRequestPatchError}; use super::*; @@ -65,11 +67,14 @@ pub use types::PollStatus; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug, Default)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct EndPollRequest {} +pub struct EndPollRequest<'a> { + #[serde(skip)] + _marker: PhantomData<&'a ()>, +} -impl EndPollRequest { +impl EndPollRequest<'_> { /// Make a new [`EndPollRequest`] - pub fn new() -> Self { Self {} } + pub fn new() -> Self { Self::default() } } /// Body Parameters for [End Poll](super::end_poll) @@ -78,13 +83,15 @@ impl EndPollRequest { #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct EndPollBody { +pub struct EndPollBody<'a> { /// The broadcaster running polls. Provided broadcaster_id must match the user_id in the user OAuth token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// ID of the poll. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub id: types::PollId, + #[serde(borrow)] + pub id: Cow<'a, types::PollIdRef>, /// The poll status to be set. /// /// Valid values: @@ -93,22 +100,22 @@ pub struct EndPollBody { pub status: PollStatus, } -impl EndPollBody { +impl<'a> EndPollBody<'a> { /// End a poll that is currently active. pub fn new( - broadcaster_id: impl Into, - id: impl Into, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + id: impl types::IntoCow<'a, types::PollIdRef> + 'a, status: PollStatus, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - id: id.into(), + broadcaster_id: broadcaster_id.to_cow(), + id: id.to_cow(), status, } } } -impl helix::private::SealedSerialize for EndPollBody {} +impl helix::private::SealedSerialize for EndPollBody<'_> {} /// Return Values for [Update CustomReward](super::end_poll) /// @@ -126,7 +133,7 @@ pub enum EndPoll { AuthFailed, } -impl Request for EndPollRequest { +impl Request for EndPollRequest<'_> { type Response = EndPoll; const PATH: &'static str = "polls"; @@ -134,8 +141,8 @@ impl Request for EndPollRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelManagePolls]; } -impl RequestPatch for EndPollRequest { - type Body = EndPollBody; +impl<'a> RequestPatch for EndPollRequest<'a> { + type Body = EndPollBody<'a>; fn parse_inner_response( request: Option, diff --git a/src/helix/endpoints/polls/get_polls.rs b/src/helix/endpoints/polls/get_polls.rs index ff9121858b..c1581c936a 100644 --- a/src/helix/endpoints/polls/get_polls.rs +++ b/src/helix/endpoints/polls/get_polls.rs @@ -7,10 +7,8 @@ //! //! ```rust //! use twitch_api::helix::polls::get_polls; -//! let request = get_polls::GetPollsRequest::builder() -//! .id(vec!["ed961efd-8a3f-4cf5-a9d0-e616c590cd2a".into()]) -//! .broadcaster_id("1234") -//! .build(); +//! let request = get_polls::GetPollsRequest::broadcaster_id("1234") +//! .ids(vec!["ed961efd-8a3f-4cf5-a9d0-e616c590cd2a".into()]); //! ``` //! //! ## Response: [Poll] @@ -25,10 +23,8 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_polls::GetPollsRequest::builder() -//! .id(vec!["ed961efd-8a3f-4cf5-a9d0-e616c590cd2a".into()]) -//! .broadcaster_id("1234") -//! .build(); +//! let request = get_polls::GetPollsRequest::broadcaster_id("1234") +//! .ids(vec!["ed961efd-8a3f-4cf5-a9d0-e616c590cd2a".into()]); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -47,41 +43,37 @@ pub use types::{PollChoice, PollStatus}; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetPollsRequest { +pub struct GetPollsRequest<'a> { /// The broadcaster running polls. Provided broadcaster_id must match the user_id in the user OAuth token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// ID of a poll. Filters results to one or more specific polls. Not providing one or more IDs will return the full list of polls for the authenticated channel. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub id: Vec, + #[serde(borrow)] + pub id: Cow<'a, [&'a types::PollIdRef]>, /// Cursor for forward pagination #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub after: Option, + pub after: Option>, /// Maximum number of objects to return. Maximum: 20. Default: 20. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub first: Option, } -impl GetPollsRequest { +impl<'a> GetPollsRequest<'a> { /// The broadcaster running polls. - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), id: Default::default(), after: Default::default(), first: Default::default(), } } - /// ID of a poll to query. - pub fn id(mut self, id: impl Into) -> Self { - self.id = vec![id.into()]; - self - } - /// IDs of the polls to query. - pub fn ids(mut self, id: impl IntoIterator>) -> Self { - self.id = id.into_iter().map(Into::into).collect(); + pub fn ids(mut self, id: impl Into>) -> Self { + self.id = id.into(); self } } @@ -125,7 +117,7 @@ pub struct Poll { pub ended_at: Option, } -impl Request for GetPollsRequest { +impl Request for GetPollsRequest<'_> { type Response = Vec; const PATH: &'static str = "polls"; @@ -133,18 +125,20 @@ impl Request for GetPollsRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelReadPolls]; } -impl RequestGet for GetPollsRequest {} +impl RequestGet for GetPollsRequest<'_> {} -impl helix::Paginated for GetPollsRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor; } +impl helix::Paginated for GetPollsRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } #[cfg(test)] #[test] fn test_request() { use helix::*; - let req = - GetPollsRequest::broadcaster_id("141981764").id("ed961efd-8a3f-4cf5-a9d0-e616c590cd2a"); + let req = GetPollsRequest::broadcaster_id("141981764") + .ids(vec!["ed961efd-8a3f-4cf5-a9d0-e616c590cd2a".into()]); // From twitch docs let data = br#" diff --git a/src/helix/endpoints/polls/mod.rs b/src/helix/endpoints/polls/mod.rs index 7e43161414..2f4bdf2f79 100644 --- a/src/helix/endpoints/polls/mod.rs +++ b/src/helix/endpoints/polls/mod.rs @@ -6,6 +6,7 @@ use crate::{ types, }; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; pub mod create_poll; pub mod end_poll; diff --git a/src/helix/endpoints/predictions/create_prediction.rs b/src/helix/endpoints/predictions/create_prediction.rs index 8c616793a7..240f162424 100644 --- a/src/helix/endpoints/predictions/create_prediction.rs +++ b/src/helix/endpoints/predictions/create_prediction.rs @@ -20,15 +20,12 @@ //! //! ``` //! # use twitch_api::helix::predictions::create_prediction; -//! let body = create_prediction::CreatePredictionBody::builder() -//! .broadcaster_id("141981764") -//! .title("Any leeks in the stream?") -//! .outcomes(create_prediction::NewPredictionOutcome::new_tuple( -//! "Yes, give it time.", -//! "Definitely not.", -//! )) -//! .prediction_window(120) -//! .build(); +//! let body = create_prediction::CreatePredictionBody::new( +//! "141981764", +//! "Any leeks in the stream?", +//! create_prediction::NewPredictionOutcome::new_tuple("Yes, give it time.", "Definitely not."), +//! 120, +//! ); //! ``` //! //! ## Response: [CreatePredictionResponse] @@ -45,14 +42,13 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = create_prediction::CreatePredictionRequest::builder() -//! .build(); -//! let body = create_prediction::CreatePredictionBody::builder() -//! .broadcaster_id("141981764") -//! .title("Any leeks in the stream?") -//! .outcomes(create_prediction::NewPredictionOutcome::new_tuple("Yes, give it time.", "Definitely not.")) -//! .prediction_window(120) -//! .build(); +//! let request = create_prediction::CreatePredictionRequest::new(); +//! let body = create_prediction::CreatePredictionBody::new( +//! "141981764", +//! "Any leeks in the stream?", +//! create_prediction::NewPredictionOutcome::new_tuple("Yes, give it time.", "Definitely not."), +//! 120, +//! ); //! let response: create_prediction::CreatePredictionResponse = client.req_post(request, body, &token).await?.data; //! # Ok(()) //! # } @@ -61,6 +57,8 @@ //! You can also get the [`http::Request`] with [`request.create_request(&token, &client_id)`](helix::RequestPost::create_request) //! and parse the [`http::Response`] with [`CreatePredictionRequest::parse_response(None, &request.get_uri(), response)`](CreatePredictionRequest::parse_response) +use std::marker::PhantomData; + use super::*; use helix::RequestPost; @@ -70,11 +68,15 @@ use helix::RequestPost; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug, Default)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct CreatePredictionRequest {} +pub struct CreatePredictionRequest<'a> { + #[cfg_attr(feature = "typed-builder", builder(default))] + #[serde(skip)] + _marker: PhantomData<&'a ()>, +} -impl CreatePredictionRequest { +impl CreatePredictionRequest<'_> { /// Create a new [`CreatePredictionRequest`] - pub fn new() -> Self { Self {} } + pub fn new() -> Self { Self::default() } } /// Body Parameters for [Create Prediction](super::create_prediction) @@ -83,57 +85,60 @@ impl CreatePredictionRequest { #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct CreatePredictionBody { +pub struct CreatePredictionBody<'a> { /// The broadcaster running Predictions. Provided broadcaster_id must match the user_id in the user OAuth token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// Title for the Prediction. Maximum: 45 characters. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub title: String, + #[serde(borrow)] + pub title: Cow<'a, str>, /// Array of outcome objects with titles for the Prediction. Array size must be 2. - pub outcomes: (NewPredictionOutcome, NewPredictionOutcome), + pub outcomes: (NewPredictionOutcome<'a>, NewPredictionOutcome<'a>), /// Total duration for the Prediction (in seconds). Minimum: 1. Maximum: 1800. pub prediction_window: i64, } -impl CreatePredictionBody { +impl<'a> CreatePredictionBody<'a> { /// Create a Channel Points Prediction for a specific Twitch channel. pub fn new( - broadcaster_id: impl Into, - title: String, - outcomes: (NewPredictionOutcome, NewPredictionOutcome), + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + title: impl Into>, + outcomes: (NewPredictionOutcome<'a>, NewPredictionOutcome<'a>), prediction_window: i64, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - title, + broadcaster_id: broadcaster_id.to_cow(), + title: title.into(), outcomes, prediction_window, } } } -impl helix::private::SealedSerialize for CreatePredictionBody {} +impl helix::private::SealedSerialize for CreatePredictionBody<'_> {} /// Choice settings for a poll #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct NewPredictionOutcome { +pub struct NewPredictionOutcome<'a> { /// Text displayed for the choice. Maximum: 25 characters. - pub title: String, + #[serde(borrow)] + pub title: Cow<'a, str>, } -impl NewPredictionOutcome { +impl<'a> NewPredictionOutcome<'a> { /// Create a new [`NewPredictionOutcome`] - pub fn new(title: impl Into) -> Self { + pub fn new(title: impl Into>) -> Self { Self { title: title.into(), } } /// Create a two new [`NewPredictionOutcome`]s - pub fn new_tuple(blue: impl Into, pink: impl Into) -> (Self, Self) { + pub fn new_tuple(blue: impl Into>, pink: impl Into>) -> (Self, Self) { (Self::new(blue), Self::new(pink)) } } @@ -143,7 +148,7 @@ impl NewPredictionOutcome { /// [`create-prediction`](https://dev.twitch.tv/docs/api/reference#create-prediction) pub type CreatePredictionResponse = super::Prediction; -impl Request for CreatePredictionRequest { +impl Request for CreatePredictionRequest<'_> { type Response = CreatePredictionResponse; const PATH: &'static str = "predictions"; @@ -152,8 +157,8 @@ impl Request for CreatePredictionRequest { &[twitch_oauth2::Scope::ChannelManagePredictions]; } -impl RequestPost for CreatePredictionRequest { - type Body = CreatePredictionBody; +impl<'a> RequestPost for CreatePredictionRequest<'a> { + type Body = CreatePredictionBody<'a>; fn parse_inner_response( request: Option, @@ -199,7 +204,7 @@ fn test_request() { let body = CreatePredictionBody::new( "141981764", - "Any leeks in the stream?".to_owned(), + "Any leeks in the stream?", NewPredictionOutcome::new_tuple("Yes, give it time.", "Definitely not."), 120, ); diff --git a/src/helix/endpoints/predictions/end_prediction.rs b/src/helix/endpoints/predictions/end_prediction.rs index 5eed4cb268..0514c91f0c 100644 --- a/src/helix/endpoints/predictions/end_prediction.rs +++ b/src/helix/endpoints/predictions/end_prediction.rs @@ -54,6 +54,8 @@ //! You can also get the [`http::Request`] with [`request.create_request(&token, &client_id)`](helix::RequestPost::create_request) //! and parse the [`http::Response`] with [`EndPredictionRequest::parse_response(None, &request.get_uri(), response)`](EndPredictionRequest::parse_response) +use std::marker::PhantomData; + use crate::helix::{parse_json, HelixRequestPatchError}; use super::*; @@ -64,11 +66,14 @@ use helix::RequestPatch; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug, Default)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct EndPredictionRequest {} +pub struct EndPredictionRequest<'a> { + #[serde(skip)] + _marker: PhantomData<&'a ()>, +} -impl EndPredictionRequest { +impl EndPredictionRequest<'_> { /// Make a new [`EndPredictionRequest`] - pub fn new() -> Self { Self {} } + pub fn new() -> Self { Self::default() } } /// Body Parameters for [End Prediction](super::end_prediction) @@ -77,13 +82,15 @@ impl EndPredictionRequest { #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct EndPredictionBody { +pub struct EndPredictionBody<'a> { /// The broadcaster running predictions. Provided broadcaster_id must match the user_id in the user OAuth token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// ID of the prediction. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub id: types::PredictionId, + #[serde(borrow)] + pub id: Cow<'a, types::PredictionIdRef>, /// The Prediction status to be set. Valid values: /// /// [`RESOLVED`](types::PredictionStatus): A winning outcome has been chosen and the Channel Points have been distributed to the users who predicted the correct outcome. @@ -92,19 +99,20 @@ pub struct EndPredictionBody { pub status: types::PredictionStatus, /// ID of the winning outcome for the Prediction. This parameter is required if status is being set to [`RESOLVED`](types::PredictionStatus). #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub winning_outcome_id: Option, + #[serde(borrow)] + pub winning_outcome_id: Option>, } -impl EndPredictionBody { +impl<'a> EndPredictionBody<'a> { /// End given prediction that is currently active. pub fn new( - broadcaster_id: impl Into, - id: impl Into, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + id: impl types::IntoCow<'a, types::PredictionIdRef> + 'a, status: impl Into, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - id: id.into(), + broadcaster_id: broadcaster_id.to_cow(), + id: id.to_cow(), status: status.into(), winning_outcome_id: None, } @@ -115,14 +123,14 @@ impl EndPredictionBody { /// This parameter is required if status is being set to [`RESOLVED`](types::PredictionStatus). pub fn winning_outcome_id( mut self, - winning_outcome_id: impl Into, + winning_outcome_id: impl types::IntoCow<'a, types::PredictionIdRef> + 'a, ) -> Self { - self.winning_outcome_id = Some(winning_outcome_id.into()); + self.winning_outcome_id = Some(winning_outcome_id.to_cow()); self } } -impl helix::private::SealedSerialize for EndPredictionBody {} +impl helix::private::SealedSerialize for EndPredictionBody<'_> {} /// Return Values for [Update CustomReward](super::end_prediction) /// @@ -140,7 +148,7 @@ pub enum EndPrediction { AuthFailed, } -impl Request for EndPredictionRequest { +impl Request for EndPredictionRequest<'_> { type Response = EndPrediction; const PATH: &'static str = "predictions"; @@ -149,8 +157,8 @@ impl Request for EndPredictionRequest { &[twitch_oauth2::Scope::ChannelManagePredictions]; } -impl RequestPatch for EndPredictionRequest { - type Body = EndPredictionBody; +impl<'a> RequestPatch for EndPredictionRequest<'a> { + type Body = EndPredictionBody<'a>; fn parse_inner_response( request: Option, diff --git a/src/helix/endpoints/predictions/get_predictions.rs b/src/helix/endpoints/predictions/get_predictions.rs index 2a4f0cf485..b25c2357ee 100644 --- a/src/helix/endpoints/predictions/get_predictions.rs +++ b/src/helix/endpoints/predictions/get_predictions.rs @@ -3,14 +3,12 @@ //! //! ## Request: [GetPredictionsRequest] //! -//! To use this endpoint, construct a [`GetPredictionsRequest`] with the [`GetPredictionsRequest::builder()`] method. +//! To use this endpoint, construct a [`GetPredictionsRequest`] with the [`GetPredictionsRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::predictions::get_predictions; -//! let request = get_predictions::GetPredictionsRequest::builder() -//! .id(vec!["ed961efd-8a3f-4cf5-a9d0-e616c590cd2a".into()]) -//! .broadcaster_id("1234") -//! .build(); +//! let request = get_predictions::GetPredictionsRequest::broadcaster_id("1234") +//! .ids(vec!["ed961efd-8a3f-4cf5-a9d0-e616c590cd2a".into()]); //! ``` //! //! ## Response: [Prediction] @@ -25,10 +23,8 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_predictions::GetPredictionsRequest::builder() -//! .id(vec!["ed961efd-8a3f-4cf5-a9d0-e616c590cd2a".into()]) -//! .broadcaster_id("1234") -//! .build(); +//! let request = get_predictions::GetPredictionsRequest::broadcaster_id("1234") +//! .ids(vec!["ed961efd-8a3f-4cf5-a9d0-e616c590cd2a".into()]); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -47,44 +43,40 @@ pub use types::{PredictionOutcome, PredictionOutcomeId, PredictionStatus}; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetPredictionsRequest { +pub struct GetPredictionsRequest<'a> { /// The broadcaster running Predictions. Provided broadcaster_id must match the user_id in the user OAuth token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// ID of a Prediction. Filters results to one or more specific Predictions. /// Not providing one or more IDs will return the full list of Predictions for the authenticated channel. /// /// Maximum: 100 #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub id: Vec, + #[serde(borrow)] + pub id: Cow<'a, [&'a types::PredictionIdRef]>, /// Cursor for forward pagination #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub after: Option, + pub after: Option>, /// Maximum number of objects to return. Maximum: 20. Default: 20. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub first: Option, } -impl GetPredictionsRequest { +impl<'a> GetPredictionsRequest<'a> { /// Get information about predictions for this broadcasters channel. - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - id: Default::default(), + broadcaster_id: broadcaster_id.to_cow(), + id: Cow::Borrowed(&[]), after: Default::default(), first: Default::default(), } } - /// ID of a Prediction. - pub fn id(mut self, id: impl Into) -> Self { - self.id = vec![id.into()]; - self - } - /// IDs of a Predictions. - pub fn ids(mut self, ids: impl IntoIterator>) -> Self { - self.id = ids.into_iter().map(Into::into).collect(); + pub fn ids(mut self, ids: impl Into>) -> Self { + self.id = ids.into(); self } } @@ -122,7 +114,7 @@ pub struct Prediction { pub locked_at: Option, } -impl Request for GetPredictionsRequest { +impl Request for GetPredictionsRequest<'_> { type Response = Vec; const PATH: &'static str = "predictions"; @@ -130,18 +122,21 @@ impl Request for GetPredictionsRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelReadPredictions]; } -impl RequestGet for GetPredictionsRequest {} +impl RequestGet for GetPredictionsRequest<'_> {} -impl helix::Paginated for GetPredictionsRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor; } +impl helix::Paginated for GetPredictionsRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } #[cfg(test)] #[test] fn test_request() { use helix::*; + let req = GetPredictionsRequest::broadcaster_id("55696719") - .id("d6676d5c-c86e-44d2-bfc4-100fb48f0656"); + .ids(vec!["d6676d5c-c86e-44d2-bfc4-100fb48f0656".into()]); // From twitch docs let data = br#" diff --git a/src/helix/endpoints/predictions/mod.rs b/src/helix/endpoints/predictions/mod.rs index 715a54c54e..5b58a8f011 100644 --- a/src/helix/endpoints/predictions/mod.rs +++ b/src/helix/endpoints/predictions/mod.rs @@ -5,6 +5,7 @@ use crate::{ types, }; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; pub mod create_prediction; pub mod end_prediction; diff --git a/src/helix/endpoints/raids/cancel_a_raid.rs b/src/helix/endpoints/raids/cancel_a_raid.rs index 4e3058cde0..1851ccd937 100644 --- a/src/helix/endpoints/raids/cancel_a_raid.rs +++ b/src/helix/endpoints/raids/cancel_a_raid.rs @@ -5,13 +5,11 @@ //! //! ## Request: [CancelARaidRequest] //! -//! To use this endpoint, construct a [`CancelARaidRequest`] with the [`CancelARaidRequest::builder()`] method. +//! To use this endpoint, construct a [`CancelARaidRequest`] with the [`CancelARaidRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::raids::cancel_a_raid; -//! let request = cancel_a_raid::CancelARaidRequest::builder() -//! .broadcaster_id("1234") -//! .build(); +//! let request = cancel_a_raid::CancelARaidRequest::broadcaster_id("1234"); //! ``` //! //! ## Response: [CancelARaidResponse] @@ -26,9 +24,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = cancel_a_raid::CancelARaidRequest::builder() -//! .broadcaster_id("1234") -//! .build(); +//! let request = cancel_a_raid::CancelARaidRequest::broadcaster_id("1234"); //! let response: cancel_a_raid::CancelARaidResponse = client.req_delete(request, &token).await?.data; //! # Ok(()) //! # } @@ -45,17 +41,18 @@ use helix::RequestDelete; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct CancelARaidRequest { +pub struct CancelARaidRequest<'a> { /// The ID of the broadcaster that sent the raiding party. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, } -impl CancelARaidRequest { +impl<'a> CancelARaidRequest<'a> { /// Cancel a pending raid on this broadcasters channel - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), } } } @@ -70,7 +67,7 @@ pub enum CancelARaidResponse { Success, } -impl Request for CancelARaidRequest { +impl Request for CancelARaidRequest<'_> { type Response = CancelARaidResponse; #[cfg(feature = "twitch_oauth2")] @@ -80,7 +77,7 @@ impl Request for CancelARaidRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelManageRaids]; } -impl RequestDelete for CancelARaidRequest { +impl RequestDelete for CancelARaidRequest<'_> { fn parse_inner_response( request: Option, uri: &http::Uri, diff --git a/src/helix/endpoints/raids/mod.rs b/src/helix/endpoints/raids/mod.rs index b5af0a66b0..c211a295c6 100644 --- a/src/helix/endpoints/raids/mod.rs +++ b/src/helix/endpoints/raids/mod.rs @@ -5,6 +5,7 @@ use crate::{ types, }; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; pub mod cancel_a_raid; pub mod start_a_raid; diff --git a/src/helix/endpoints/raids/start_a_raid.rs b/src/helix/endpoints/raids/start_a_raid.rs index 742ac79e9a..a29b914974 100644 --- a/src/helix/endpoints/raids/start_a_raid.rs +++ b/src/helix/endpoints/raids/start_a_raid.rs @@ -43,24 +43,26 @@ use helix::RequestPost; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct StartARaidRequest { +pub struct StartARaidRequest<'a> { /// The ID of the broadcaster that’s sending the raiding party. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - from_broadcaster_id: types::UserId, + #[serde(borrow)] + from_broadcaster_id: Cow<'a, types::UserIdRef>, /// The ID of the broadcaster to raid. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - to_broadcaster_id: types::UserId, + #[serde(borrow)] + to_broadcaster_id: Cow<'a, types::UserIdRef>, } -impl StartARaidRequest { +impl<'a> StartARaidRequest<'a> { /// Create a new [`StartARaidRequest`] pub fn new( - from_broadcaster_id: impl Into, - to_broadcaster_id: impl Into, + from_broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + to_broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, ) -> Self { Self { - from_broadcaster_id: from_broadcaster_id.into(), - to_broadcaster_id: to_broadcaster_id.into(), + from_broadcaster_id: from_broadcaster_id.to_cow(), + to_broadcaster_id: to_broadcaster_id.to_cow(), } } } @@ -77,7 +79,7 @@ pub struct StartARaidResponse { /// A Boolean value that indicates whether the channel being raided contains mature content. is_mature: bool, } -impl Request for StartARaidRequest { +impl Request for StartARaidRequest<'_> { type Response = StartARaidResponse; const PATH: &'static str = "raids"; @@ -85,7 +87,7 @@ impl Request for StartARaidRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelManageRaids]; } -impl RequestPost for StartARaidRequest { +impl RequestPost for StartARaidRequest<'_> { type Body = helix::EmptyBody; fn parse_inner_response( diff --git a/src/helix/endpoints/schedule/create_channel_stream_schedule_segment.rs b/src/helix/endpoints/schedule/create_channel_stream_schedule_segment.rs index ba3f52bead..4ffd3b23e6 100644 --- a/src/helix/endpoints/schedule/create_channel_stream_schedule_segment.rs +++ b/src/helix/endpoints/schedule/create_channel_stream_schedule_segment.rs @@ -5,15 +5,12 @@ //! //! ## Request: [CreateChannelStreamScheduleSegmentRequest] //! -//! To use this endpoint, construct a [`CreateChannelStreamScheduleSegmentRequest`] with the [`CreateChannelStreamScheduleSegmentRequest::builder()`] method. +//! To use this endpoint, construct a [`CreateChannelStreamScheduleSegmentRequest`] with the [`CreateChannelStreamScheduleSegmentRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::schedule::create_channel_stream_schedule_segment; //! let request = -//! create_channel_stream_schedule_segment::CreateChannelStreamScheduleSegmentRequest::builder( -//! ) -//! .broadcaster_id("141981764") -//! .build(); +//! create_channel_stream_schedule_segment::CreateChannelStreamScheduleSegmentRequest::broadcaster_id("141981764"); //! ``` //! //! ## Body: [CreateChannelStreamScheduleSegmentBody] @@ -23,15 +20,15 @@ //! ``` //! # use std::convert::TryFrom; //! # use twitch_api::helix::schedule::create_channel_stream_schedule_segment; -//! let body = -//! create_channel_stream_schedule_segment::CreateChannelStreamScheduleSegmentBody::builder() -//! .start_time(twitch_api::types::Timestamp::try_from("2021-07-01T18:00:00Z").unwrap()) -//! .timezone("America/New_York") -//! .is_recurring(false) -//! .duration("60".to_string()) -//! .category_id(Some("509670".into())) -//! .title("TwitchDev Monthly Update // July 1, 2021".to_string()) -//! .build(); +//! let mut body = +//! create_channel_stream_schedule_segment::CreateChannelStreamScheduleSegmentBody::new( +//! twitch_api::types::Timestamp::try_from("2021-07-01T18:00:00Z").unwrap(), +//! "America/New_York", +//! false, +//! ); +//! body.duration = Some("60".into()); +//! body.category_id = Some(twitch_types::CategoryIdRef::from_static("509670").as_cow()); +//! body.title = Some("TwitchDev Monthly Update // July 1, 2021".into()); //! ``` //! //! ## Response: [ScheduledBroadcasts] @@ -49,17 +46,18 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = create_channel_stream_schedule_segment::CreateChannelStreamScheduleSegmentRequest::builder() -//! .broadcaster_id("141981764") -//! .build(); -//! let body = create_channel_stream_schedule_segment::CreateChannelStreamScheduleSegmentBody::builder() -//! .start_time(twitch_api::types::Timestamp::try_from("2021-07-01T18:00:00Z")?) -//! .timezone("America/New_York") -//! .is_recurring(false) -//! .duration("60".to_string()) -//! .category_id(Some("509670".into())) -//! .title("TwitchDev Monthly Update // July 1, 2021".to_string()) -//! .build(); +//! let request = +//! create_channel_stream_schedule_segment::CreateChannelStreamScheduleSegmentRequest::broadcaster_id("141981764"); +//! let timestamp = twitch_api::types::Timestamp::try_from("2021-07-01T18:00:00Z")?; +//! let mut body = +//! create_channel_stream_schedule_segment::CreateChannelStreamScheduleSegmentBody::new( +//! twitch_api::types::Timestamp::try_from("2021-07-01T18:00:00Z").unwrap(), +//! "America/New_York", +//! false, +//! ); +//! body.duration = Some("60".into()); +//! body.category_id = Some(twitch_types::CategoryIdRef::from_static("509670").as_cow()); +//! body.title = Some("TwitchDev Monthly Update // July 1, 2021".into()); //! let response: create_channel_stream_schedule_segment::CreateChannelStreamScheduleSegmentResponse = client.req_post(request, body, &token).await?.data; //! # Ok(()) //! # } @@ -76,17 +74,18 @@ use helix::RequestPost; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct CreateChannelStreamScheduleSegmentRequest { +pub struct CreateChannelStreamScheduleSegmentRequest<'a> { /// User ID of the broadcaster who owns the channel streaming schedule. Provided broadcaster_id must match the user_id in the user OAuth token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, } -impl CreateChannelStreamScheduleSegmentRequest { +impl<'a> CreateChannelStreamScheduleSegmentRequest<'a> { /// Create a single scheduled broadcast or a recurring scheduled broadcast for a channel’s [stream schedule](https://help.twitch.tv/s/article/channel-page-setup#Schedule). - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), } } } @@ -97,40 +96,42 @@ impl CreateChannelStreamScheduleSegmentRequest { #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct CreateChannelStreamScheduleSegmentBody { +pub struct CreateChannelStreamScheduleSegmentBody<'a> { /// Start time for the scheduled broadcast specified in RFC3339 format. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub start_time: types::Timestamp, + #[serde(borrow)] + pub start_time: Cow<'a, types::TimestampRef>, // FIXME: specific braid? /// The timezone of the application creating the scheduled broadcast using the IANA time zone database format. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub timezone: String, + #[serde(borrow)] + pub timezone: Cow<'a, str>, /// Indicates if the scheduled broadcast is recurring weekly. pub is_recurring: bool, /// Duration of the scheduled broadcast in minutes from the start_time. Default: 240. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - #[serde(skip_serializing_if = "Option::is_none")] - pub duration: Option, + #[serde(skip_serializing_if = "Option::is_none", borrow)] + pub duration: Option>, /// Game/Category ID for the scheduled broadcast. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - #[serde(skip_serializing_if = "Option::is_none")] - pub category_id: Option, + #[serde(skip_serializing_if = "Option::is_none", borrow)] + pub category_id: Option>, /// Title for the scheduled broadcast. Maximum: 140 characters. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - #[serde(skip_serializing_if = "Option::is_none")] - pub title: Option, + #[serde(skip_serializing_if = "Option::is_none", borrow)] + pub title: Option>, } -impl CreateChannelStreamScheduleSegmentBody { +impl<'a> CreateChannelStreamScheduleSegmentBody<'a> { /// Create a single scheduled broadcast or a recurring scheduled broadcast for a channel’s [stream schedule](https://help.twitch.tv/s/article/channel-page-setup#Schedule). pub fn new( - start_time: impl Into, - timezone: String, + start_time: impl types::IntoCow<'a, types::TimestampRef> + 'a, + timezone: impl Into>, is_recurring: bool, ) -> Self { Self { - start_time: start_time.into(), - timezone, + start_time: start_time.to_cow(), + timezone: timezone.into(), is_recurring, duration: Default::default(), category_id: Default::default(), @@ -139,14 +140,14 @@ impl CreateChannelStreamScheduleSegmentBody { } } -impl helix::private::SealedSerialize for CreateChannelStreamScheduleSegmentBody {} +impl helix::private::SealedSerialize for CreateChannelStreamScheduleSegmentBody<'_> {} /// Return Values for [Create Channel Stream Schedule Segment](super::create_channel_stream_schedule_segment) /// /// [`create-channel-stream-schedule-segment`](https://dev.twitch.tv/docs/api/reference#create-channel-stream-schedule-segment) pub type CreateChannelStreamScheduleSegmentResponse = ScheduledBroadcasts; -impl Request for CreateChannelStreamScheduleSegmentRequest { +impl Request for CreateChannelStreamScheduleSegmentRequest<'_> { type Response = CreateChannelStreamScheduleSegmentResponse; const PATH: &'static str = "schedule/segment"; @@ -154,8 +155,8 @@ impl Request for CreateChannelStreamScheduleSegmentRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelManageSchedule]; } -impl RequestPost for CreateChannelStreamScheduleSegmentRequest { - type Body = CreateChannelStreamScheduleSegmentBody; +impl<'a> RequestPost for CreateChannelStreamScheduleSegmentRequest<'a> { + type Body = CreateChannelStreamScheduleSegmentBody<'a>; } #[cfg(test)] @@ -166,15 +167,12 @@ fn test_request() { use helix::*; let req = CreateChannelStreamScheduleSegmentRequest::broadcaster_id("141981764"); + let ts = types::Timestamp::try_from("2021-07-01T18:00:00Z").unwrap(); let body = CreateChannelStreamScheduleSegmentBody { - duration: Some("60".to_string()), - category_id: Some("509670".into()), - title: Some("TwitchDev Monthly Update // July 1, 2021".to_string()), - ..CreateChannelStreamScheduleSegmentBody::new( - types::Timestamp::try_from("2021-07-01T18:00:00Z").unwrap(), - "America/New_York".to_owned(), - false, - ) + duration: Some("60".into()), + category_id: Some(types::IntoCow::to_cow("509670")), + title: Some("TwitchDev Monthly Update // July 1, 2021".into()), + ..CreateChannelStreamScheduleSegmentBody::new(&*ts, "America/New_York", false) }; dbg!(req.create_request(body, "token", "clientid").unwrap()); diff --git a/src/helix/endpoints/schedule/delete_channel_stream_schedule_segment.rs b/src/helix/endpoints/schedule/delete_channel_stream_schedule_segment.rs index c75224c7fa..33bbca5373 100644 --- a/src/helix/endpoints/schedule/delete_channel_stream_schedule_segment.rs +++ b/src/helix/endpoints/schedule/delete_channel_stream_schedule_segment.rs @@ -50,24 +50,26 @@ use helix::RequestDelete; #[derive(PartialEq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct DeleteChannelStreamScheduleSegmentRequest { +pub struct DeleteChannelStreamScheduleSegmentRequest<'a> { /// User ID of the broadcaster who owns the channel streaming schedule. Provided broadcaster_id must match the user_id in the user OAuth token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// The ID of the streaming segment to delete. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub id: types::StreamSegmentId, + #[serde(borrow)] + pub id: Cow<'a, types::StreamSegmentIdRef>, } -impl DeleteChannelStreamScheduleSegmentRequest { +impl<'a> DeleteChannelStreamScheduleSegmentRequest<'a> { /// Delete a single scheduled broadcast or a recurring scheduled broadcast for a channel’s [stream schedule](https://help.twitch.tv/s/article/channel-page-setup#Schedule). pub fn new( - broadcaster_id: impl Into, - id: impl Into, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + id: impl types::IntoCow<'a, types::StreamSegmentIdRef> + 'a, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - id: id.into(), + broadcaster_id: broadcaster_id.to_cow(), + id: id.to_cow(), } } } @@ -82,7 +84,7 @@ pub enum DeleteChannelStreamScheduleSegment { Success, } -impl Request for DeleteChannelStreamScheduleSegmentRequest { +impl Request for DeleteChannelStreamScheduleSegmentRequest<'_> { type Response = DeleteChannelStreamScheduleSegment; const PATH: &'static str = "schedule/segment"; @@ -90,7 +92,7 @@ impl Request for DeleteChannelStreamScheduleSegmentRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelManageSchedule]; } -impl RequestDelete for DeleteChannelStreamScheduleSegmentRequest { +impl RequestDelete for DeleteChannelStreamScheduleSegmentRequest<'_> { fn parse_inner_response( request: Option, uri: &http::Uri, diff --git a/src/helix/endpoints/schedule/get_channel_stream_schedule.rs b/src/helix/endpoints/schedule/get_channel_stream_schedule.rs index 0ba5f58496..4ee771441a 100644 --- a/src/helix/endpoints/schedule/get_channel_stream_schedule.rs +++ b/src/helix/endpoints/schedule/get_channel_stream_schedule.rs @@ -6,13 +6,12 @@ //! See also [`get_channel_schedule`](helix::HelixClient::get_channel_schedule) //! ## Request: [GetChannelStreamScheduleRequest] //! -//! To use this endpoint, construct a [`GetChannelStreamScheduleRequest`] with the [`GetChannelStreamScheduleRequest::builder()`] method. +//! To use this endpoint, construct a [`GetChannelStreamScheduleRequest`] with the [`GetChannelStreamScheduleRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::schedule::get_channel_stream_schedule; -//! let request = get_channel_stream_schedule::GetChannelStreamScheduleRequest::builder() -//! .broadcaster_id("1234") -//! .build(); +//! let request = +//! get_channel_stream_schedule::GetChannelStreamScheduleRequest::broadcaster_id("1234"); //! ``` //! //! ## Response: [ScheduledBroadcasts] @@ -27,9 +26,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_channel_stream_schedule::GetChannelStreamScheduleRequest::builder() -//! .broadcaster_id("1234") -//! .build(); +//! let request = get_channel_stream_schedule::GetChannelStreamScheduleRequest::broadcaster_id("1234"); //! let response: helix::schedule::ScheduledBroadcasts = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -47,32 +44,36 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetChannelStreamScheduleRequest { +pub struct GetChannelStreamScheduleRequest<'a> { /// User ID of the broadcaster who owns the channel streaming schedule. Provided broadcaster_id must match the user_id in the user OAuth token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// The ID of the stream segment to return. Maximum: 100. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub id: Option, + #[serde(borrow)] + pub id: Option>, /// A timestamp in RFC3339 format to start returning stream segments from. If not specified, the current date and time is used. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub start_time: Option, + #[serde(borrow)] + pub start_time: Option>, /// A timezone offset for the requester specified in minutes. This is recommended to ensure stream segments are returned for the correct week. For example, a timezone that is +4 hours from GMT would be “240.” If not specified, “0” is used for GMT. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub utc_offset: Option, + #[serde(borrow)] + pub utc_offset: Option>, /// Cursor for forward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub after: Option, + pub after: Option>, /// Maximum number of stream segments to return. Maximum: 25. Default: 20. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub first: Option, } -impl GetChannelStreamScheduleRequest { +impl<'a> GetChannelStreamScheduleRequest<'a> { /// Get a broadcasters schedule - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), id: Default::default(), start_time: Default::default(), utc_offset: Default::default(), @@ -82,19 +83,22 @@ impl GetChannelStreamScheduleRequest { } /// Set the id for the request. - pub fn id(mut self, id: impl Into) -> Self { - self.id = Some(id.into()); + pub fn id(mut self, id: impl types::IntoCow<'a, types::StreamSegmentIdRef> + 'a) -> Self { + self.id = Some(id.to_cow()); self } /// Set the start_time for the request. - pub fn start_time(mut self, start_time: impl Into) -> Self { - self.start_time = Some(start_time.into()); + pub fn start_time( + mut self, + start_time: impl types::IntoCow<'a, types::TimestampRef> + 'a, + ) -> Self { + self.start_time = Some(start_time.to_cow()); self } /// Set the utc_offset for the request. - pub fn utc_offset(mut self, utc_offset: impl Into) -> Self { + pub fn utc_offset(mut self, utc_offset: impl Into>) -> Self { self.utc_offset = Some(utc_offset.into()); self } @@ -111,7 +115,7 @@ impl GetChannelStreamScheduleRequest { /// [`get-channel-stream-schedule`](https://dev.twitch.tv/docs/api/reference#get-channel-stream-schedule) pub type GetChannelStreamScheduleResponse = ScheduledBroadcasts; -impl Request for GetChannelStreamScheduleRequest { +impl Request for GetChannelStreamScheduleRequest<'_> { type Response = ScheduledBroadcasts; const PATH: &'static str = "schedule"; @@ -119,10 +123,12 @@ impl Request for GetChannelStreamScheduleRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetChannelStreamScheduleRequest {} +impl RequestGet for GetChannelStreamScheduleRequest<'_> {} -impl helix::Paginated for GetChannelStreamScheduleRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor; } +impl helix::Paginated for GetChannelStreamScheduleRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } #[cfg(test)] diff --git a/src/helix/endpoints/schedule/mod.rs b/src/helix/endpoints/schedule/mod.rs index 3abf3ebbbc..6bceba2725 100644 --- a/src/helix/endpoints/schedule/mod.rs +++ b/src/helix/endpoints/schedule/mod.rs @@ -4,6 +4,7 @@ use crate::{ types, }; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; pub mod create_channel_stream_schedule_segment; pub mod delete_channel_stream_schedule_segment; diff --git a/src/helix/endpoints/schedule/update_channel_stream_schedule.rs b/src/helix/endpoints/schedule/update_channel_stream_schedule.rs index 994381de6b..4c41b52448 100644 --- a/src/helix/endpoints/schedule/update_channel_stream_schedule.rs +++ b/src/helix/endpoints/schedule/update_channel_stream_schedule.rs @@ -10,9 +10,11 @@ //! //! ```rust //! use twitch_api::helix::schedule::update_channel_stream_schedule; -//! let request = update_channel_stream_schedule::UpdateChannelStreamScheduleRequest::builder() -//! .broadcaster_id("274637212") -//! .build(); +//! let mut request = +//! update_channel_stream_schedule::UpdateChannelStreamScheduleRequest::broadcaster_id( +//! "274637212", +//! ); +//! request.is_vacation_enabled = Some(true); //! ``` //! //! ## Response: [UpdateChannelStreamSchedule] @@ -29,10 +31,8 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = update_channel_stream_schedule::UpdateChannelStreamScheduleRequest::builder() -//! .broadcaster_id("274637212") -//! .is_vacation_enabled(false) -//! .build(); +//! let mut request = update_channel_stream_schedule::UpdateChannelStreamScheduleRequest::broadcaster_id("274637212"); +//! request.is_vacation_enabled = Some(true); //! let body = helix::EmptyBody; //! let response: update_channel_stream_schedule::UpdateChannelStreamSchedule = client.req_patch(request, body, &token).await?.data; //! # Ok(()) @@ -49,29 +49,33 @@ use helix::RequestPatch; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct UpdateChannelStreamScheduleRequest { +pub struct UpdateChannelStreamScheduleRequest<'a> { /// User ID of the broadcaster who owns the channel streaming schedule. Provided broadcaster_id must match the user_id in the user OAuth token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// Indicates if Vacation Mode is enabled. Set to true to add a vacation or false to remove vacation from the channel streaming schedule. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub is_vacation_enabled: Option, /// Start time for vacation specified in RFC3339 format. Required if is_vacation_enabled is set to true. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub vacation_start_time: Option, + #[serde(borrow)] + pub vacation_start_time: Option>, /// End time for vacation specified in RFC3339 format. Required if is_vacation_enabled is set to true. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub vacation_end_time: Option, + #[serde(borrow)] + pub vacation_end_time: Option>, /// The timezone for when the vacation is being scheduled using the IANA time zone database format. Required if is_vacation_enabled is set to true. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub timezone: Option, + #[serde(borrow)] + pub timezone: Option>, } -impl UpdateChannelStreamScheduleRequest { +impl<'a> UpdateChannelStreamScheduleRequest<'a> { /// Update the settings for a channel’s stream schedule. - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), is_vacation_enabled: Default::default(), vacation_start_time: Default::default(), vacation_end_time: Default::default(), @@ -91,7 +95,7 @@ pub enum UpdateChannelStreamSchedule { Success, } -impl Request for UpdateChannelStreamScheduleRequest { +impl Request for UpdateChannelStreamScheduleRequest<'_> { type Response = UpdateChannelStreamSchedule; const PATH: &'static str = "schedule/settings"; @@ -99,7 +103,7 @@ impl Request for UpdateChannelStreamScheduleRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelManageSchedule]; } -impl RequestPatch for UpdateChannelStreamScheduleRequest { +impl RequestPatch for UpdateChannelStreamScheduleRequest<'_> { type Body = helix::EmptyBody; fn parse_inner_response( @@ -137,13 +141,17 @@ impl RequestPatch for UpdateChannelStreamScheduleRequest { #[cfg(test)] #[test] fn test_request() { + use std::convert::TryFrom; + use helix::*; - use std::convert::TryInto; + + let start = types::Timestamp::try_from("2021-05-16T00:00:00Z").unwrap(); + let end = types::Timestamp::try_from("2021-05-23T00:00:00Z").unwrap(); let req = UpdateChannelStreamScheduleRequest { is_vacation_enabled: Some(true), - vacation_start_time: Some("2021-05-16T00:00:00Z".try_into().unwrap()), - vacation_end_time: Some("2021-05-23T00:00:00Z".try_into().unwrap()), - timezone: Some("America/New_York".to_string()), + vacation_start_time: Some(types::IntoCow::to_cow(&start)), + vacation_end_time: Some(types::IntoCow::to_cow(&end)), + timezone: Some("America/New_York".into()), ..UpdateChannelStreamScheduleRequest::broadcaster_id("141981764") }; diff --git a/src/helix/endpoints/schedule/update_channel_stream_schedule_segment.rs b/src/helix/endpoints/schedule/update_channel_stream_schedule_segment.rs index 6945b22993..96abca7cbc 100644 --- a/src/helix/endpoints/schedule/update_channel_stream_schedule_segment.rs +++ b/src/helix/endpoints/schedule/update_channel_stream_schedule_segment.rs @@ -5,14 +5,14 @@ //! //! ## Request: [UpdateChannelStreamScheduleSegmentRequest] //! -//! To use this endpoint, construct a [`UpdateChannelStreamScheduleSegmentRequest`] with the [`UpdateChannelStreamScheduleSegmentRequest::builder()`] method. +//! To use this endpoint, construct a [`UpdateChannelStreamScheduleSegmentRequest`] with the [`UpdateChannelStreamScheduleSegmentRequest::new()`] method. //! //! ```rust //! use twitch_api::helix::schedule::update_channel_stream_schedule_segment; -//! let request = update_channel_stream_schedule_segment::UpdateChannelStreamScheduleSegmentRequest::builder() -//! .broadcaster_id("141981764") -//! .id("eyJzZWdtZW50SUQiOiJlNGFjYzcyNC0zNzFmLTQwMmMtODFjYS0yM2FkYTc5NzU5ZDQiLCJpc29ZZWFyIjoyMDIxLCJpc29XZWVrIjoyNn0=") -//! .build(); +//! let request = update_channel_stream_schedule_segment::UpdateChannelStreamScheduleSegmentRequest::new( +//! "141981764", +//! "eyJzZWdtZW50SUQiOiJlNGFjYzcyNC0zNzFmLTQwMmMtODFjYS0yM2FkYTc5NzU5ZDQiLCJpc29ZZWFyIjoyMDIxLCJpc29XZWVrIjoyNn0=", +//! ); //! ``` //! //! ## Body: [UpdateChannelStreamScheduleSegmentBody] @@ -23,7 +23,7 @@ //! # use twitch_api::helix::schedule::update_channel_stream_schedule_segment; //! let body = //! update_channel_stream_schedule_segment::UpdateChannelStreamScheduleSegmentBody::builder() -//! .duration("120".to_string()) +//! .duration(Some("120".into())) //! .build(); //! ``` //! @@ -41,12 +41,12 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = update_channel_stream_schedule_segment::UpdateChannelStreamScheduleSegmentRequest::builder() -//! .broadcaster_id("141981764") -//! .id("eyJzZWdtZW50SUQiOiJlNGFjYzcyNC0zNzFmLTQwMmMtODFjYS0yM2FkYTc5NzU5ZDQiLCJpc29ZZWFyIjoyMDIxLCJpc29XZWVrIjoyNn0=") -//! .build(); +//! let request = update_channel_stream_schedule_segment::UpdateChannelStreamScheduleSegmentRequest::new( +//! "141981764", +//! "eyJzZWdtZW50SUQiOiJlNGFjYzcyNC0zNzFmLTQwMmMtODFjYS0yM2FkYTc5NzU5ZDQiLCJpc29ZZWFyIjoyMDIxLCJpc29XZWVrIjoyNn0=", +//! ); //! let body = update_channel_stream_schedule_segment::UpdateChannelStreamScheduleSegmentBody::builder() -//! .duration("120".to_string()) +//! .duration(Some("120".into())) //! .build(); //! let response: update_channel_stream_schedule_segment::UpdateChannelStreamScheduleSegmentResponse = client.req_patch(request, body, &token).await?.data; //! # Ok(()) @@ -64,24 +64,26 @@ use helix::RequestPatch; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct UpdateChannelStreamScheduleSegmentRequest { +pub struct UpdateChannelStreamScheduleSegmentRequest<'a> { /// User ID of the broadcaster who owns the channel streaming schedule. Provided broadcaster_id must match the user_id in the user OAuth token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// The ID of the streaming segment to update. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub id: types::StreamSegmentId, + #[serde(borrow)] + pub id: Cow<'a, types::StreamSegmentIdRef>, } -impl UpdateChannelStreamScheduleSegmentRequest { +impl<'a> UpdateChannelStreamScheduleSegmentRequest<'a> { /// Update a single scheduled broadcast or a recurring scheduled broadcast for a channel’s [stream schedule](https://help.twitch.tv/s/article/channel-page-setup#Schedule). pub fn new( - broadcaster_id: impl Into, - id: impl Into, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + id: impl types::IntoCow<'a, types::StreamSegmentIdRef> + 'a, ) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - id: id.into(), + broadcaster_id: broadcaster_id.to_cow(), + id: id.to_cow(), } } } @@ -92,23 +94,23 @@ impl UpdateChannelStreamScheduleSegmentRequest { #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug, Default)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct UpdateChannelStreamScheduleSegmentBody { +pub struct UpdateChannelStreamScheduleSegmentBody<'a> { /// Start time for the scheduled broadcast specified in RFC3339 format. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - #[serde(skip_serializing_if = "Option::is_none")] - pub start_time: Option, + #[serde(skip_serializing_if = "Option::is_none", borrow)] + pub start_time: Option>, /// Duration of the scheduled broadcast in minutes from the start_time. Default: 240. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - #[serde(skip_serializing_if = "Option::is_none")] - pub duration: Option, + #[serde(skip_serializing_if = "Option::is_none", borrow)] + pub duration: Option>, /// Game/Category ID for the scheduled broadcast. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - #[serde(skip_serializing_if = "Option::is_none")] - pub category_id: Option, + #[serde(skip_serializing_if = "Option::is_none", borrow)] + pub category_id: Option>, /// Title for the scheduled broadcast. Maximum: 140 characters. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - #[serde(skip_serializing_if = "Option::is_none")] - pub title: Option, + #[serde(skip_serializing_if = "Option::is_none", borrow)] + pub title: Option>, /// Indicated if the scheduled broadcast is canceled. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] #[serde(skip_serializing_if = "Option::is_none")] @@ -116,18 +118,18 @@ pub struct UpdateChannelStreamScheduleSegmentBody { // FIXME: Enum? /// The timezone of the application creating the scheduled broadcast using the IANA time zone database format. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - #[serde(skip_serializing_if = "Option::is_none")] - pub timezone: Option, + #[serde(skip_serializing_if = "Option::is_none", borrow)] + pub timezone: Option>, } -impl helix::private::SealedSerialize for UpdateChannelStreamScheduleSegmentBody {} +impl helix::private::SealedSerialize for UpdateChannelStreamScheduleSegmentBody<'_> {} /// Return Values for [Update Channel Stream Schedule Segment](super::update_channel_stream_schedule_segment) /// /// [`update-channel-stream-schedule-segment`](https://dev.twitch.tv/docs/api/reference#update-channel-stream-schedule-segment) pub type UpdateChannelStreamScheduleSegmentResponse = ScheduledBroadcasts; -impl Request for UpdateChannelStreamScheduleSegmentRequest { +impl Request for UpdateChannelStreamScheduleSegmentRequest<'_> { type Response = UpdateChannelStreamScheduleSegmentResponse; const PATH: &'static str = "schedule/segment"; @@ -135,8 +137,8 @@ impl Request for UpdateChannelStreamScheduleSegmentRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelManageSchedule]; } -impl RequestPatch for UpdateChannelStreamScheduleSegmentRequest { - type Body = UpdateChannelStreamScheduleSegmentBody; +impl<'a> RequestPatch for UpdateChannelStreamScheduleSegmentRequest<'a> { + type Body = UpdateChannelStreamScheduleSegmentBody<'a>; fn parse_inner_response( request: Option, @@ -175,7 +177,7 @@ fn test_request() { "eyJzZWdtZW50SUQiOiJlNGFjYzcyNC0zNzFmLTQwMmMtODFjYS0yM2FkYTc5NzU5ZDQiLCJpc29ZZWFyIjoyMDIxLCJpc29XZWVrIjoyNn0="); let body = UpdateChannelStreamScheduleSegmentBody { - duration: Some("120".to_string()), + duration: Some("120".into()), ..<_>::default() }; diff --git a/src/helix/endpoints/search/mod.rs b/src/helix/endpoints/search/mod.rs index 08bfe5430e..79f074bf87 100644 --- a/src/helix/endpoints/search/mod.rs +++ b/src/helix/endpoints/search/mod.rs @@ -22,6 +22,7 @@ use crate::{ types, }; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; pub mod search_categories; pub mod search_channels; diff --git a/src/helix/endpoints/search/search_categories.rs b/src/helix/endpoints/search/search_categories.rs index 8eb783356b..3eb66fb9a8 100644 --- a/src/helix/endpoints/search/search_categories.rs +++ b/src/helix/endpoints/search/search_categories.rs @@ -46,24 +46,26 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct SearchCategoriesRequest { +pub struct SearchCategoriesRequest<'a> { /// URI encoded search query #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub query: String, + #[serde(borrow)] + pub query: Cow<'a, str>, /// Cursor for forward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub after: Option, + pub after: Option>, /// Cursor for backward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub before: Option, + #[serde(borrow)] + pub before: Option>, /// Number of values to be returned per page. Limit: 100. Default: 20. #[cfg_attr(feature = "typed-builder", builder(setter(into), default))] pub first: Option, } -impl SearchCategoriesRequest { +impl<'a> SearchCategoriesRequest<'a> { /// Search categories with the following query. - pub fn query(query: impl Into) -> Self { + pub fn query(query: impl Into>) -> Self { Self { query: query.into(), after: None, @@ -84,7 +86,7 @@ impl SearchCategoriesRequest { /// [`search-categories`](https://dev.twitch.tv/docs/api/reference#search-categories) pub type Category = types::TwitchCategory; -impl Request for SearchCategoriesRequest { +impl Request for SearchCategoriesRequest<'_> { type Response = Vec; const PATH: &'static str = "search/categories"; @@ -92,7 +94,7 @@ impl Request for SearchCategoriesRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for SearchCategoriesRequest { +impl RequestGet for SearchCategoriesRequest<'_> { fn parse_inner_response( request: Option, uri: &http::Uri, @@ -121,8 +123,10 @@ impl RequestGet for SearchCategoriesRequest { } } -impl helix::Paginated for SearchCategoriesRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor } +impl helix::Paginated for SearchCategoriesRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } #[cfg(test)] diff --git a/src/helix/endpoints/search/search_channels.rs b/src/helix/endpoints/search/search_channels.rs index e313353578..c676621692 100644 --- a/src/helix/endpoints/search/search_channels.rs +++ b/src/helix/endpoints/search/search_channels.rs @@ -46,13 +46,14 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct SearchChannelsRequest { +pub struct SearchChannelsRequest<'a> { /// URL encoded search query #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub query: String, + #[serde(borrow)] + pub query: Cow<'a, str>, /// Cursor for forward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub after: Option, + pub after: Option>, /// Maximum number of objects to return. Maximum: 100 Default: 20 #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] // FIXME: No setter because int @@ -62,9 +63,9 @@ pub struct SearchChannelsRequest { pub live_only: Option, } -impl SearchChannelsRequest { +impl<'a> SearchChannelsRequest<'a> { /// Search channels with the following query. - pub fn query(query: impl Into) -> Self { + pub fn query(query: impl Into>) -> Self { Self { query: query.into(), after: None, @@ -122,7 +123,7 @@ pub struct Channel { pub tag_ids: Vec, } -impl Request for SearchChannelsRequest { +impl Request for SearchChannelsRequest<'_> { type Response = Vec; const PATH: &'static str = "search/channels"; @@ -130,10 +131,12 @@ impl Request for SearchChannelsRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for SearchChannelsRequest {} +impl RequestGet for SearchChannelsRequest<'_> {} -impl helix::Paginated for SearchChannelsRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor } +impl helix::Paginated for SearchChannelsRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } #[cfg(test)] diff --git a/src/helix/endpoints/streams/get_followed_streams.rs b/src/helix/endpoints/streams/get_followed_streams.rs index d9df296809..78a5773dc9 100644 --- a/src/helix/endpoints/streams/get_followed_streams.rs +++ b/src/helix/endpoints/streams/get_followed_streams.rs @@ -5,13 +5,11 @@ //! //! ## Request: [GetFollowedStreamsRequest] //! -//! To use this endpoint, construct a [`GetFollowedStreamsRequest`] with the [`GetFollowedStreamsRequest::builder()`] method. +//! To use this endpoint, construct a [`GetFollowedStreamsRequest`] with the [`GetFollowedStreamsRequest::user_id()`] method. //! //! ```rust //! use twitch_api::helix::streams::get_followed_streams; -//! let request = get_followed_streams::GetFollowedStreamsRequest::builder() -//! .user_id("1234") -//! .build(); +//! let request = get_followed_streams::GetFollowedStreamsRequest::user_id("1234"); //! ``` //! //! ## Response: [Stream] @@ -26,9 +24,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_followed_streams::GetFollowedStreamsRequest::builder() -//! .user_id("1234") -//! .build(); +//! let request = get_followed_streams::GetFollowedStreamsRequest::user_id("1234"); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -46,30 +42,32 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetFollowedStreamsRequest { +pub struct GetFollowedStreamsRequest<'a> { /// Returns streams broadcast by one or more specified user IDs. You can specify up to 100 IDs. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub user_id: types::UserId, + #[serde(borrow)] + pub user_id: Cow<'a, types::UserIdRef>, /// Cursor for forward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub after: Option, + pub after: Option>, /// Cursor for backward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub before: Option, + #[serde(borrow)] + pub before: Option>, /// Maximum number of objects to return. Maximum: 100. Default: 20. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub first: Option, } -impl GetFollowedStreamsRequest { +impl<'a> GetFollowedStreamsRequest<'a> { /// Get a users followed streams. /// /// Requires token with scope [`user:read:follows`](twitch_oauth2::Scope::UserReadFollows). /// /// See also [`HelixClient::get_followed_streams`](crate::helix::HelixClient::get_followed_streams). - pub fn user_id(user_id: impl Into) -> Self { + pub fn user_id(user_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - user_id: user_id.into(), + user_id: user_id.to_cow(), after: Default::default(), before: Default::default(), first: Default::default(), @@ -88,7 +86,7 @@ impl GetFollowedStreamsRequest { /// [`get-followed-streams`](https://dev.twitch.tv/docs/api/reference#get-followed-streams) pub type GetFollowedStreamsResponse = Stream; -impl Request for GetFollowedStreamsRequest { +impl Request for GetFollowedStreamsRequest<'_> { type Response = Vec; const PATH: &'static str = "streams/followed"; @@ -96,10 +94,12 @@ impl Request for GetFollowedStreamsRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::UserReadFollows]; } -impl RequestGet for GetFollowedStreamsRequest {} +impl RequestGet for GetFollowedStreamsRequest<'_> {} -impl helix::Paginated for GetFollowedStreamsRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor } +impl helix::Paginated for GetFollowedStreamsRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } #[cfg(test)] diff --git a/src/helix/endpoints/streams/get_stream_tags.rs b/src/helix/endpoints/streams/get_stream_tags.rs index 4791079457..1f65fb0033 100644 --- a/src/helix/endpoints/streams/get_stream_tags.rs +++ b/src/helix/endpoints/streams/get_stream_tags.rs @@ -5,13 +5,11 @@ //! //! ## Request: [GetStreamTagsRequest] //! -//! To use this endpoint, construct a [`GetStreamTagsRequest`] with the [`GetStreamTagsRequest::builder()`] method. +//! To use this endpoint, construct a [`GetStreamTagsRequest`] with the [`GetStreamTagsRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::streams::get_stream_tags; -//! let request = get_stream_tags::GetStreamTagsRequest::builder() -//! .broadcaster_id("1234") -//! .build(); +//! let request = get_stream_tags::GetStreamTagsRequest::broadcaster_id("1234"); //! ``` //! //! ## Response: [Tag](helix::tags::TwitchTag) @@ -26,9 +24,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_stream_tags::GetStreamTagsRequest::builder() -//! .broadcaster_id("1234") -//! .build(); +//! let request = get_stream_tags::GetStreamTagsRequest::broadcaster_id("1234"); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -46,18 +42,19 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetStreamTagsRequest { +pub struct GetStreamTagsRequest<'a> { // FIXME: twitch docs sucks /// ID of the stream whose tags are going to be fetched #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, } -impl GetStreamTagsRequest { +impl<'a> GetStreamTagsRequest<'a> { /// ID of the stream whose tags are going to be fetched - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), } } } @@ -67,7 +64,7 @@ impl GetStreamTagsRequest { /// [`get-stream-tags`](https://dev.twitch.tv/docs/api/reference#get-stream-tags) pub type Tag = helix::tags::TwitchTag; -impl Request for GetStreamTagsRequest { +impl Request for GetStreamTagsRequest<'_> { type Response = Vec; const PATH: &'static str = "streams/tags"; @@ -75,7 +72,7 @@ impl Request for GetStreamTagsRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetStreamTagsRequest {} +impl RequestGet for GetStreamTagsRequest<'_> {} #[cfg(test)] #[test] diff --git a/src/helix/endpoints/streams/get_streams.rs b/src/helix/endpoints/streams/get_streams.rs index 6773c2979f..b49b8c20d4 100644 --- a/src/helix/endpoints/streams/get_streams.rs +++ b/src/helix/endpoints/streams/get_streams.rs @@ -10,7 +10,7 @@ //! ```rust //! use twitch_api::helix::streams::get_streams; //! let request = get_streams::GetStreamsRequest::builder() -//! .user_login(vec!["justintvfan".into()]) +//! .user_login(&["justintvfan".into()][..]) //! .build(); //! ``` //! @@ -20,14 +20,16 @@ //! //! ```rust, no_run //! use twitch_api::helix::{self, streams::get_streams}; -//! # use twitch_api::client; +//! # use twitch_api::{client, types}; //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { -//! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); +//! let client: helix::HelixClient<'static, client::DummyHttpClient> = +//! helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; +//! let logins: &[&types::UserNameRef] = &["justintvfan".into()]; //! let request = get_streams::GetStreamsRequest::builder() -//! .user_login(vec!["justintvfan".into()]) +//! .user_login(logins) //! .build(); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) @@ -43,62 +45,60 @@ use helix::RequestGet; /// Query Parameters for [Get Streams](super::get_streams) /// /// [`get-streams`](https://dev.twitch.tv/docs/api/reference#get-streams) -#[derive(PartialEq, Deserialize, Serialize, Clone, Debug, Default)] +#[derive(PartialEq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetStreamsRequest { +pub struct GetStreamsRequest<'a> { /// Cursor for forward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub after: Option, + pub after: Option>, /// Cursor for backward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub before: Option, + #[serde(borrow)] + pub before: Option>, /// Maximum number of objects to return. Maximum: 100. Default: 20. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub first: Option, /// Returns streams broadcasting a specified game ID. You can specify up to 10 IDs. - #[cfg_attr(feature = "typed-builder", builder(default))] - pub game_id: Vec, + #[cfg_attr( + feature = "typed-builder", + builder(default_code = "Cow::Borrowed(&[])", setter(into)) + )] + #[serde(borrow)] + pub game_id: Cow<'a, [&'a types::CategoryIdRef]>, /// Stream language. You can specify up to 100 languages. #[cfg_attr(feature = "typed-builder", builder(default))] - pub language: Option, + #[serde(borrow)] + pub language: Option>, /// Returns streams broadcast by one or more specified user IDs. You can specify up to 100 IDs. - #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub user_id: Vec, + #[cfg_attr( + feature = "typed-builder", + builder(default_code = "Cow::Borrowed(&[])", setter(into)) + )] + #[serde(borrow)] + pub user_id: Cow<'a, [&'a types::UserIdRef]>, /// Returns streams broadcast by one or more specified user login names. You can specify up to 100 names. - #[cfg_attr(feature = "typed-builder", builder(default))] - pub user_login: Vec, + #[cfg_attr( + feature = "typed-builder", + builder(default_code = "Cow::Borrowed(&[])", setter(into)) + )] + #[serde(borrow)] + pub user_login: Cow<'a, [&'a types::UserNameRef]>, } -impl GetStreamsRequest { - /// Return stream for specified user id - pub fn user_id(user_id: impl Into) -> Self { - Self { - user_id: vec![user_id.into()], - ..Self::default() - } - } - +impl<'a> GetStreamsRequest<'a> { /// Return streams for specified user ids - pub fn user_ids(user_ids: impl IntoIterator>) -> Self { + pub fn user_ids(user_ids: impl Into>) -> Self { Self { - user_id: user_ids.into_iter().map(Into::into).collect(), - ..Self::default() - } - } - - /// Return stream for specified user by [nickname](types::UserName) - pub fn user_login(user_login: impl Into) -> Self { - Self { - user_login: vec![user_login.into()], + user_id: user_ids.into(), ..Self::default() } } /// Return streams for specified users by [nickname](types::UserName) - pub fn user_logins(user_logins: impl IntoIterator>) -> Self { + pub fn user_logins(user_logins: impl Into>) -> Self { Self { - user_login: user_logins.into_iter().map(Into::into).collect(), + user_login: user_logins.into(), ..Self::default() } } @@ -110,6 +110,20 @@ impl GetStreamsRequest { } } +impl Default for GetStreamsRequest<'_> { + fn default() -> Self { + Self { + after: None, + before: None, + first: None, + game_id: Cow::Borrowed(&[]), + language: None, + user_id: Cow::Borrowed(&[]), + user_login: Cow::Borrowed(&[]), + } + } +} + /// Return Values for [Get Streams](super::get_streams) /// /// [`get-streams`](https://dev.twitch.tv/docs/api/reference#get-streams) @@ -149,7 +163,7 @@ pub struct Stream { pub viewer_count: usize, } -impl Request for GetStreamsRequest { +impl Request for GetStreamsRequest<'_> { type Response = Vec; const PATH: &'static str = "streams"; @@ -157,10 +171,12 @@ impl Request for GetStreamsRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetStreamsRequest {} +impl RequestGet for GetStreamsRequest<'_> {} -impl helix::Paginated for GetStreamsRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor } +impl helix::Paginated for GetStreamsRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } #[cfg(test)] diff --git a/src/helix/endpoints/streams/mod.rs b/src/helix/endpoints/streams/mod.rs index b4c1c7dcb3..d2061f5ecc 100644 --- a/src/helix/endpoints/streams/mod.rs +++ b/src/helix/endpoints/streams/mod.rs @@ -4,16 +4,15 @@ //! # Examples //! //! ```rust,no_run -//! # use twitch_api::helix::{HelixClient, streams::GetStreamsRequest}; +//! # use twitch_api::{helix::{HelixClient, streams::GetStreamsRequest}, types}; //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { //! let client = HelixClient::new(); //! # let _: &HelixClient = &client; //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let req = GetStreamsRequest::builder() -//! .user_login(vec!["justinfan1337".into()]) -//! .build(); +//! let logins: &[&types::UserNameRef] = &["justinfan1337".into()]; +//! let req = GetStreamsRequest::builder().user_login(logins).build(); //! //! // If this doesn't return a result, that would mean the stream is not live. //! println!("{:?}", &client.req_get(req, &token).await?.data.get(0)); @@ -25,6 +24,7 @@ use crate::{ types, }; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; #[doc(inline)] pub use get_followed_streams::GetFollowedStreamsRequest; diff --git a/src/helix/endpoints/streams/replace_stream_tags.rs b/src/helix/endpoints/streams/replace_stream_tags.rs index 66b01ac765..3cd7349a9e 100644 --- a/src/helix/endpoints/streams/replace_stream_tags.rs +++ b/src/helix/endpoints/streams/replace_stream_tags.rs @@ -9,9 +9,7 @@ //! //! ```rust //! use twitch_api::helix::streams::replace_stream_tags; -//! let request = replace_stream_tags::ReplaceStreamTagsRequest::builder() -//! .broadcaster_id("1234") -//! .build(); +//! let request = replace_stream_tags::ReplaceStreamTagsRequest::broadcaster_id("1234"); //! ``` //! //! ## Body: [ReplaceStreamTagsBody] @@ -20,12 +18,10 @@ //! //! ``` //! # use twitch_api::helix::streams::replace_stream_tags; -//! let body = replace_stream_tags::ReplaceStreamTagsBody::builder() -//! .tag_ids(vec![ -//! "621fb5bf-5498-4d8f-b4ac-db4d40d401bf".into(), -//! "79977fb9-f106-4a87-a386-f1b0f99783dd".into(), -//! ]) -//! .build(); +//! let body = replace_stream_tags::ReplaceStreamTagsBody::tag_ids(vec![ +//! "621fb5bf-5498-4d8f-b4ac-db4d40d401bf".into(), +//! "79977fb9-f106-4a87-a386-f1b0f99783dd".into(), +//! ]); //! ``` //! //! ## Response: [ReplaceStreamTags] @@ -42,15 +38,11 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = replace_stream_tags::ReplaceStreamTagsRequest::builder() -//! .broadcaster_id("1234") -//! .build(); -//! let body = replace_stream_tags::ReplaceStreamTagsBody::builder() -//! .tag_ids(vec![ -//! "621fb5bf-5498-4d8f-b4ac-db4d40d401bf".into(), -//! "79977fb9-f106-4a87-a386-f1b0f99783dd".into(), -//! ]) -//! .build(); +//! let request = replace_stream_tags::ReplaceStreamTagsRequest::broadcaster_id("1234"); +//! let body = replace_stream_tags::ReplaceStreamTagsBody::tag_ids(vec![ +//! "621fb5bf-5498-4d8f-b4ac-db4d40d401bf".into(), +//! "79977fb9-f106-4a87-a386-f1b0f99783dd".into(), +//! ]); //! let response: replace_stream_tags::ReplaceStreamTags = client.req_put(request, body, &token).await?.data; //! # Ok(()) //! # } @@ -67,17 +59,18 @@ use helix::RequestPut; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct ReplaceStreamTagsRequest { +pub struct ReplaceStreamTagsRequest<'a> { /// ID of the stream for which tags are to be replaced. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, } -impl ReplaceStreamTagsRequest { +impl<'a> ReplaceStreamTagsRequest<'a> { /// ID of the stream for which tags are to be replaced. - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), } } } @@ -92,25 +85,18 @@ impl ReplaceStreamTagsRequest { #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug, Default)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct ReplaceStreamTagsBody { +pub struct ReplaceStreamTagsBody<'a> { /// IDs of tags to be applied to the stream. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub tag_ids: Vec, + #[serde(borrow)] + pub tag_ids: Cow<'a, [&'a types::TagIdRef]>, } -impl ReplaceStreamTagsBody { +impl<'a> ReplaceStreamTagsBody<'a> { /// IDs of tags to be applied to the stream. - pub fn tag_ids(tag_ids: impl IntoIterator>) -> Self { + pub fn tag_ids(tag_ids: impl Into>) -> Self { Self { - tag_ids: tag_ids.into_iter().map(Into::into).collect(), - ..Self::default() - } - } - - /// ID of tag to be applied to the stream. - pub fn tag_id(tag_id: impl Into) -> Self { - Self { - tag_ids: vec![tag_id.into()], + tag_ids: tag_ids.into(), ..Self::default() } } @@ -125,9 +111,9 @@ pub enum ReplaceStreamTags { Success, } -impl helix::private::SealedSerialize for ReplaceStreamTagsBody {} +impl helix::private::SealedSerialize for ReplaceStreamTagsBody<'_> {} -impl Request for ReplaceStreamTagsRequest { +impl Request for ReplaceStreamTagsRequest<'_> { type Response = ReplaceStreamTags; const PATH: &'static str = "streams/tags"; @@ -135,8 +121,8 @@ impl Request for ReplaceStreamTagsRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelManageBroadcast]; } -impl RequestPut for ReplaceStreamTagsRequest { - type Body = ReplaceStreamTagsBody; +impl<'a> RequestPut for ReplaceStreamTagsRequest<'a> { + type Body = ReplaceStreamTagsBody<'a>; fn parse_inner_response( request: Option, @@ -171,10 +157,11 @@ fn test_request() { use helix::*; let req = ReplaceStreamTagsRequest::broadcaster_id("0"); - let body = ReplaceStreamTagsBody::tag_ids([ - "621fb5bf-5498-4d8f-b4ac-db4d40d401bf", - "79977fb9-f106-4a87-a386-f1b0f99783dd", - ]); + let ids: &[&types::TagIdRef] = &[ + "621fb5bf-5498-4d8f-b4ac-db4d40d401bf".into(), + "79977fb9-f106-4a87-a386-f1b0f99783dd".into(), + ]; + let body = ReplaceStreamTagsBody::tag_ids(ids); dbg!(req.create_request(body, "token", "clientid").unwrap()); // From twitch docs diff --git a/src/helix/endpoints/subscriptions/check_user_subscription.rs b/src/helix/endpoints/subscriptions/check_user_subscription.rs index 591da087ae..55700bf5f9 100644 --- a/src/helix/endpoints/subscriptions/check_user_subscription.rs +++ b/src/helix/endpoints/subscriptions/check_user_subscription.rs @@ -5,13 +5,11 @@ //! //! ## Request: [CheckUserSubscriptionRequest] //! -//! To use this endpoint, construct a [`CheckUserSubscriptionRequest`] with the [`CheckUserSubscriptionRequest::builder()`] method. +//! To use this endpoint, construct a [`CheckUserSubscriptionRequest`] with the [`CheckUserSubscriptionRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::subscriptions::check_user_subscription; -//! let request = check_user_subscription::CheckUserSubscriptionRequest::builder() -//! .broadcaster_id("1234") -//! .build(); +//! let request = check_user_subscription::CheckUserSubscriptionRequest::broadcaster_id("1234"); //! ``` //! //! ## Response: [UserSubscription] @@ -26,9 +24,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = check_user_subscription::CheckUserSubscriptionRequest::builder() -//! .broadcaster_id("1234") -//! .build(); +//! let request = check_user_subscription::CheckUserSubscriptionRequest::broadcaster_id("1234"); //! let response: check_user_subscription::UserSubscription = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -45,36 +41,29 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct CheckUserSubscriptionRequest { +pub struct CheckUserSubscriptionRequest<'a> { /// User ID of the broadcaster. Must match the User ID in the Bearer token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// Unique identifier of account to get subscription status of. Accepts up to 100 values. #[cfg_attr(feature = "typed-builder", builder(default))] - pub user_id: Vec, + #[serde(borrow)] + pub user_id: Cow<'a, [&'a types::UserIdRef]>, } -impl CheckUserSubscriptionRequest { +impl<'a> CheckUserSubscriptionRequest<'a> { /// Checks subscribed users to this specific channel. - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - user_id: Default::default(), + broadcaster_id: broadcaster_id.to_cow(), + user_id: Cow::Borrowed(&[]), } } /// Filter the results for specific users. - pub fn user_ids( - mut self, - user_ids: impl IntoIterator>, - ) -> Self { - self.user_id = user_ids.into_iter().map(Into::into).collect(); - self - } - - /// Filter the results for specific user. - pub fn user_id(mut self, user_id: impl Into) -> Self { - self.user_id = vec![user_id.into()]; + pub fn user_ids(mut self, user_ids: impl Into>) -> Self { + self.user_id = user_ids.into(); self } } @@ -102,7 +91,7 @@ pub struct UserSubscription { pub tier: types::SubscriptionTier, } -impl Request for CheckUserSubscriptionRequest { +impl Request for CheckUserSubscriptionRequest<'_> { type Response = UserSubscription; const PATH: &'static str = "subscriptions/user"; @@ -110,7 +99,7 @@ impl Request for CheckUserSubscriptionRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::UserReadSubscriptions]; } -impl RequestGet for CheckUserSubscriptionRequest { +impl RequestGet for CheckUserSubscriptionRequest<'_> { fn parse_inner_response( request: Option, uri: &http::Uri, diff --git a/src/helix/endpoints/subscriptions/get_broadcaster_subscriptions.rs b/src/helix/endpoints/subscriptions/get_broadcaster_subscriptions.rs index 83f5ee3a90..24c4372037 100644 --- a/src/helix/endpoints/subscriptions/get_broadcaster_subscriptions.rs +++ b/src/helix/endpoints/subscriptions/get_broadcaster_subscriptions.rs @@ -5,13 +5,12 @@ //! //! ## Request: [GetBroadcasterSubscriptionsRequest] //! -//! To use this endpoint, construct a [`GetBroadcasterSubscriptionsRequest`] with the [`GetBroadcasterSubscriptionsRequest::builder()`] method. +//! To use this endpoint, construct a [`GetBroadcasterSubscriptionsRequest`] with the [`GetBroadcasterSubscriptionsRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::subscriptions::get_broadcaster_subscriptions; -//! let request = get_broadcaster_subscriptions::GetBroadcasterSubscriptionsRequest::builder() -//! .broadcaster_id("1234") -//! .build(); +//! let request = +//! get_broadcaster_subscriptions::GetBroadcasterSubscriptionsRequest::broadcaster_id("1234"); //! ``` //! //! ## Response: [BroadcasterSubscription] @@ -26,9 +25,8 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_broadcaster_subscriptions::GetBroadcasterSubscriptionsRequest::builder() -//! .broadcaster_id("1234") -//! .build(); +//! let request = +//! get_broadcaster_subscriptions::GetBroadcasterSubscriptionsRequest::broadcaster_id("1234"); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -39,44 +37,44 @@ use super::*; use helix::RequestGet; + /// Query Parameters for [Get Broadcaster Subscriptions](super::get_broadcaster_subscriptions) /// /// [`get-broadcaster-subscriptions`](https://dev.twitch.tv/docs/api/reference#get-broadcaster-subscriptions) #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetBroadcasterSubscriptionsRequest { +pub struct GetBroadcasterSubscriptionsRequest<'a> { /// User ID of the broadcaster. Must match the User ID in the Bearer token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// Unique identifier of account to get subscription status of. Accepts up to 100 values. #[cfg_attr(feature = "typed-builder", builder(default))] - pub user_id: Vec, + #[serde(borrow)] + pub user_id: Cow<'a, [&'a types::UserIdRef]>, /// Cursor for forward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub after: Option, + pub after: Option>, /// Number of values to be returned per page. Limit: 100. Default: 20. #[cfg_attr(feature = "typed-builder", builder(setter(into), default))] pub first: Option, } -impl GetBroadcasterSubscriptionsRequest { +impl<'a> GetBroadcasterSubscriptionsRequest<'a> { /// Get a broadcasters subscribers - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - user_id: Default::default(), + broadcaster_id: broadcaster_id.to_cow(), + user_id: Cow::Borrowed(&[]), after: Default::default(), first: Default::default(), } } /// check for specific users in broadcasters subscriptions - pub fn subscriber( - mut self, - user_ids: impl IntoIterator>, - ) -> Self { - self.user_id = user_ids.into_iter().map(Into::into).collect(); + pub fn subscriber(mut self, user_ids: impl Into>) -> Self { + self.user_id = user_ids.into(); self } @@ -132,7 +130,7 @@ pub struct BroadcasterSubscription { pub user_name: types::DisplayName, } -impl Request for GetBroadcasterSubscriptionsRequest { +impl Request for GetBroadcasterSubscriptionsRequest<'_> { type Response = Vec; const PATH: &'static str = "subscriptions"; @@ -141,13 +139,15 @@ impl Request for GetBroadcasterSubscriptionsRequest { &[twitch_oauth2::Scope::ChannelReadSubscriptions]; } -impl RequestGet for GetBroadcasterSubscriptionsRequest {} +impl RequestGet for GetBroadcasterSubscriptionsRequest<'_> {} -impl helix::Paginated for GetBroadcasterSubscriptionsRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor } +impl helix::Paginated for GetBroadcasterSubscriptionsRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } -impl helix::Response> { +impl helix::Response, Vec> { /// The current number of subscriber points earned by this broadcaster. pub fn points(&self) -> Result { let points = self.get_other("points")?; diff --git a/src/helix/endpoints/subscriptions/get_broadcaster_subscriptions_events.rs b/src/helix/endpoints/subscriptions/get_broadcaster_subscriptions_events.rs index 3100c4fb4c..99407a6b4b 100644 --- a/src/helix/endpoints/subscriptions/get_broadcaster_subscriptions_events.rs +++ b/src/helix/endpoints/subscriptions/get_broadcaster_subscriptions_events.rs @@ -10,14 +10,12 @@ //! //! ## Request: [GetBroadcasterSubscriptionsEventsRequest] //! -//! To use this endpoint, construct a [`GetBroadcasterSubscriptionsEventsRequest`] with the [`GetBroadcasterSubscriptionsEventsRequest::builder()`] method. +//! To use this endpoint, construct a [`GetBroadcasterSubscriptionsEventsRequest`] with the [`GetBroadcasterSubscriptionsEventsRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::subscriptions::get_broadcaster_subscriptions_events; //! let request = -//! get_broadcaster_subscriptions_events::GetBroadcasterSubscriptionsEventsRequest::builder() -//! .broadcaster_id("1234") -//! .build(); +//! get_broadcaster_subscriptions_events::GetBroadcasterSubscriptionsEventsRequest::broadcaster_id("1234"); //! ``` //! //! ## Response: [BroadcasterSubscriptionEvent] @@ -32,9 +30,8 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_broadcaster_subscriptions_events::GetBroadcasterSubscriptionsEventsRequest::builder() -//! .broadcaster_id("1234") -//! .build(); +//! let request = +//! get_broadcaster_subscriptions_events::GetBroadcasterSubscriptionsEventsRequest::broadcaster_id("1234"); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -53,31 +50,34 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetBroadcasterSubscriptionsEventsRequest { +pub struct GetBroadcasterSubscriptionsEventsRequest<'a> { /// Must match the User ID in the Bearer token. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// Filters the results and only returns a status object for users who have a subscribe event in this channel and have a matching user_id. /// Maximum: 100 #[cfg_attr(feature = "typed-builder", builder(default))] - pub user_id: Vec, + #[serde(borrow)] + pub user_id: Cow<'a, [&'a types::UserIdRef]>, /// Cursor for forward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub after: Option, + pub after: Option>, /// Maximum number of objects to return. Maximum: 100. Default: 20. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub first: Option, /// Retreive a single event by event ID #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub id: Option, + #[serde(borrow)] + pub id: Option>, } -impl GetBroadcasterSubscriptionsEventsRequest { +impl<'a> GetBroadcasterSubscriptionsEventsRequest<'a> { /// Get events for this broadcaster - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), - user_id: Default::default(), + broadcaster_id: broadcaster_id.to_cow(), + user_id: Cow::Borrowed(&[]), after: Default::default(), first: Default::default(), id: Default::default(), @@ -85,17 +85,8 @@ impl GetBroadcasterSubscriptionsEventsRequest { } /// Filter the results for specific users. - pub fn user_ids( - mut self, - user_ids: impl IntoIterator>, - ) -> Self { - self.user_id = user_ids.into_iter().map(Into::into).collect(); - self - } - - /// Filter the results for specific user. - pub fn user_id(mut self, user_id: impl Into) -> Self { - self.user_id = vec![user_id.into()]; + pub fn user_ids(mut self, user_ids: impl Into>) -> Self { + self.user_id = user_ids.into(); self } } @@ -181,7 +172,7 @@ where D: serde::de::Deserializer<'de> { }) } -impl Request for GetBroadcasterSubscriptionsEventsRequest { +impl Request for GetBroadcasterSubscriptionsEventsRequest<'_> { type Response = Vec; const PATH: &'static str = "subscriptions/events"; @@ -190,10 +181,12 @@ impl Request for GetBroadcasterSubscriptionsEventsRequest { &[twitch_oauth2::Scope::ChannelReadSubscriptions]; } -impl RequestGet for GetBroadcasterSubscriptionsEventsRequest {} +impl RequestGet for GetBroadcasterSubscriptionsEventsRequest<'_> {} -impl helix::Paginated for GetBroadcasterSubscriptionsEventsRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor } +impl helix::Paginated for GetBroadcasterSubscriptionsEventsRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } #[cfg(test)] diff --git a/src/helix/endpoints/subscriptions/mod.rs b/src/helix/endpoints/subscriptions/mod.rs index e1c3a41abb..f92ab4ad47 100644 --- a/src/helix/endpoints/subscriptions/mod.rs +++ b/src/helix/endpoints/subscriptions/mod.rs @@ -11,10 +11,7 @@ //! # let _: &HelixClient = &client; //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let req = GetBroadcasterSubscriptionsRequest::builder() -//! .broadcaster_id("1234") -//! .build(); -//! +//! let req = GetBroadcasterSubscriptionsRequest::broadcaster_id("1234"); //! //! println!("{:?}", &client.req_get(req, &token).await?.data); //! # Ok(()) @@ -26,6 +23,7 @@ use crate::{ types, }; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; pub mod check_user_subscription; pub mod get_broadcaster_subscriptions; diff --git a/src/helix/endpoints/tags/get_all_stream_tags.rs b/src/helix/endpoints/tags/get_all_stream_tags.rs index fc7506c5f5..0d2ccc9fa2 100644 --- a/src/helix/endpoints/tags/get_all_stream_tags.rs +++ b/src/helix/endpoints/tags/get_all_stream_tags.rs @@ -42,32 +42,37 @@ use helix::RequestGet; /// Query Parameters for [Get All Stream Tags](super::get_all_stream_tags) /// /// [`get-all-stream-tags`](https://dev.twitch.tv/docs/api/reference#get-all-stream-tags) -#[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug, Default)] +#[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetAllStreamTagsRequest { +pub struct GetAllStreamTagsRequest<'a> { /// Cursor for forward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub after: Option, + pub after: Option>, /// Maximum number of objects to return. Maximum: 100. Default: 20. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub first: Option, /// ID of a tag. Multiple IDs can be specified. If provided, only the specified tag(s) is(are) returned. Maximum of 100. #[cfg_attr(feature = "typed-builder", builder(default))] - pub tag_id: Vec, + #[serde(borrow)] + pub tag_id: Cow<'a, [&'a types::TagIdRef]>, } -impl GetAllStreamTagsRequest { +impl<'a> GetAllStreamTagsRequest<'a> { /// Filter the results for specific tag. - pub fn tag_ids(mut self, tag_ids: impl IntoIterator>) -> Self { - self.tag_id = tag_ids.into_iter().map(Into::into).collect(); + pub fn tag_ids(mut self, tag_ids: impl Into>) -> Self { + self.tag_id = tag_ids.into(); self } +} - /// Filter the results for specific tag. - pub fn tag_id(mut self, tag_id: impl Into) -> Self { - self.tag_id = vec![tag_id.into()]; - self +impl Default for GetAllStreamTagsRequest<'_> { + fn default() -> Self { + Self { + after: None, + first: None, + tag_id: Cow::Borrowed(&[]), + } } } @@ -76,7 +81,7 @@ impl GetAllStreamTagsRequest { /// [`get-all-stream-tags`](https://dev.twitch.tv/docs/api/reference#get-all-stream-tags) pub type Tag = helix::tags::TwitchTag; -impl Request for GetAllStreamTagsRequest { +impl Request for GetAllStreamTagsRequest<'_> { type Response = Vec; const PATH: &'static str = "tags/streams"; @@ -84,10 +89,12 @@ impl Request for GetAllStreamTagsRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetAllStreamTagsRequest {} +impl RequestGet for GetAllStreamTagsRequest<'_> {} -impl helix::Paginated for GetAllStreamTagsRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor } +impl helix::Paginated for GetAllStreamTagsRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } #[cfg(test)] diff --git a/src/helix/endpoints/tags/mod.rs b/src/helix/endpoints/tags/mod.rs index 698d1c9797..61a2a8e092 100644 --- a/src/helix/endpoints/tags/mod.rs +++ b/src/helix/endpoints/tags/mod.rs @@ -22,6 +22,7 @@ use crate::{ types, }; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; use std::collections::HashMap; pub mod get_all_stream_tags; diff --git a/src/helix/endpoints/teams/get_channel_teams.rs b/src/helix/endpoints/teams/get_channel_teams.rs index 7d0dfad9de..62abb73b77 100644 --- a/src/helix/endpoints/teams/get_channel_teams.rs +++ b/src/helix/endpoints/teams/get_channel_teams.rs @@ -3,13 +3,11 @@ //! //! ## Request: [GetChannelTeamsRequest] //! -//! To use this endpoint, construct a [`GetChannelTeamsRequest`] with the [`GetChannelTeamsRequest::builder()`] method. +//! To use this endpoint, construct a [`GetChannelTeamsRequest`] with the [`GetChannelTeamsRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::teams::get_channel_teams; -//! let request = get_channel_teams::GetChannelTeamsRequest::builder() -//! .broadcaster_id("1337") -//! .build(); +//! let request = get_channel_teams::GetChannelTeamsRequest::broadcaster_id("1337"); //! ``` //! //! ## Response: [BroadcasterTeam] @@ -24,9 +22,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_channel_teams::GetChannelTeamsRequest::builder() -//! .broadcaster_id("1337") -//! .build(); +//! let request = get_channel_teams::GetChannelTeamsRequest::broadcaster_id("1337"); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -44,17 +40,18 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetChannelTeamsRequest { +pub struct GetChannelTeamsRequest<'a> { /// Team ID. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, } -impl GetChannelTeamsRequest { +impl<'a> GetChannelTeamsRequest<'a> { /// Get the team of this specific broadcaster - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), } } } @@ -77,7 +74,7 @@ pub struct BroadcasterTeam { pub team: TeamInformation, } -impl Request for GetChannelTeamsRequest { +impl Request for GetChannelTeamsRequest<'_> { type Response = Vec; #[cfg(feature = "twitch_oauth2")] @@ -87,7 +84,7 @@ impl Request for GetChannelTeamsRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetChannelTeamsRequest {} +impl RequestGet for GetChannelTeamsRequest<'_> {} #[cfg(test)] #[test] diff --git a/src/helix/endpoints/teams/get_teams.rs b/src/helix/endpoints/teams/get_teams.rs index 4d68bc523d..b836a1bffb 100644 --- a/src/helix/endpoints/teams/get_teams.rs +++ b/src/helix/endpoints/teams/get_teams.rs @@ -3,13 +3,11 @@ //! //! ## Request: [GetTeamsRequest] //! -//! To use this endpoint, construct a [`GetTeamsRequest`] with the [`GetTeamsRequest::builder()`] method. +//! To use this endpoint, construct a [`GetTeamsRequest`] //! //! ```rust //! use twitch_api::helix::teams::get_teams; -//! let request = get_teams::GetTeamsRequest::builder() -//! .name("coolteam".to_string()) -//! .build(); +//! let request = get_teams::GetTeamsRequest::name("coolteam"); //! ``` //! //! ## Response: [Team] @@ -24,9 +22,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_teams::GetTeamsRequest::builder() -//! .name("coolteam".to_string()) -//! .build(); +//! let request = get_teams::GetTeamsRequest::name("coolteam"); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -44,29 +40,31 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetTeamsRequest { +pub struct GetTeamsRequest<'a> { /// Team ID. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub id: Option, + #[serde(borrow)] + pub id: Option>, /// Team name. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub name: Option, + #[serde(borrow)] + pub name: Option>, } -impl GetTeamsRequest { +impl<'a> GetTeamsRequest<'a> { /// Get team with this [`TeamId`](types::TeamId) - pub fn id(id: impl Into) -> Self { + pub fn id(id: impl types::IntoCow<'a, types::TeamIdRef> + 'a) -> Self { Self { - id: Some(id.into()), + id: Some(id.to_cow()), name: None, } } /// Get team with this name - pub fn name(name: String) -> Self { + pub fn name(name: impl Into>) -> Self { Self { id: None, - name: Some(name), + name: Some(name.into()), } } } @@ -85,7 +83,7 @@ pub struct Team { pub team: TeamInformation, } -impl Request for GetTeamsRequest { +impl Request for GetTeamsRequest<'_> { type Response = Vec; #[cfg(feature = "twitch_oauth2")] @@ -95,7 +93,7 @@ impl Request for GetTeamsRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetTeamsRequest {} +impl RequestGet for GetTeamsRequest<'_> {} #[cfg(test)] #[test] diff --git a/src/helix/endpoints/teams/mod.rs b/src/helix/endpoints/teams/mod.rs index 6b949b4e9a..ba9ed304fb 100644 --- a/src/helix/endpoints/teams/mod.rs +++ b/src/helix/endpoints/teams/mod.rs @@ -5,6 +5,7 @@ use crate::{ types, }; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; pub mod get_channel_teams; pub mod get_teams; diff --git a/src/helix/endpoints/users/block_user.rs b/src/helix/endpoints/users/block_user.rs index 289678fc42..7d942a8d4f 100644 --- a/src/helix/endpoints/users/block_user.rs +++ b/src/helix/endpoints/users/block_user.rs @@ -5,19 +5,15 @@ //! //! ## Request: [BlockUserRequest] //! -//! To use this endpoint, construct a [`BlockUserRequest`] with the [`BlockUserRequest::builder()`] method. +//! To use this endpoint, construct a [`BlockUserRequest`] with the [`BlockUserRequest::block_user()`] method. //! //! ```rust //! use twitch_api::helix::users::block_user::{self, Reason, SourceContext}; -//! let request = block_user::BlockUserRequest::builder() -//! .target_user_id("1234") -//! .build(); +//! let request = block_user::BlockUserRequest::block_user("1234"); //! // Or, specifying a reason for the block -//! let request = block_user::BlockUserRequest::builder() -//! .target_user_id("1234") +//! let request = block_user::BlockUserRequest::block_user("1234") //! .source_context(SourceContext::Chat) -//! .reason(Reason::Spam) -//! .build(); +//! .reason(Reason::Spam); //! ``` //! //! ## Response: [BlockUser] @@ -32,9 +28,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = block_user::BlockUserRequest::builder() -//! .target_user_id("1234") -//! .build(); +//! let request = block_user::BlockUserRequest::block_user("1234"); //! let response: block_user::BlockUser = client.req_put(request, helix::EmptyBody, &token).await?.data; //! # Ok(()) //! # } @@ -52,10 +46,11 @@ use helix::RequestPut; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct BlockUserRequest { +pub struct BlockUserRequest<'a> { /// User ID of the follower #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub target_user_id: types::UserId, + #[serde(borrow)] + pub target_user_id: Cow<'a, types::UserIdRef>, /// Source context for blocking the user. Valid values: "chat", "whisper". #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub source_context: Option, @@ -64,11 +59,11 @@ pub struct BlockUserRequest { pub reason: Option, } -impl BlockUserRequest { +impl<'a> BlockUserRequest<'a> { /// Block a user - pub fn block_user(target_user_id: impl Into) -> Self { + pub fn block_user(target_user_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - target_user_id: target_user_id.into(), + target_user_id: target_user_id.to_cow(), source_context: None, reason: None, } @@ -88,7 +83,7 @@ impl BlockUserRequest { } /// Source context for blocking the user. -#[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] +#[derive(PartialEq, Eq, Deserialize, Serialize, Copy, Clone, Debug)] #[serde(rename_all = "lowercase")] #[non_exhaustive] pub enum SourceContext { @@ -99,7 +94,7 @@ pub enum SourceContext { } /// Reason for blocking the user. -#[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] +#[derive(PartialEq, Eq, Deserialize, Serialize, Copy, Clone, Debug)] #[serde(rename_all = "lowercase")] #[non_exhaustive] pub enum Reason { @@ -121,7 +116,7 @@ pub enum BlockUser { Success, } -impl Request for BlockUserRequest { +impl Request for BlockUserRequest<'_> { type Response = BlockUser; #[cfg(feature = "twitch_oauth2")] @@ -131,7 +126,7 @@ impl Request for BlockUserRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::UserManageBlockedUsers]; } -impl RequestPut for BlockUserRequest { +impl RequestPut for BlockUserRequest<'_> { type Body = helix::EmptyBody; fn parse_inner_response( diff --git a/src/helix/endpoints/users/get_user_block_list.rs b/src/helix/endpoints/users/get_user_block_list.rs index 52f227d655..d0f69f6e2e 100644 --- a/src/helix/endpoints/users/get_user_block_list.rs +++ b/src/helix/endpoints/users/get_user_block_list.rs @@ -3,13 +3,11 @@ //! //! ## Request: [GetUserBlockListRequest] //! -//! To use this endpoint, construct a [`GetUserBlockListRequest`] with the [`GetUserBlockListRequest::builder()`] method. +//! To use this endpoint, construct a [`GetUserBlockListRequest`] with the [`GetUserBlockListRequest::broadcaster_id()`] method. //! //! ```rust //! use twitch_api::helix::users::get_user_block_list; -//! let request = get_user_block_list::GetUserBlockListRequest::builder() -//! .broadcaster_id("1234".to_string()) -//! .build(); +//! let request = get_user_block_list::GetUserBlockListRequest::broadcaster_id("1234"); //! ``` //! //! ## Response: [UserBlock] @@ -24,9 +22,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_user_block_list::GetUserBlockListRequest::builder() -//! .broadcaster_id("1234".to_string()) -//! .build(); +//! let request = get_user_block_list::GetUserBlockListRequest::broadcaster_id("1234"); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -44,23 +40,24 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetUserBlockListRequest { +pub struct GetUserBlockListRequest<'a> { /// User ID for a Twitch user. #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub broadcaster_id: types::UserId, + #[serde(borrow)] + pub broadcaster_id: Cow<'a, types::UserIdRef>, /// Cursor for forward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub after: Option, + pub after: Option>, /// Maximum number of objects to return. Maximum: 100. Default: 20. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub first: Option, } -impl GetUserBlockListRequest { +impl<'a> GetUserBlockListRequest<'a> { /// Get a specified user’s block list - pub fn broadcaster_id(broadcaster_id: impl Into) -> Self { + pub fn broadcaster_id(broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - broadcaster_id: broadcaster_id.into(), + broadcaster_id: broadcaster_id.to_cow(), after: Default::default(), first: Default::default(), } @@ -82,7 +79,7 @@ pub struct UserBlock { pub display_name: types::DisplayName, } -impl Request for GetUserBlockListRequest { +impl Request for GetUserBlockListRequest<'_> { type Response = Vec; #[cfg(feature = "twitch_oauth2")] @@ -93,10 +90,12 @@ impl Request for GetUserBlockListRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetUserBlockListRequest {} +impl RequestGet for GetUserBlockListRequest<'_> {} -impl helix::Paginated for GetUserBlockListRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor } +impl helix::Paginated for GetUserBlockListRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } #[cfg(test)] diff --git a/src/helix/endpoints/users/get_users.rs b/src/helix/endpoints/users/get_users.rs index 77f540f843..e85ed4a14a 100644 --- a/src/helix/endpoints/users/get_users.rs +++ b/src/helix/endpoints/users/get_users.rs @@ -8,8 +8,8 @@ //! ```rust //! use twitch_api::helix::users::get_users; //! let request = get_users::GetUsersRequest::builder() -//! .id(vec!["1234".into()]) -//! .login(vec!["justintvfan".into()]) +//! .id(&["1234".into()][..]) +//! .login(&["justintvfan".into()][..]) //! .build(); //! ``` //! @@ -19,15 +19,17 @@ //! //! ```rust, no_run //! use twitch_api::helix::{self, users::get_users}; -//! # use twitch_api::client; +//! # use twitch_api::{client, types}; //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; +//! let ids: &[&types::UserIdRef] = &["1234".into()]; +//! let logins: &[&types::UserNameRef] = &["justintvfan".into()]; //! let request = get_users::GetUsersRequest::builder() -//! .id(vec!["1234".into()]) -//! .login(vec!["justintvfan".into()]) +//! .id(ids) +//! .login(logins) //! .build(); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) @@ -46,50 +48,42 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetUsersRequest { +pub struct GetUsersRequest<'a> { /// User ID. Multiple user IDs can be specified. Limit: 100. - #[cfg_attr(feature = "typed-builder", builder(default))] - pub id: Vec, + #[cfg_attr( + feature = "typed-builder", + builder(default_code = "Cow::Borrowed(&[])", setter(into)) + )] + #[serde(borrow)] + pub id: Cow<'a, [&'a types::UserIdRef]>, /// User login name. Multiple login names can be specified. Limit: 100. - #[cfg_attr(feature = "typed-builder", builder(default))] - pub login: Vec, + #[cfg_attr( + feature = "typed-builder", + builder(default_code = "Cow::Borrowed(&[])", setter(into)) + )] + #[serde(borrow)] + pub login: Cow<'a, [&'a types::UserNameRef]>, } -impl GetUsersRequest { - /// Get a single user by their [`UserName`](types::UserName) - pub fn login(login: impl Into) -> Self { - Self { - id: Vec::default(), - login: vec![login.into()], - } - } - +impl<'a> GetUsersRequest<'a> { /// Get multiple user by their [`UserName`](types::UserName) /// /// ```rust /// use twitch_api::helix::users::get_users::GetUsersRequest; - /// GetUsersRequest::logins(["twitch", "justintv"]); + /// GetUsersRequest::logins(&["twitch".into(), "justintv".into()][..]); /// ``` - pub fn logins(login: impl IntoIterator>) -> Self { + pub fn logins(login: impl Into>) -> Self { Self { - id: Vec::default(), - login: login.into_iter().map(Into::into).collect(), - } - } - - /// Get a user by their [`UserId`](types::UserId) - pub fn id(id: impl Into) -> Self { - Self { - id: vec![id.into()], - login: Vec::default(), + id: Cow::Borrowed(&[]), + login: login.into(), } } /// Get multiple user by their [`UserId`](types::UserId) - pub fn ids(ids: impl IntoIterator>) -> Self { + pub fn ids(ids: impl Into>) -> Self { Self { - id: ids.into_iter().map(Into::into).collect(), - login: Vec::default(), + id: ids.into(), + login: Cow::Borrowed(&[]), } } @@ -100,8 +94,8 @@ impl GetUsersRequest { /// This is not a valid request, it needs to be filled out with other fields. pub fn new() -> Self { Self { - id: Vec::default(), - login: Vec::default(), + id: Cow::Borrowed(&[]), + login: Cow::Borrowed(&[]), } } } @@ -143,7 +137,7 @@ pub struct User { pub view_count: usize, } -impl Request for GetUsersRequest { +impl Request for GetUsersRequest<'_> { type Response = Vec; #[cfg(feature = "twitch_oauth2")] @@ -153,13 +147,15 @@ impl Request for GetUsersRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetUsersRequest {} +impl RequestGet for GetUsersRequest<'_> {} #[cfg(test)] #[test] fn test_request() { use helix::*; - let req = GetUsersRequest::id("44322889"); + + let ids: &[&types::UserIdRef] = &["44322889".into()]; + let req = GetUsersRequest::ids(ids); // From twitch docs // FIXME: This is not valid anymore. Twitch.... diff --git a/src/helix/endpoints/users/get_users_follows.rs b/src/helix/endpoints/users/get_users_follows.rs index 5b4106ca5e..8db12207c7 100644 --- a/src/helix/endpoints/users/get_users_follows.rs +++ b/src/helix/endpoints/users/get_users_follows.rs @@ -3,13 +3,11 @@ //! //! ## Request: [GetUsersFollowsRequest] //! -//! To use this endpoint, construct a [`GetUsersFollowsRequest`] with the [`GetUsersFollowsRequest::builder()`] method. +//! To use this endpoint, construct a [`GetUsersFollowsRequest`] //! //! ```rust //! use twitch_api::helix::users::get_users_follows; -//! let request = get_users_follows::GetUsersFollowsRequest::builder() -//! .to_id(Some("1234".into())) -//! .build(); +//! let request = get_users_follows::GetUsersFollowsRequest::following("1234"); //! ``` //! //! ## Response: [UsersFollows] @@ -24,9 +22,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_users_follows::GetUsersFollowsRequest::builder() -//! .to_id(Some("1234".into())) -//! .build(); +//! let request = get_users_follows::GetUsersFollowsRequest::following("1234"); //! let response: Vec = client.req_get(request, &token).await?.data.follow_relationships; //! # Ok(()) //! # } @@ -43,46 +39,48 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetUsersFollowsRequest { +pub struct GetUsersFollowsRequest<'a> { /// Cursor for forward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub after: Option, + pub after: Option>, /// Maximum number of objects to return. Maximum: 100. Default: 20. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub first: Option, /// User ID. The request returns information about users who are being followed by the from_id user. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub from_id: Option, + #[serde(borrow)] + pub from_id: Option>, /// User ID. The request returns information about users who are following the to_id user. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub to_id: Option, + #[serde(borrow)] + pub to_id: Option>, } -impl GetUsersFollowsRequest { +impl<'a> GetUsersFollowsRequest<'a> { /// Get the broadcasters that `from_id` is following - pub fn following(from_id: impl Into) -> Self { + pub fn following(from_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - from_id: Some(from_id.into()), + from_id: Some(from_id.to_cow()), ..Self::empty() } } /// Get the followers of `to_id` - pub fn followers(to_id: impl Into) -> Self { + pub fn followers(to_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - to_id: Some(to_id.into()), + to_id: Some(to_id.to_cow()), ..Self::empty() } } /// Check if user follows a specific broadcaster pub fn follows( - user_id: impl Into, - broadcaster_id: impl Into, + user_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, + broadcaster_id: impl types::IntoCow<'a, types::UserIdRef> + 'a, ) -> Self { Self { - from_id: Some(user_id.into()), - to_id: Some(broadcaster_id.into()), + from_id: Some(user_id.to_cow()), + to_id: Some(broadcaster_id.to_cow()), ..Self::empty() } } @@ -141,7 +139,7 @@ pub struct FollowRelationship { pub to_login: types::UserName, } -impl Request for GetUsersFollowsRequest { +impl Request for GetUsersFollowsRequest<'_> { type Response = UsersFollows; #[cfg(feature = "twitch_oauth2")] @@ -151,7 +149,7 @@ impl Request for GetUsersFollowsRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[]; } -impl RequestGet for GetUsersFollowsRequest { +impl RequestGet for GetUsersFollowsRequest<'_> { fn parse_inner_response( request: Option, uri: &http::Uri, @@ -190,8 +188,10 @@ impl RequestGet for GetUsersFollowsRequest { } } -impl helix::Paginated for GetUsersFollowsRequest { - fn set_pagination(&mut self, cursor: Option) { self.after = cursor } +impl helix::Paginated for GetUsersFollowsRequest<'_> { + fn set_pagination(&mut self, cursor: Option) { + self.after = cursor.map(|c| c.into_cow()) + } } #[cfg(test)] diff --git a/src/helix/endpoints/users/mod.rs b/src/helix/endpoints/users/mod.rs index fabd5fdc93..9007f03d01 100644 --- a/src/helix/endpoints/users/mod.rs +++ b/src/helix/endpoints/users/mod.rs @@ -5,16 +5,15 @@ //! # Examples //! //! ```rust,no_run -//! # use twitch_api::helix::{HelixClient, users::GetUsersRequest}; +//! # use twitch_api::{helix::{HelixClient, users::GetUsersRequest}, types}; //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { //! let client = HelixClient::new(); //! # let _: &HelixClient = &client; //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let req = GetUsersRequest::builder() -//! .login(vec!["justinfan1337".into()]) -//! .build(); +//! let logins: &[&types::UserNameRef] = &["justintvfan".into()]; +//! let req = GetUsersRequest::builder().login(logins).build(); //! //! println!("{:?}", &client.req_get(req, &token).await?.data); //! # Ok(()) @@ -25,6 +24,7 @@ use crate::{ types, }; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; pub mod block_user; pub mod get_user_block_list; diff --git a/src/helix/endpoints/users/unblock_user.rs b/src/helix/endpoints/users/unblock_user.rs index b8800688d0..779f292bdf 100644 --- a/src/helix/endpoints/users/unblock_user.rs +++ b/src/helix/endpoints/users/unblock_user.rs @@ -5,13 +5,11 @@ //! //! ## Request: [UnblockUserRequest] //! -//! To use this endpoint, construct a [`UnblockUserRequest`] with the [`UnblockUserRequest::builder()`] method. +//! To use this endpoint, construct a [`UnblockUserRequest`] with the [`UnblockUserRequest::unblock_user()`] method. //! //! ```rust //! use twitch_api::helix::users::unblock_user; -//! let request = unblock_user::UnblockUserRequest::builder() -//! .target_user_id("1234") -//! .build(); +//! let request = unblock_user::UnblockUserRequest::unblock_user("1234"); //! ``` //! //! ## Response: [UnblockUser] @@ -26,9 +24,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = unblock_user::UnblockUserRequest::builder() -//! .target_user_id("1234") -//! .build(); +//! let request = unblock_user::UnblockUserRequest::unblock_user("1234"); //! let response: unblock_user::UnblockUser = client.req_delete(request, &token).await?.data; //! # Ok(()) //! # } @@ -45,17 +41,18 @@ use helix::RequestDelete; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct UnblockUserRequest { +pub struct UnblockUserRequest<'a> { /// User ID of the follower #[cfg_attr(feature = "typed-builder", builder(setter(into)))] - pub target_user_id: types::UserId, + #[serde(borrow)] + pub target_user_id: Cow<'a, types::UserIdRef>, } -impl UnblockUserRequest { +impl<'a> UnblockUserRequest<'a> { /// Create a new unblock request - pub fn unblock_user(target_user_id: impl Into) -> Self { + pub fn unblock_user(target_user_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - target_user_id: target_user_id.into(), + target_user_id: target_user_id.to_cow(), } } } @@ -70,7 +67,7 @@ pub enum UnblockUser { Success, } -impl Request for UnblockUserRequest { +impl Request for UnblockUserRequest<'_> { type Response = UnblockUser; #[cfg(feature = "twitch_oauth2")] @@ -80,7 +77,7 @@ impl Request for UnblockUserRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::UserManageBlockedUsers]; } -impl RequestDelete for UnblockUserRequest { +impl RequestDelete for UnblockUserRequest<'_> { fn parse_inner_response( request: Option, uri: &http::Uri, diff --git a/src/helix/endpoints/videos/delete_videos.rs b/src/helix/endpoints/videos/delete_videos.rs index c6afeae1d6..0a2abcbabf 100644 --- a/src/helix/endpoints/videos/delete_videos.rs +++ b/src/helix/endpoints/videos/delete_videos.rs @@ -10,7 +10,7 @@ //! ```rust //! use twitch_api::helix::videos::delete_videos; //! let request = delete_videos::DeleteVideosRequest::builder() -//! .id(vec!["1234".into()]) +//! .id(&["1234".into()][..]) //! .build(); //! ``` //! @@ -20,14 +20,15 @@ //! //! ```rust, no_run //! use twitch_api::helix::{self, videos::delete_videos}; -//! # use twitch_api::client; +//! # use twitch_api::{client, types}; //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; +//! let ids: &[&types::VideoIdRef] = &["1234".into()]; //! let request = delete_videos::DeleteVideosRequest::builder() -//! .id(vec!["1234".into()]) +//! .id(ids) //! .build(); //! let response: delete_videos::DeleteVideo = client.req_delete(request, &token).await?.data; //! # Ok(()) @@ -47,26 +48,19 @@ use helix::RequestDelete; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct DeleteVideosRequest { +pub struct DeleteVideosRequest<'a> { /// ID of the video(s) to be deleted. Limit: 5. - #[cfg_attr(feature = "typed-builder", builder(default))] - pub id: Vec, + #[cfg_attr( + feature = "typed-builder", + builder(default_code = "Cow::Borrowed(&[])", setter(into)) + )] + #[serde(borrow)] + pub id: Cow<'a, [&'a types::VideoIdRef]>, } -impl DeleteVideosRequest { - /// ID of the video to be deleted - pub fn id(id: impl Into) -> Self { - Self { - id: vec![id.into()], - } - } - +impl<'a> DeleteVideosRequest<'a> { /// ID of the videos to be deleted - pub fn ids(ids: impl IntoIterator>) -> Self { - Self { - id: ids.into_iter().map(Into::into).collect(), - } - } + pub fn ids(ids: impl Into>) -> Self { Self { id: ids.into() } } } // FIXME: Should return VideoIds /// Return Values for [Delete Videos](super::delete_videos) @@ -79,7 +73,7 @@ pub enum DeleteVideo { Success, } -impl Request for DeleteVideosRequest { +impl Request for DeleteVideosRequest<'_> { type Response = DeleteVideo; const PATH: &'static str = "videos"; @@ -87,7 +81,7 @@ impl Request for DeleteVideosRequest { const SCOPE: &'static [twitch_oauth2::Scope] = &[twitch_oauth2::Scope::ChannelManageVideos]; } -impl RequestDelete for DeleteVideosRequest { +impl RequestDelete for DeleteVideosRequest<'_> { fn parse_inner_response( request: Option, uri: &http::Uri, @@ -119,7 +113,7 @@ impl RequestDelete for DeleteVideosRequest { #[test] fn test_request() { use helix::*; - let req = DeleteVideosRequest::id("234482848"); + let req = DeleteVideosRequest::ids(vec!["234482848".into()]); // From twitch docs let data = br#""#.to_vec(); diff --git a/src/helix/endpoints/videos/get_videos.rs b/src/helix/endpoints/videos/get_videos.rs index 61f3d7bcf0..11cda9346f 100644 --- a/src/helix/endpoints/videos/get_videos.rs +++ b/src/helix/endpoints/videos/get_videos.rs @@ -5,13 +5,11 @@ //! //! ## Request: [GetVideosRequest] //! -//! To use this endpoint, construct a [`GetVideosRequest`] with the [`GetVideosRequest::builder()`] method. +//! To use this endpoint, construct a [`GetVideosRequest`] //! //! ```rust //! use twitch_api::helix::videos::get_videos; -//! let request = get_videos::GetVideosRequest::builder() -//! .user_id(Some("1234".into())) -//! .build(); +//! let request = get_videos::GetVideosRequest::user_id("1234"); //! ``` //! //! ## Response: [Video] @@ -26,9 +24,7 @@ //! # let client: helix::HelixClient<'static, client::DummyHttpClient> = helix::HelixClient::default(); //! # let token = twitch_oauth2::AccessToken::new("validtoken".to_string()); //! # let token = twitch_oauth2::UserToken::from_existing(&client, token, None, None).await?; -//! let request = get_videos::GetVideosRequest::builder() -//! .user_id(Some("1234".into())) -//! .build(); +//! let request = get_videos::GetVideosRequest::user_id("1234"); //! let response: Vec = client.req_get(request, &token).await?.data; //! # Ok(()) //! # } @@ -47,28 +43,36 @@ use helix::RequestGet; #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug, Default)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct GetVideosRequest { +pub struct GetVideosRequest<'a> { /// ID of the video being queried. Limit: 100. If this is specified, you cannot use any of the optional query parameters below. - #[cfg_attr(feature = "typed-builder", builder(default))] - pub id: Vec, + #[cfg_attr( + feature = "typed-builder", + builder(default_code = "Cow::Borrowed(&[])", setter(into)) + )] + #[serde(borrow)] + pub id: Cow<'a, [&'a types::VideoIdRef]>, /// ID of the user who owns the video. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub user_id: Option, + #[serde(borrow)] + pub user_id: Option>, /// ID of the game the video is of. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub game_id: Option, + #[serde(borrow)] + pub game_id: Option>, /// Cursor for forward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub after: Option, + pub after: Option>, /// Cursor for backward pagination: tells the server where to start fetching the next set of results, in a multi-page response. The cursor value specified here is from the pagination response field of a prior query. #[cfg_attr(feature = "typed-builder", builder(default))] - pub before: Option, + #[serde(borrow)] + pub before: Option>, /// Number of values to be returned when getting videos by user or game ID. Limit: 100. Default: 20. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub first: Option, /// Language of the video being queried. Limit: 1. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] - pub language: Option, + #[serde(borrow)] + pub language: Option>, /// Period during which the video was created. Valid values: "all", "day", "week", "month". Default: "all". #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] pub period: Option, @@ -81,35 +85,27 @@ pub struct GetVideosRequest { pub type_: Option, } -impl GetVideosRequest { - /// ID of the video being queried. - pub fn id(id: impl Into) -> Self { - Self { - id: vec![id.into()], - ..Self::default() - } - } - +impl<'a> GetVideosRequest<'a> { /// IDs of the videos being queried. - pub fn ids(ids: impl IntoIterator>) -> Self { + pub fn ids(ids: impl Into>) -> Self { Self { - id: ids.into_iter().map(Into::into).collect(), + id: ids.into(), ..Self::default() } } /// ID of the user who owns the video. - pub fn user_id(user_id: impl Into) -> Self { + pub fn user_id(user_id: impl types::IntoCow<'a, types::UserIdRef> + 'a) -> Self { Self { - user_id: Some(user_id.into()), + user_id: Some(user_id.to_cow()), ..Self::default() } } /// ID of the game the video is of. - pub fn game_id(game_id: impl Into) -> Self { + pub fn game_id(game_id: impl types::IntoCow<'a, types::CategoryIdRef> + 'a) -> Self { Self { - game_id: Some(game_id.into()), + game_id: Some(game_id.to_cow()), ..Self::default() } } @@ -171,7 +167,7 @@ pub struct MutedSegment { pub offset: i64, } -impl Request for GetVideosRequest { +impl Request for GetVideosRequest<'_> { type Response = Vec