1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //! Structs and functions for working with lists. //! //! A list is a way to group accounts together, either as a way to highlight those accounts, or to //! view as a subset of or supplement to your main timeline. A list can be viewed in the same way //! as the user's own timelines, loading in pages with the same [`Timeline`] interface. //! //! [`Timeline`]: ../tweet/struct.Timeline.html //! //! Lists can be private or public. If a list if public, then other users can view the list's //! members and the statuses of those members. (Statuses by protected accounts can still only be //! read by approved followers.) //! //! A list has "members", those accounts that are tracked as part of the list. When you call //! `statuses` on a given list, you're looking at the recent posts by its members. Members can be //! added and removed either one at a time, or in batches. Protected accounts can only be added to //! a list if the user is an approved follower of that account. //! //! A user can "subscribe" to another user's list as a sort of bookmark. Doing this places the list //! in the "lists" section of their profile, and in the `subscriptions` and `list` sets available //! from the API. Note that you don't need to subscribe to a list to view the statuses or users in //! it; you can do that to any public or personally owned list anyway. Subscribing merely allows //! quick access in case you want to keep track of a list someone else made. //! //! If a list is public, then all metadata about that list is public. Protected accounts can be //! seen as members of a list, but their statuses are still only visible to approved followers. //! None of the user-focused queries in this module assume that they're about the authenticated //! user; they can all be called in reference to any user. //! //! ## Types //! //! - `List`: This is the list metadata returned from Twitter when requesting information about the //! list itself, or when performing some modification to one. //! - `ListID`: There are two ways to reference a list in the Twitter API: Either via a unique //! numeric ID, or with its "slug" combined with a reference to the user who created it. This //! enum wraps that distinction into one type that all the methods take when they need to //! reference a list like this. See the enum's documentation for details on how to create one. //! - `ListUpdate`: When updating a list's metadata, all the fields that can be updated are //! optional, so the `update` function returns this builder struct so you don't have to provide //! all the parameters if you don't need to. //! //! ## Functions //! //! ### Basic actions //! //! These functions perform basic write actions on lists as a whole. These all require write access //! to the authenticated user's account. //! //! - `create`/`delete` //! - `update` (see `ListUpdate` for full details) //! - `subscribe`/`unsubscribe` //! - `add_member`/`remove_member` //! - `add_member_list`/`remove_member_list` //! //! ### Basic queries //! //! These functions let you query information about lists, or related to lists somehow. //! //! - `ownerships`/`subscriptions`/`list`: Note that `list` will only return the most recent 100 //! lists in the `ownerships`/`subscriptions` sets. //! - `memberships` //! - `members`/`is_member` //! - `subscribers`/`is_subscriber` //! - `show` //! - `statuses` use std::collections::HashMap; use common::*; use rustc_serialize::json; use chrono; use auth; use links; use user; use error::Error::InvalidResponse; use error; mod fun; pub use self::fun::*; /// Convenience enum to refer to a list via its owner and name or via numeric ID. /// /// Any API call that needs to reference a specific list has a set of parameters that collectively /// refer to it. Not only do lists have a unique numeric ID that refers to them, they have a "slug" /// that stands in as the list's unique name. This slug is only unique when taken in combination /// with the user that created it, though, so this leads to the raw API call having parameters that /// refer to the user by screen name or ID, or the list as a whole by this pair of slug parameters /// or the single ID parameter. egg-mode wraps this pattern with this `ListID` enum. /// /// Because the slug is formed from two variables instead of one, this enum foregoes the many /// `From` implementations that `UserID` has and instead opts for two creation functions. If you /// have a user/name combo, use `ListID::from_slug` when looking for the list. If you have the /// list's ID instead, then you can use `ListID::from_id`. /// /// # Example /// /// ```rust /// use egg_mode::list::ListID; /// /// //The following two ListIDs refer to the same list: /// let slug = ListID::from_slug("Twitter", "support"); /// let id = ListID::from_id(99924643); /// ``` #[derive(Debug, Copy, Clone)] pub enum ListID<'a> { ///Referring via the list's owner and its "slug" or name. Slug(user::UserID<'a>, &'a str), ///Referring via the list's numeric ID. ID(u64) } impl<'a> ListID<'a> { ///Make a new `ListID` by supplying its owner and name. pub fn from_slug<T: Into<user::UserID<'a>>>(owner: T, list_name: &'a str) -> ListID<'a> { ListID::Slug(owner.into(), list_name) } ///Make a new `ListID` by supplying its numeric ID. pub fn from_id(list_id: u64) -> ListID<'a> { ListID::ID(list_id) } } /// Represents the metadata for a list. /// /// Because of the myriad ways to reference a list, there are a few seemingly-redundant fields on /// here. It's worthwhile to understand all the referential fields: /// /// * `name` is the human-readable name of the list. Notably, this can contain spaces and uppercase /// letters. /// * `slug` is simply `name` converted to a format that can be put into a URL and used to /// reference the list for API calls. /// * `full_name` is how you'd link the list as a @mention, in the form `@screen_name/slug`. /// * `id` is the numeric ID, which can be used with `ListID::from_id` to make a `ListID` for the /// list. /// * `uri` is how you assemble a link to the list. Start with `"https://twitter.com"`, concat this /// field to the end, and you have a full URL. Note that the field does start with its own slash. /// * `user` is a mostly-populated `TwitterUser` corresponding to the creator of the list. If you /// combine `user.screen_name` or `user.id` with `slug`, you can send them to `ListID::from_slug` /// to make a `ListID` for the list. #[derive(Clone, Debug)] pub struct List { ///The name of the list. pub name: String, ///The user who created the list. pub user: user::TwitterUser, ///The "slug" of a list, that can be combined with its creator's `UserID` to refer to the list. pub slug: String, ///The numeric ID of the list. pub id: u64, ///The number of accounts "subscribed" to the list, for whom it will appear in their collection ///of available lists. pub subscriber_count: u64, ///The number of accounts added to the list. pub member_count: u64, ///The full name of the list, preceded by `@`, that can be used to link to the list as part of ///a tweet, direct message, or other place on Twitter where @mentions are parsed. pub full_name: String, ///The description of the list, as entered by its creator. pub description: String, ///The full name of the list, preceded by `/`, that can be preceded with `https://twitter.com` ///to create a link to the list. pub uri: String, ///UTC timestamp of when the list was created. pub created_at: chrono::DateTime<chrono::Utc>, } impl FromJson for List { fn from_json(input: &json::Json) -> Result<Self, error::Error> { if !input.is_object() { return Err(InvalidResponse("List received json that wasn't an object", Some(input.to_string()))); } field_present!(input, uri); field_present!(input, full_name); field_present!(input, description); field_present!(input, slug); field_present!(input, name); field_present!(input, subscriber_count); field_present!(input, member_count); field_present!(input, id); field_present!(input, name); field_present!(input, created_at); field_present!(input, user); Ok(List { created_at: try!(field(input, "created_at")), name: try!(field(input, "name")), slug: try!(field(input, "slug")), id: try!(field(input, "id")), user: try!(field(input, "user")), subscriber_count: try!(field(input, "subscriber_count")), member_count: try!(field(input, "member_count")), full_name: try!(field(input, "full_name")), description: try!(field(input, "description")), uri: try!(field(input, "uri")) }) } } /// Represents a pending update to a list's metadata. /// /// As updating a list could modify each field independently, this operation is exposed as a builder /// struct. To update any field, call the method named after that field, then call `send` to send /// the update to Twitter. /// /// # Example /// /// ```rust,no_run /// # extern crate egg_mode; extern crate tokio_core; extern crate futures; /// # use egg_mode::Token; use tokio_core::reactor::{Core, Handle}; /// # fn main() { /// # let (token, mut core, handle): (Token, Core, Handle) = unimplemented!(); /// use egg_mode::list::{self, ListID}; /// /// //remember, you can only update a list if you own it! /// let update = list::update(ListID::from_slug("Twitter", "support")); /// let list = core.run(update.name("Official Support").send(&token, &handle)).unwrap(); /// # } /// ``` pub struct ListUpdate<'a> { list: ListID<'a>, name: Option<&'a str>, public: Option<bool>, desc: Option<&'a str>, } impl<'a> ListUpdate<'a> { ///Updates the name of the list. pub fn name(self, name: &'a str) -> ListUpdate<'a> { ListUpdate { name: Some(name), ..self } } ///Sets whether the list is public. pub fn public(self, public: bool) -> ListUpdate<'a> { ListUpdate { public: Some(public), ..self } } ///Updates the description of the list. pub fn desc(self, desc: &'a str) -> ListUpdate<'a> { ListUpdate { desc: Some(desc), ..self } } ///Sends the update request to Twitter. pub fn send<'h>(self, token: &auth::Token, handle: &'h Handle) -> FutureResponse<'h, List> { let mut params = HashMap::new(); add_list_param(&mut params, &self.list); if let Some(name) = self.name { add_param(&mut params, "name", name); } if let Some(public) = self.public { if public { add_param(&mut params, "mode", "public"); } else { add_param(&mut params, "mode", "private"); } } if let Some(desc) = self.desc { add_param(&mut params, "description", desc); } let req = auth::post(links::lists::UPDATE, token, Some(¶ms)); make_parsed_future(handle, req) } }