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
use structures::comment_list::CommentList;
use structures::submission::FlairList;
use structures::user::User;
use structures::subreddit::Subreddit;
use errors::APIError;

/// An object that can be voted upon and has a score based on the upvotes - downvotes.
/// ## Notes
/// The `ups` and `downs` values from the API no longer represent the true upvotes and downvotes,
/// so this trait does not expose them.
pub trait Votable {
    /// The (fuzzed) points score of the object.
    fn score(&self) -> i64;
    /// Indicates the logged-in user's current vote on this object:
    /// - Some(true) = Upvoted
    /// - Some(false) = Downvoted
    /// - None = No vote
    fn likes(&self) -> Option<bool>;
    /// Upvotes the specified post, if possible.
    fn upvote(&self) -> Result<(), APIError>;
    /// Downvotes the specified post, if possible.
    fn downvote(&self) -> Result<(), APIError>;
    /// Removes the vote on the specified post, if possible.
    fn cancel_vote(&self) -> Result<(), APIError>;
}

/// A paginatable listing.
pub trait PageListing {
    /// The ID to use for anchoring when paginating to the previous page.
    fn before(&self) -> Option<String>;
    /// The ID to use for anchoring when paginating to the next page.
    fn after(&self) -> Option<String>;
    /// The modhash (CSRF token) sent with this listing. Largely redundant, since the main
    /// modhash can be used.
    fn modhash(&self) -> Option<String>;
}

/// An object that was created at some point (e.g. a subreddit, a submission or a comment)
pub trait Created {
    /// The timestamp of the time when the post was created, as would be shown to the logged-in
    /// user.
    fn created(&self) -> i64;
    /// The timestamp of post creation, in UTC.
    fn created_utc(&self) -> i64;
}

/// An object that can be edited (anything that has a body).
pub trait Editable {
    /// `true` if edited, otherwise `false`.
    fn edited(&self) -> bool;
    /// Returns `Some(edited timestamp in logged-in user's time zone)` if edited, otherwise `None`.
    fn edited_time(&self) -> Option<i64>;
    /// Edits the specified post (if possible) with the new text (in **Markdown** format)
    fn edit(&mut self, text: &str) -> Result<(), APIError>;
    /// Gets the body of a comment or message or the self text of a post, if available.
    /// In the case of link posts, this will be `None`.
    fn body(&self) -> Option<String>;
    /// Gets the comment/message body HTML or self text HTML if available.
    fn body_html(&self) -> Option<String>;
    // TODO: anything editable has a body: refactor to handle this!
}

/// An object that was created by an author and is in a subreddit (i.e. a submission or comment)
pub trait Content {
    /// The author of the object.
    fn author(&self) -> User;
    /// The flair text of the user flair, if present.
    fn author_flair_text(&self) -> Option<String>;
    /// The flair CSS class of the user flair, if present.
    fn author_flair_css(&self) -> Option<String>;
    /// For submissions (link/self posts), this is the subreddit where it was posted. For comments,
    /// this is the subreddit of the parent submission.
    fn subreddit(&self) -> Subreddit;
    /// Deletes the specified object, if possible. **This may be irreversible. Use with caution.**
    fn delete(self) -> Result<(), APIError>;
    /// Gets the full ID of this comment (kind + id)
    fn name(&self) -> &str;
}

/// An object that can be commented upon and may have comments.
pub trait Commentable<'a> {
    /// The number of comments on this object. Prefer this to `replies().count()`.
    fn reply_count(&self) -> u64;
    /// Sends a reply with the specified body.
    fn reply(&self, &str) -> Result<(), APIError>;
    /// Gets all replies as a self-paginating `CommentList`, which can be iterated through as
    /// necessary. Comments cannot be batched like submission listings, so there may be
    /// multiple requests on large threads to get all comments.
    fn replies(self) -> Result<CommentList<'a>, APIError>;
}


