diff --git a/examples/client.rs b/examples/client.rs index f3a3b73..3ec9ef2 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -117,6 +117,26 @@ async fn test_file_upload() -> Result<(), Box = all_channels_scroller + .collect_items_stream(&session, Duration::from_millis(1000)) + .await?; + println!("Number of channels: {:#?}", channels.len()); + + if let Some(general_channel_info) = channels + .into_iter() + .find(|c| c.flags.is_general.unwrap_or(false)) + { + println!("General channel info: {:#?}", general_channel_info); + let files_list_scroller = SlackApiFilesListRequest::new() + .with_channel(general_channel_info.id) + .scroller(); + let files: Vec = files_list_scroller + .collect_items_stream(&session, Duration::from_millis(1000)) + .await?; + println!("Number of files in general: {:#?}", files.len()); + } + Ok(()) } diff --git a/src/api/chat.rs b/src/api/chat.rs index c36266e..9d1bb21 100644 --- a/src/api/chat.rs +++ b/src/api/chat.rs @@ -453,10 +453,11 @@ impl SlackApiScrollableResponse for SlackApiChatScheduledMessagesListResponse { type CursorType = SlackCursorId; type ResponseItemType = SlackApiChatScheduledMessageInfo; - fn next_cursor(&self) -> Option<&Self::CursorType> { + fn next_cursor(&self) -> Option { self.response_metadata .as_ref() .and_then(|rm| rm.next_cursor.as_ref()) + .cloned() } fn scrollable_items<'a>(&'a self) -> Box + 'a> { diff --git a/src/api/conversations.rs b/src/api/conversations.rs index a0a58a5..76e15c5 100644 --- a/src/api/conversations.rs +++ b/src/api/conversations.rs @@ -426,10 +426,11 @@ impl SlackApiScrollableResponse for SlackApiConversationsHistoryResponse { type CursorType = SlackCursorId; type ResponseItemType = SlackHistoryMessage; - fn next_cursor(&self) -> Option<&Self::CursorType> { + fn next_cursor(&self) -> Option { self.response_metadata .as_ref() .and_then(|rm| rm.next_cursor.as_ref()) + .cloned() } fn scrollable_items<'a>(&'a self) -> Box + 'a> { @@ -540,10 +541,11 @@ impl SlackApiScrollableResponse for SlackApiConversationsListResponse { type CursorType = SlackCursorId; type ResponseItemType = SlackChannelInfo; - fn next_cursor(&self) -> Option<&Self::CursorType> { + fn next_cursor(&self) -> Option { self.response_metadata .as_ref() .and_then(|rm| rm.next_cursor.as_ref()) + .cloned() } fn scrollable_items<'a>(&'a self) -> Box + 'a> { @@ -590,10 +592,11 @@ impl SlackApiScrollableResponse for SlackApiConversationsMembersResponse { type CursorType = SlackCursorId; type ResponseItemType = SlackUserId; - fn next_cursor(&self) -> Option<&Self::CursorType> { + fn next_cursor(&self) -> Option { self.response_metadata .as_ref() .and_then(|rm| rm.next_cursor.as_ref()) + .cloned() } fn scrollable_items<'a>(&'a self) -> Box + 'a> { @@ -677,10 +680,11 @@ impl SlackApiScrollableResponse for SlackApiConversationsRepliesResponse { type CursorType = SlackCursorId; type ResponseItemType = SlackHistoryMessage; - fn next_cursor(&self) -> Option<&Self::CursorType> { + fn next_cursor(&self) -> Option { self.response_metadata .as_ref() .and_then(|rm| rm.next_cursor.as_ref()) + .cloned() } fn scrollable_items<'a>(&'a self) -> Box + 'a> { diff --git a/src/api/files.rs b/src/api/files.rs index f331480..a58bfcc 100644 --- a/src/api/files.rs +++ b/src/api/files.rs @@ -2,11 +2,6 @@ //! Support for Slack Files API methods //! -use rsb_derive::Builder; -use rvstruct::ValueStruct; -use serde::{Deserialize, Serialize, Serializer}; -use serde_with::skip_serializing_none; - use crate::api::{ SlackApiUsersConversationsRequest, SlackApiUsersConversationsResponse, SlackApiUsersProfileSetRequest, SlackApiUsersProfileSetResponse, @@ -14,8 +9,15 @@ use crate::api::{ use crate::models::*; use crate::multipart_form::FileMultipartData; use crate::ratectl::*; -use crate::SlackClientSession; use crate::{ClientResult, SlackClientHttpConnector}; +use crate::{SlackApiScrollableRequest, SlackApiScrollableResponse, SlackClientSession}; +use futures_util::future::BoxFuture; +use futures_util::FutureExt; +use rsb_derive::Builder; +use rvstruct::ValueStruct; +use serde::{Deserialize, Serialize, Serializer}; +use serde_with::skip_serializing_none; +use tokio_stream::StreamExt; impl<'a, SCHC> SlackClientSession<'a, SCHC> where @@ -223,6 +225,46 @@ pub struct SlackApiFilesListPaging { pub pages: Option, } +impl SlackApiScrollableRequest for SlackApiFilesListRequest +where + SCHC: SlackClientHttpConnector + Send + Sync + Clone + 'static, +{ + type ResponseType = SlackApiFilesListResponse; + type CursorType = u32; + type ResponseItemType = SlackFile; + + fn with_new_cursor(&self, new_cursor: Option<&Self::CursorType>) -> Self { + self.clone().opt_page(new_cursor.cloned()) + } + + fn scroll<'a, 's>( + &'a self, + session: &'a SlackClientSession<'s, SCHC>, + ) -> BoxFuture<'a, ClientResult> { + async move { session.files_list(self).await }.boxed() + } +} + +impl SlackApiScrollableResponse for SlackApiFilesListResponse { + type CursorType = u32; + type ResponseItemType = SlackFile; + + fn next_cursor(&self) -> Option { + self.paging + .as_ref() + .into_iter() + .filter_map(|paging| match (paging.page, paging.pages) { + (Some(page), Some(pages)) if page < pages => Some(page + 1), + _ => None, + }) + .next() + } + + fn scrollable_items<'a>(&'a self) -> Box + 'a> { + Box::new(self.files.iter()) + } +} + #[skip_serializing_none] #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] pub struct SlackApiFilesUploadRequest { diff --git a/src/api/users.rs b/src/api/users.rs index a635a9c..d8acf07 100644 --- a/src/api/users.rs +++ b/src/api/users.rs @@ -233,10 +233,11 @@ impl SlackApiScrollableResponse for SlackApiUsersConversationsResponse { type CursorType = SlackCursorId; type ResponseItemType = SlackChannelInfo; - fn next_cursor(&self) -> Option<&Self::CursorType> { + fn next_cursor(&self) -> Option { self.response_metadata .as_ref() .and_then(|rm| rm.next_cursor.as_ref()) + .cloned() } fn scrollable_items<'a>(&'a self) -> Box + 'a> { @@ -284,10 +285,11 @@ impl SlackApiScrollableResponse for SlackApiUsersListResponse { type CursorType = SlackCursorId; type ResponseItemType = SlackUser; - fn next_cursor(&self) -> Option<&Self::CursorType> { + fn next_cursor(&self) -> Option { self.response_metadata .as_ref() .and_then(|rm| rm.next_cursor.as_ref()) + .cloned() } fn scrollable_items<'a>(&'a self) -> Box + 'a> { diff --git a/src/scroller.rs b/src/scroller.rs index 6d3941d..a2d9be5 100644 --- a/src/scroller.rs +++ b/src/scroller.rs @@ -80,7 +80,7 @@ pub trait SlackApiScrollableResponse { type CursorType; type ResponseItemType; - fn next_cursor(&self) -> Option<&Self::CursorType>; + fn next_cursor(&self) -> Option; fn scrollable_items<'a>(&'a self) -> Box + 'a>; } @@ -164,7 +164,7 @@ where .scroll(session) .map_ok(|res| { self.last_response = Some(res.clone()); - self.last_cursor = res.next_cursor().cloned(); + self.last_cursor = res.next_cursor(); res }) .await