modio/
comments.rs

1//! Mod comments interface
2
3use serde::ser::{Serialize, SerializeMap, Serializer};
4use serde_derive::Serialize;
5
6use crate::prelude::*;
7use crate::types::id::{CommentId, GameId, ModId};
8pub use crate::types::mods::Comment;
9
10/// Interface for comments of a mod.
11#[derive(Clone)]
12pub struct Comments {
13    modio: Modio,
14    game: GameId,
15    mod_id: ModId,
16}
17
18impl Comments {
19    pub(crate) fn new(modio: Modio, game: GameId, mod_id: ModId) -> Self {
20        Self {
21            modio,
22            game,
23            mod_id,
24        }
25    }
26
27    /// Returns a `Query` interface to retrieve all comments.
28    ///
29    /// See [Filters and sorting](filters).
30    pub fn search(&self, filter: Filter) -> Query<Comment> {
31        let route = Route::GetModComments {
32            game_id: self.game,
33            mod_id: self.mod_id,
34        };
35        Query::new(self.modio.clone(), route, filter)
36    }
37
38    /// Return comment by id.
39    pub async fn get(self, id: CommentId) -> Result<Comment> {
40        let route = Route::GetModComment {
41            game_id: self.game,
42            mod_id: self.mod_id,
43            comment_id: id,
44        };
45        self.modio.request(route).send().await
46    }
47
48    /// Add a new comment. [required: token]
49    pub async fn add<S>(self, content: S, reply_id: Option<CommentId>) -> Result<Comment>
50    where
51        S: Into<String>,
52    {
53        let route = Route::AddModComment {
54            game_id: self.game,
55            mod_id: self.mod_id,
56        };
57        let content = content.into();
58        let data = CommentOptions { content, reply_id };
59        self.modio.request(route).form(&data).send().await
60    }
61
62    /// Edit a comment by id. [required: token]
63    pub async fn edit<S>(self, id: CommentId, content: S) -> Result<Comment>
64    where
65        S: Into<String>,
66    {
67        let route = Route::EditModComment {
68            game_id: self.game,
69            mod_id: self.mod_id,
70            comment_id: id,
71        };
72        let data = CommentOptions {
73            content: content.into(),
74            reply_id: None,
75        };
76        self.modio.request(route).form(&data).send().await
77    }
78
79    /// Delete a comment by id. [required: token]
80    pub async fn delete(self, id: CommentId) -> Result<()> {
81        let route = Route::DeleteModComment {
82            game_id: self.game,
83            mod_id: self.mod_id,
84            comment_id: id,
85        };
86        self.modio.request(route).send().await
87    }
88
89    /// Update the karma for a comment. [required: token]
90    pub async fn karma(self, id: CommentId, karma: Karma) -> Result<Editing<Comment>> {
91        let route = Route::AddModCommentKarma {
92            game_id: self.game,
93            mod_id: self.mod_id,
94            comment_id: id,
95        };
96        self.modio
97            .request(route)
98            .form(&karma)
99            .send()
100            .await
101            .map(Editing::Entity)
102            .or_else(|e| match (e.status(), e.error_ref()) {
103                (Some(StatusCode::FORBIDDEN), Some(15059)) => Ok(Editing::NoChanges),
104                _ => Err(e),
105            })
106    }
107}
108
109/// Comment filters and sorting.
110///
111/// # Filters
112/// - `Fulltext`
113/// - `Id`
114/// - `ModId`
115/// - `SubmittedBy`
116/// - `DateAdded`
117/// - `ReplyId`
118/// - `ThreadPosition`
119/// - `Karma`
120/// - `Content`
121///
122/// # Sorting
123/// - `Id`
124/// - `ModId`
125/// - `SubmittedBy`
126/// - `DateAdded`
127///
128/// See [modio docs](https://docs.mod.io/restapiref/#get-mod-comments) for more information.
129///
130/// By default this returns up to `100` items. You can limit the result by using `limit` and
131/// `offset`.
132///
133/// # Example
134/// ```
135/// use modio::filter::prelude::*;
136/// use modio::comments::filters::Id;
137///
138/// let filter = Id::_in(vec![1, 2]).order_by(Id::desc());
139/// ```
140#[rustfmt::skip]
141pub mod filters {
142    #[doc(inline)]
143    pub use crate::filter::prelude::Fulltext;
144    #[doc(inline)]
145    pub use crate::filter::prelude::Id;
146    #[doc(inline)]
147    pub use crate::filter::prelude::ModId;
148    #[doc(inline)]
149    pub use crate::filter::prelude::DateAdded;
150    #[doc(inline)]
151    pub use crate::filter::prelude::SubmittedBy;
152
153    filter!(ReplyId, REPLY_ID, "reply_id", Eq, NotEq, In, Cmp);
154    filter!(ThreadPosition, THREAD_POSITION, "thread_position", Eq, NotEq, In, Like);
155    filter!(Karma, KARMA, "karma", Eq, NotEq, In, Cmp);
156    filter!(Content, CONTENT, "content", Eq, NotEq, Like);
157}
158
159pub enum Karma {
160    Positive,
161    Negative,
162}
163
164impl Serialize for Karma {
165    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
166        let mut s = serializer.serialize_map(Some(1))?;
167        match self {
168            Self::Positive => s.serialize_entry("karma", &1)?,
169            Self::Negative => s.serialize_entry("karma", &-1)?,
170        }
171        s.end()
172    }
173}
174
175#[derive(Serialize)]
176struct CommentOptions {
177    content: String,
178    #[serde(skip_serializing_if = "Option::is_none")]
179    reply_id: Option<CommentId>,
180}