Added upvotes and downvotes
Changes: - Added upvotes and downvotes parsing. - Added text to read from selftext and selftext_html. - Added an texttype to imply what type of text is present (HTML or raw text) - Added some documentation. - A few minor changes (I hope). - Removed the pinned field. Todo: - Remove texttype field and parse the selftext_html into raw text
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
/target
|
/target
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
rust.json
|
rust.json
|
||||||
|
post.json
|
||||||
|
*.json
|
||||||
|
|||||||
+34
-7
@@ -1,17 +1,44 @@
|
|||||||
|
//!# Reddit api wrapper
|
||||||
|
//!Example program
|
||||||
|
//!You need tokio to use this.
|
||||||
|
//!Make sure to use `tokio = { version = "1.3.*", features = ["full"] }`
|
||||||
|
//!```rust
|
||||||
|
//!extern crate rapr;
|
||||||
|
//!extern crate tokio;
|
||||||
|
//!use crate::rapr::{RaprClient,RaSub};
|
||||||
|
//!#[tokio::main]
|
||||||
|
//!async fn main() {
|
||||||
|
//! let client = RaprClient::new();
|
||||||
|
//! let mut sub = RaSub::new("pics");
|
||||||
|
//! client.fetch(10, &mut sub).await.unwrap();
|
||||||
|
//! for post in sub.posts {
|
||||||
|
//! println!("{}",post.title);
|
||||||
|
//! }
|
||||||
|
//!}
|
||||||
mod rapr;
|
mod rapr;
|
||||||
pub use crate::rapr::RaprClient;
|
pub use crate::rapr::{RaPost, RaSub, RaprClient};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::rapr::{RaSub, RaprClient};
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn subreddit() {
|
async fn subreddit() {
|
||||||
use crate::rapr::RaprClient;
|
|
||||||
let client = RaprClient::new();
|
let client = RaprClient::new();
|
||||||
let mut sub = RaprClient::subreddit("rust");
|
let mut sub = RaSub::new("rust");
|
||||||
client.fetch(10, &mut sub).await.unwrap();
|
client.fetch(10, &mut sub).await.unwrap();
|
||||||
println!("{:#?}", sub.posts.len());
|
// println!("{:#?}", sub.posts.len());
|
||||||
client.fetch(5, &mut sub).await.unwrap();
|
// client.fetch(5, &mut sub).await.unwrap();
|
||||||
println!("{:#?}", sub.posts.len());
|
for post in sub.posts {
|
||||||
println!("{:#?}", sub.pinned_posts());
|
println!("{:#?}", post);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[tokio::test]
|
||||||
|
async fn title() {
|
||||||
|
let client = RaprClient::new();
|
||||||
|
let mut sub = RaSub::new("rust");
|
||||||
|
client.fetch(10, &mut sub).await.unwrap();
|
||||||
|
for post in sub.posts {
|
||||||
|
println!("{}", post.title);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+82
-31
@@ -10,14 +10,27 @@ pub enum Error {
|
|||||||
HttpGetError,
|
HttpGetError,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum TextType {
|
||||||
|
HTML,
|
||||||
|
Raw,
|
||||||
|
Empty,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reddit post object
|
||||||
|
/// (more like repost object).
|
||||||
|
/// Keeps track of post information.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RaPost {
|
pub struct RaPost {
|
||||||
id: String,
|
pub id: String,
|
||||||
datetime: DateTime<Local>,
|
pub datetime: DateTime<Local>,
|
||||||
title: String,
|
pub title: String,
|
||||||
text: Option<String>,
|
pub text: Option<String>,
|
||||||
json: json::JsonValue,
|
texttype: TextType,
|
||||||
pinned: bool,
|
pub permalink: String,
|
||||||
|
pub upvotes: u32,
|
||||||
|
pub downvotes: u32,
|
||||||
|
pub json: json::JsonValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for RaPost {
|
impl fmt::Debug for RaPost {
|
||||||
@@ -27,31 +40,73 @@ impl fmt::Debug for RaPost {
|
|||||||
.field("datetime", &self.datetime)
|
.field("datetime", &self.datetime)
|
||||||
.field("title", &self.title)
|
.field("title", &self.title)
|
||||||
.field("text", &self.text)
|
.field("text", &self.text)
|
||||||
|
.field("texttype", &self.texttype)
|
||||||
|
.field("upvotes", &self.upvotes)
|
||||||
|
.field("downvotes", &self.downvotes)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RaPost {
|
impl RaPost {
|
||||||
pub fn new(id: &str, title: &str, json: &json::JsonValue, pinned: bool) -> Self {
|
/// Generate new post object (you probably don't need this).
|
||||||
|
pub fn new(
|
||||||
|
id: &str,
|
||||||
|
title: &str,
|
||||||
|
text: Option<&str>,
|
||||||
|
texttype: TextType,
|
||||||
|
permalink: &str,
|
||||||
|
upvotes: u32,
|
||||||
|
downvotes: u32,
|
||||||
|
json: &json::JsonValue,
|
||||||
|
) -> Self {
|
||||||
|
let _text = match text {
|
||||||
|
Some(text) => Some(text.to_string()),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
Self {
|
Self {
|
||||||
id: id.to_string(),
|
id: id.to_string(),
|
||||||
title: title.to_string(),
|
title: title.to_string(),
|
||||||
datetime: Local::now(), // Temp
|
datetime: Local::now(), // Temp
|
||||||
text: Some(String::from("")),
|
text: _text,
|
||||||
|
texttype,
|
||||||
|
permalink: String::from(permalink),
|
||||||
|
upvotes,
|
||||||
|
downvotes,
|
||||||
json: json.clone(),
|
json: json.clone(),
|
||||||
pinned,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Parse json from `json['data']['children']` array elements.
|
||||||
pub fn parse(post: &json::JsonValue) -> Result<RaPost, json::Error> {
|
pub fn parse(post: &json::JsonValue) -> Result<RaPost, json::Error> {
|
||||||
|
let mut text: Option<&str> = post["selftext"].as_str();
|
||||||
|
let mut texttype: TextType = TextType::Empty;
|
||||||
|
// Reddit always returns an empty string on selftext
|
||||||
|
// if there is no text. So this shouldn't panic!
|
||||||
|
// Some also have both selftext and selftext_html
|
||||||
|
// I am only taking selftext from these.
|
||||||
|
if text.unwrap().is_empty() {
|
||||||
|
text = post["selftext_html"].as_str();
|
||||||
|
if text != None {
|
||||||
|
texttype = TextType::HTML;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
texttype = TextType::Raw;
|
||||||
|
}
|
||||||
Ok(RaPost::new(
|
Ok(RaPost::new(
|
||||||
json::stringify(post["name"].clone()).as_str(),
|
post["name"].as_str().unwrap(),
|
||||||
json::stringify(post["title"].clone()).as_str(),
|
post["title"].as_str().unwrap(),
|
||||||
|
text,
|
||||||
|
texttype,
|
||||||
|
post["permalink"].as_str().unwrap(),
|
||||||
|
post["ups"].as_u32().unwrap(),
|
||||||
|
post["downs"].as_u32().unwrap(),
|
||||||
post,
|
post,
|
||||||
post["pinned"].as_bool().unwrap(),
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///# RaSub
|
||||||
|
///Subreddit object
|
||||||
|
///Keeps track of posts
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RaSub {
|
pub struct RaSub {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@@ -60,21 +115,19 @@ pub struct RaSub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RaSub {
|
impl RaSub {
|
||||||
pub fn pinned_posts(&self) -> Option<Vec<RaPost>> {
|
/// Generate subreddit object
|
||||||
let mut pinned_posts: Vec<RaPost> = Vec::new();
|
pub fn new(name: &str) -> RaSub {
|
||||||
for post in &self.posts {
|
RaSub {
|
||||||
if post.pinned {
|
name: String::from(name),
|
||||||
pinned_posts.push(post.clone());
|
posts: Vec::new(),
|
||||||
}
|
after: None,
|
||||||
}
|
|
||||||
if pinned_posts.is_empty() {
|
|
||||||
return None;
|
|
||||||
} else {
|
|
||||||
return Some(pinned_posts);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reddit api client.
|
||||||
|
/// Uses a [reqwest::Client](https://docs.rs/reqwest/0.11.2/reqwest/struct.Client.html) internally.
|
||||||
|
/// Currently no authentication
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RaprClient {
|
pub struct RaprClient {
|
||||||
oauth: Option<String>,
|
oauth: Option<String>,
|
||||||
@@ -88,13 +141,9 @@ impl RaprClient {
|
|||||||
rwclient: reqwest::Client::new(),
|
rwclient: reqwest::Client::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn subreddit(name: &str) -> RaSub {
|
|
||||||
RaSub {
|
/// Fetch posts from subreddit and store them in the subreddit object.
|
||||||
name: String::from(name),
|
/// Note: First fetch always seems to pull two pinned posts which are not marked pinned in the json
|
||||||
posts: Vec::new(),
|
|
||||||
after: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub async fn fetch(&self, count: u32, sub: &mut RaSub) -> Result<Vec<RaPost>, Error> {
|
pub async fn fetch(&self, count: u32, sub: &mut RaSub) -> Result<Vec<RaPost>, Error> {
|
||||||
let url = match self.oauth {
|
let url = match self.oauth {
|
||||||
None => format!("https://reddit.com/r/{}.json", sub.name),
|
None => format!("https://reddit.com/r/{}.json", sub.name),
|
||||||
@@ -128,7 +177,9 @@ impl RaprClient {
|
|||||||
let mut parsed_posts: Vec<RaPost> = Vec::new();
|
let mut parsed_posts: Vec<RaPost> = Vec::new();
|
||||||
|
|
||||||
for post in raw_posts {
|
for post in raw_posts {
|
||||||
parsed_posts.push(RaPost::parse(&post["data"]).unwrap());
|
if post["kind"].as_str().unwrap() == "t3" {
|
||||||
|
parsed_posts.push(RaPost::parse(&post["data"]).unwrap());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if parsed["data"]["after"].is_string() {
|
if parsed["data"]["after"].is_string() {
|
||||||
sub.after = Some(parsed["data"]["after"].to_string());
|
sub.after = Some(parsed["data"]["after"].to_string());
|
||||||
|
|||||||
Reference in New Issue
Block a user