Skip to content

Commit

Permalink
wip: start adding an ActivityCursorIter
Browse files Browse the repository at this point in the history
  • Loading branch information
QuietMisdreavus committed Jul 21, 2020
1 parent 05be063 commit 7e8468f
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 2 deletions.
85 changes: 84 additions & 1 deletion src/cursor.rs
Expand Up @@ -9,9 +9,11 @@
//! module. The rest of it is available to make sure consumers of the API can understand precisely
//! what types come out of functions that return `CursorIter`.

use futures::Stream;
use hyper::{Body, Request};
use futures::{FutureExt, Stream};
use serde::{de::DeserializeOwned, Deserialize};
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};

Expand Down Expand Up @@ -369,3 +371,84 @@ where
self.poll_next(cx)
}
}

/// Trait to generalize over paginated views of results on the Account Activity API.
///
/// Cursors on the Account Activity API differ from other cursors on the REST API in that they
/// cannot page backwards, and in that their page IDs are strings instead of integers.
///
/// This is mainly meant to be implemented on raw/internal types to allow `ActivityCursorIter` to
/// function, when paired with a type that implements `ActivityItem`.
pub trait ActivityCursor: DeserializeOwned {
/// Moves the ID for the next page, if present, out of the cursor.
fn next_cursor(&mut self) -> Option<String>;
}

/// Trait for items which can be returned by `ActivityCursorIter`.
pub trait ActivityItem: Sized {
/// What is the cursor type that `ActivityCursorIter` can deserialize into?
type Cursor: ActivityCursor + Into<Vec<Self>>;
}

pub struct ActivityCursorIter<CursorItem> {
url: &'static str,
token: auth::Token,
pub next_cursor: Option<String>,
pub count: u32,
pub loaded: bool,
_marker: PhantomData<CursorItem>,
}

impl<CursorItem: ActivityItem> ActivityCursorIter<CursorItem> {
pub(crate) fn new(url: &'static str, token: &auth::Token) -> Self {
ActivityCursorIter {
url,
token: token.clone(),
next_cursor: None,
count: 20,
loaded: false,
_marker: PhantomData,
}
}

pub fn with_page_size(self, count: u32) -> Self {
ActivityCursorIter {
count,
next_cursor: None,
loaded: false,
..self
}
}

/// Clears the saved cursor information on this `ActivityCursorIter`.
pub fn reset(&mut self) {
self.next_cursor = None;
self.loaded = false;
}

fn request(&self, cursor: Option<String>) -> Request<Body> {
let params = ParamList::new()
.add_param("count", self.count.to_string())
.add_opt_param("cursor", cursor);

get(self.url, &self.token, Some(&params))
}

/// Loads the next page of messages, setting the `next_cursor` to the one received from
/// Twitter.
pub fn next_page<'s>(&'s mut self)
-> impl Future<Output = Result<Response<Vec<CursorItem>>>> + 's
{
let next_cursor = self.next_cursor.take();
let req = self.request(next_cursor);
let loader = request_with_json_response(req);
loader.map(
move |resp: Result<Response<CursorItem::Cursor>>| {
let mut resp = resp?;
self.loaded = true;
self.next_cursor = resp.next_cursor();
Ok(Response::map(resp, |c| c.into()))
}
)
}
}
4 changes: 4 additions & 0 deletions src/direct/mod.rs
Expand Up @@ -123,6 +123,10 @@ impl From<raw::EventCursor> for Vec<DirectMessage> {
}
}

impl crate::cursor::ActivityItem for DirectMessage {
type Cursor = raw::EventCursor;
}

/// Container for URL, hashtag, and mention information associated with a direct message.
///
/// As far as entities are concerned, a DM can contain nearly everything a tweet can. The only
Expand Down
6 changes: 6 additions & 0 deletions src/direct/raw.rs
Expand Up @@ -171,6 +171,12 @@ pub struct EventCursor {
pub next_cursor: Option<String>,
}

impl crate::cursor::ActivityCursor for EventCursor {
fn next_cursor(&mut self) -> Option<String> {
self.next_cursor.take()
}
}

/// Wrapper enum to represent a `DMEvent` in the Account Activity API.
///
/// As direct messages are part of the Account Activity API, they are presented as an event type in
Expand Down
13 changes: 12 additions & 1 deletion src/direct/welcome_messages.rs
Expand Up @@ -72,6 +72,10 @@ impl From<MessageCursor> for Vec<WelcomeMessage> {
}
}

impl crate::cursor::ActivityItem for WelcomeMessage {
type Cursor = MessageCursor;
}

#[derive(Deserialize)]
struct RawWelcomeMessage {
#[serde(with = "serde_via_string")]
Expand Down Expand Up @@ -113,13 +117,20 @@ struct SingleMessage {
}

#[derive(Deserialize)]
struct MessageCursor {
#[doc(hidden)]
pub struct MessageCursor {
#[serde(default)]
apps: HashMap<String, TweetSource>,
welcome_messages: Vec<RawWelcomeMessage>,
next_cursor: Option<String>,
}

impl crate::cursor::ActivityCursor for MessageCursor {
fn next_cursor(&mut self) -> Option<String> {
self.next_cursor.take()
}
}

/// Helper struct to navigate a collection of welcome messages.
pub struct Timeline {
link: &'static str,
Expand Down

0 comments on commit 7e8468f

Please sign in to comment.