/// An object that can be stickied (made into an 'annoucement post'). In practice, this is only
/// self/link posts.
pub trait Stickable {
    /// Returns the **current** sticky state of the submission.
    fn stickied(&self) -> bool;
    /// Makes the selected post a sticky, provided that you have the correct privileges.
    /// For comments, this will also distinguish the post as [M].
    fn stick(&mut self) -> Result<(), APIError>;
    /// Makes this sticky post a normal post, provided that you have the correct privileges.
    /// For comments, this will also remove the [M] distinguish.
    fn unstick(&mut self) -> Result<(), APIError>;
    /// Toggles the sticky state (i.e. becomes sticky if it is not one, and becomes normal if it
    /// is a sticky)
    fn toggle_sticky(&mut self) -> Result<(), APIError> {
        if self.stickied() {
            self.unstick()
        } else {
            self.stick()
        }
    }
}

/// An object that can be locked so that no further comments can be added.
pub trait Lockable {
    /// Returns the **current** locked state of the submission.
    fn locked(&self) -> bool;
    /// Locks the current submission, provided you have the correct privileges.
    fn lock(&mut self) -> Result<(), APIError>;
    /// Unlocks the current submission, provided you have the correct privileges.
    fn unlock(&mut self) -> Result<(), APIError>;
    /// Toggles the lock state (locks if unlocked, unlocks if locked).
    fn toggle_lock(&mut self) -> Result<(), APIError> {
        if self.locked() {
            self.unlock()
        } else {
            self.lock()
        }
    }
}

/// An object that can be reported (submission, comment, private message). Also exposes moderation
/// options to review reports.
pub trait Reportable {
    /// Reports the object for the specified reason (must be less than 100 characters).
    fn report(&self, reason: &str) -> Result<(), APIError>;
    /// Gets the number of reports for this post. If you do not have the rights to view this value,
    /// this will return `None`.
    fn report_count(&self) -> Option<u64>;
    // TODO: add report_reasons
}

/// An object that has a flair and can be given a flair by the creator or a moderator.
pub trait Flairable {
    /// Gets the current flair text, if present.
    fn get_flair_text(&self) -> Option<String>;
    /// Gets the current CSS class of the flair, if present.
    fn get_flair_css(&self) -> Option<String>;
    /// Tries to fetch the flair options for this item. If you do not have the privileges to add
    /// a flair to this (i.e. not moderator or author), this will give an
    /// `APIError::HTTPError(Forbidden)`.
    fn flair_options(&self) -> Result<FlairList, APIError>;
    /// Sets the flair for this post, if possible. The `template` parameter is the
    /// `FlairChoice.flair_template_id` field. The template will also be returned if using
    /// `FlairList.find_text`. This may fail with `APIError::HTTPError(Forbidden)` if you are
    /// not authorized to do this.
    fn flair(&self, template: &str) -> Result<(), APIError>;
}

/// An object that can be shown/hidden in listings.
pub trait Visible {
    /// Gets the current visibility state of the ooject.
    fn hidden(&self) -> bool;
    /// Hides the object, so it is not shown in listings. May be useful so that bots do not
    /// view the same links multiple times.
    fn hide(&mut self) -> Result<(), APIError>;
    /// Shows the object again, so it **is** visible in listings.
    fn show(&mut self) -> Result<(), APIError>;
    /// Hides the object if visible, and shows it if hidden.
    fn toggle_hidden(&mut self) -> Result<(), APIError> {
        if self.hidden() {
            self.show()
        } else {
            self.hide()
        }
    }
}

/// An object that can be distinguished (moderator/admin/special indicator).
pub trait Distinguishable {
    /// Indicates whether the user has used a special flag for themselves, e.g. [M] or [A].
    /// Possible values:
    ///
    /// - `None` - Normal user
    /// - `Some("moderator")` - [M]
    /// - `Some("admin")` - [A]
    /// - `Some("special")` - other special 'distinguishes' e.g. [Δ]
    fn distinguished(&self) -> Option<String>;
    /// Sets the post to have a [M] distinguish.
    fn distinguish(&mut self) -> Result<(), APIError>;
    /// Removes any distinguish on the comment. This will also unsticky a comment, if it is
    /// stickied.
    fn undistinguish(&mut self) -> Result<(), APIError>;
    /// Distinguishes if undistinguished, and vice versa.
    fn toggle_distinguish(&mut self) -> Result<(), APIError> {
        if let Some(_) = self.distinguished() {
            self.undistinguish()
        } else {
            self.distinguish()
        }
    }
}