pub mod response;
extern crate serde_json;
use crate::models::subreddit::response::{SubredditData, SubredditResponse, SubredditsData};
use crate::client::Client;
use crate::util::defaults::default_client;
use crate::util::{FeedOption, RouxError};
use crate::models::{Comments, Moderators, Submissions};
pub struct Subreddits;
impl Subreddits {
#[maybe_async::maybe_async]
pub async fn search(
name: &str,
limit: Option<u32>,
options: Option<FeedOption>,
) -> Result<SubredditsData, RouxError> {
let url = &mut format!("https://www.reddit.com/subreddits/search.json?q={}", name);
if let Some(limit) = limit {
url.push_str(&format!("&limit={}", limit));
}
if let Some(options) = options {
options.build_url(url);
}
let client = default_client();
Ok(client
.get(&url.to_owned())
.send()
.await?
.json::<SubredditsData>()
.await?)
}
}
pub struct Subreddit {
pub name: String,
url: String,
client: Client,
is_oauth: bool,
}
impl Subreddit {
pub fn new(name: &str) -> Subreddit {
let subreddit_url = format!("https://www.reddit.com/r/{}", name);
Subreddit {
name: name.to_owned(),
url: subreddit_url,
client: default_client(),
is_oauth: false,
}
}
pub fn new_oauth(name: &str, client: &Client) -> Subreddit {
let subreddit_url = format!("https://oauth.reddit.com/r/{}", name);
Subreddit {
name: name.to_owned(),
url: subreddit_url,
client: client.to_owned(),
is_oauth: true,
}
}
#[maybe_async::maybe_async]
pub async fn moderators(&self) -> Result<Moderators, RouxError> {
if self.is_oauth {
Ok(self
.client
.get(&format!("{}/about/moderators/.json", self.url))
.send()
.await?
.json::<Moderators>()
.await?)
} else {
Err(RouxError::OAuthClientRequired)
}
}
#[maybe_async::maybe_async]
pub async fn about(&self) -> Result<SubredditData, RouxError> {
Ok(self
.client
.get(&format!("{}/about/.json", self.url))
.send()
.await?
.json::<SubredditResponse>()
.await?
.data)
}
#[maybe_async::maybe_async]
async fn get_feed(
&self,
ty: &str,
limit: u32,
options: Option<FeedOption>,
) -> Result<Submissions, RouxError> {
let url = &mut format!("{}/{}.json?limit={}", self.url, ty, limit);
if let Some(options) = options {
options.build_url(url);
}
Ok(self
.client
.get(&url.to_owned())
.send()
.await?
.json::<Submissions>()
.await?)
}
#[maybe_async::maybe_async]
async fn get_comment_feed(
&self,
ty: &str,
depth: Option<u32>,
limit: Option<u32>,
) -> Result<Comments, RouxError> {
let url = &mut format!("{}/{}.json?", self.url, ty);
if let Some(depth) = depth {
url.push_str(&format!("&depth={}", depth));
}
if let Some(limit) = limit {
url.push_str(&format!("&limit={}", limit));
}
if url.contains("comments/") {
let mut comments = self
.client
.get(&url.to_owned())
.send()
.await?
.json::<Vec<Comments>>()
.await?;
Ok(comments.pop().unwrap())
} else {
Ok(self
.client
.get(&url.to_owned())
.send()
.await?
.json::<Comments>()
.await?)
}
}
#[maybe_async::maybe_async]
pub async fn hot(
&self,
limit: u32,
options: Option<FeedOption>,
) -> Result<Submissions, RouxError> {
self.get_feed("hot", limit, options).await
}
#[maybe_async::maybe_async]
pub async fn rising(
&self,
limit: u32,
options: Option<FeedOption>,
) -> Result<Submissions, RouxError> {
self.get_feed("rising", limit, options).await
}
#[maybe_async::maybe_async]
pub async fn top(
&self,
limit: u32,
options: Option<FeedOption>,
) -> Result<Submissions, RouxError> {
self.get_feed("top", limit, options).await
}
#[maybe_async::maybe_async]
pub async fn latest(
&self,
limit: u32,
options: Option<FeedOption>,
) -> Result<Submissions, RouxError> {
self.get_feed("new", limit, options).await
}
#[maybe_async::maybe_async]
pub async fn latest_comments(
&self,
depth: Option<u32>,
limit: Option<u32>,
) -> Result<Comments, RouxError> {
self.get_comment_feed("comments", depth, limit).await
}
#[maybe_async::maybe_async]
pub async fn article_comments(
&self,
article: &str,
depth: Option<u32>,
limit: Option<u32>,
) -> Result<Comments, RouxError> {
self.get_comment_feed(&format!("comments/{}", article), depth, limit)
.await
}
}
#[cfg(test)]
mod tests {
use super::Subreddit;
use super::Subreddits;
#[maybe_async::async_impl]
#[tokio::test]
async fn test_no_auth() {
let subreddit = Subreddit::new("astolfo");
let hot = subreddit.hot(25, None).await;
assert!(hot.is_ok());
let rising = subreddit.rising(25, None).await;
assert!(rising.is_ok());
let top = subreddit.top(25, None).await;
assert!(top.is_ok());
let latest_comments = subreddit.latest_comments(None, Some(25)).await;
assert!(latest_comments.is_ok());
let article_id = &hot.unwrap().data.children.first().unwrap().data.id.clone();
let article_comments = subreddit.article_comments(article_id, None, Some(25)).await;
assert!(article_comments.is_ok());
let data_res = subreddit.about().await;
assert!(data_res.is_ok());
let data = data_res.unwrap();
assert!(data.title == Some(String::from("Rider of Black, Astolfo")));
assert!(data.subscribers.is_some());
assert!(data.subscribers.unwrap() > 1000);
assert!(subreddit.moderators().await.is_err());
let subreddits_limit = 3u32;
let subreddits = Subreddits::search("rust", Some(subreddits_limit), None).await;
assert!(subreddits.is_ok());
assert!(subreddits.unwrap().data.children.len() == subreddits_limit as usize);
}
}