From: Dessalines Date: Sun, 8 Dec 2019 01:57:30 +0000 (-0800) Subject: Merge branch 'teromene-config_dif_addr' into config_dif_addr_merge X-Git-Url: http://these/git/%7B%60%24%7BwebArchiveUrl%7D/%22%7B%7D/readmes/%7B%7D/%22%7Burl%7D/README.ru.md?a=commitdiff_plain;h=2325690cb60ca27a8850512e117a18dd3111bb63;p=lemmy.git Merge branch 'teromene-config_dif_addr' into config_dif_addr_merge --- 2325690cb60ca27a8850512e117a18dd3111bb63 diff --cc server/src/db/post_view.rs index e4e3c16d,cdf75554..7a589738 --- a/server/src/db/post_view.rs +++ b/server/src/db/post_view.rs @@@ -73,62 -75,154 +75,154 @@@ pub struct PostView pub saved: Option, } - impl PostView { - pub fn list( - conn: &PgConnection, - type_: ListingType, - sort: &SortType, - for_community_id: Option, - for_creator_id: Option, - search_term: Option, - url_search: Option, - my_user_id: Option, - show_nsfw: bool, - saved_only: bool, - unread_only: bool, - page: Option, - limit: Option, - ) -> Result, Error> { + pub struct PostQueryBuilder<'a> { + conn: &'a PgConnection, + query: BoxedQuery<'a, Pg>, + listing_type: ListingType, + sort: &'a SortType, + my_user_id: Option, + for_creator_id: Option, + show_nsfw: bool, + saved_only: bool, + unread_only: bool, + page: Option, + limit: Option, + } + + impl<'a> PostQueryBuilder<'a> { + pub fn create(conn: &'a PgConnection) -> Self { use super::post_view::post_view::dsl::*; - let (limit, offset) = limit_and_offset(page, limit); + let query = post_view.into_boxed(); + + PostQueryBuilder { + conn, + query, + my_user_id: None, + for_creator_id: None, + listing_type: ListingType::All, + sort: &SortType::Hot, - show_nsfw: false, ++ show_nsfw: true, + saved_only: false, + unread_only: false, + page: None, + limit: None, + } + } - let mut query = post_view.into_boxed(); + pub fn listing_type(mut self, listing_type: ListingType) -> Self { + self.listing_type = listing_type; + self + } - // If its for a specific user, show the removed / deleted - if let Some(for_creator_id) = for_creator_id { - query = query.filter(creator_id.eq(for_creator_id)); - } else { - query = query - .filter(removed.eq(false)) - .filter(deleted.eq(false)) - .filter(community_removed.eq(false)) - .filter(community_deleted.eq(false)); - }; + pub fn sort(mut self, sort: &'a SortType) -> Self { + self.sort = sort; + self + } - if let Some(search_term) = search_term { - query = query.filter(name.ilike(fuzzy_search(&search_term))); - }; + pub fn for_community_id(mut self, for_community_id: i32) -> Self { + use super::post_view::post_view::dsl::*; + self.query = self.query.filter(community_id.eq(for_community_id)); + self.query = self.query.then_order_by(stickied.desc()); + self + } - if let Some(url_search) = url_search { - query = query.filter(url.eq(url_search)); - }; + pub fn for_community_id_optional(self, for_community_id: Option) -> Self { + match for_community_id { + Some(for_community_id) => self.for_community_id(for_community_id), + None => self, + } + } - if let Some(for_community_id) = for_community_id { - query = query.filter(community_id.eq(for_community_id)); - query = query.then_order_by(stickied.desc()); - }; + pub fn for_creator_id(mut self, for_creator_id: i32) -> Self { + self.for_creator_id = Some(for_creator_id); + self + } - // TODO these are wrong, bc they'll only show saved for your logged in user, not theirs - if saved_only { - query = query.filter(saved.eq(true)); - }; + pub fn for_creator_id_optional(self, for_creator_id: Option) -> Self { + match for_creator_id { + Some(for_creator_id) => self.for_creator_id(for_creator_id), + None => self, + } + } - if unread_only { - query = query.filter(read.eq(false)); - }; + pub fn search_term(mut self, search_term: String) -> Self { + use super::post_view::post_view::dsl::*; + self.query = self.query.filter(name.ilike(fuzzy_search(&search_term))); + self + } - match type_ { + pub fn search_term_optional(self, search_term: Option) -> Self { + match search_term { + Some(search_term) => self.search_term(search_term), + None => self, + } + } + + pub fn url_search(mut self, url_search: String) -> Self { + use super::post_view::post_view::dsl::*; + self.query = self.query.filter(url.eq(url_search)); + self + } + + pub fn url_search_optional(self, url_search: Option) -> Self { + match url_search { + Some(url_search) => self.url_search(url_search), + None => self, + } + } + + pub fn my_user_id(mut self, my_user_id: i32) -> Self { + self.my_user_id = Some(my_user_id); + self + } + + pub fn my_user_id_optional(mut self, my_user_id: Option) -> Self { + self.my_user_id = my_user_id; + self + } + + pub fn show_nsfw(mut self, show_nsfw: bool) -> Self { + self.show_nsfw = show_nsfw; + self + } + + pub fn saved_only(mut self, saved_only: bool) -> Self { + self.saved_only = saved_only; + self + } + + pub fn unread_only(mut self, unread_only: bool) -> Self { + self.unread_only = unread_only; + self + } + + pub fn page(mut self, page: i64) -> Self { + self.page = Some(page); + self + } + + pub fn page_optional(mut self, page: Option) -> Self { + self.page = page; + self + } + + pub fn limit(mut self, limit: i64) -> Self { + self.limit = Some(limit); + self + } + + pub fn limit_optional(mut self, limit: Option) -> Self { + self.limit = limit; + self + } + + pub fn list(self) -> Result, Error> { + use super::post_view::post_view::dsl::*; + + let mut query = self.query; + + match self.listing_type { ListingType::Subscribed => { query = query.filter(subscribed.eq(true)); } diff --cc server/src/feeds.rs index 737207fa,19dd1121..66c1b02a --- a/server/src/feeds.rs +++ b/server/src/feeds.rs @@@ -1,13 -1,12 +1,13 @@@ extern crate rss; use super::*; +use crate::db::comment_view::ReplyView; use crate::db::community::Community; use crate::db::community_view::SiteView; - use crate::db::post_view::PostView; -use crate::db::post_view::PostQueryBuilder; ++use crate::db::post_view::{PostView, PostQueryBuilder}; use crate::db::user::User_; -use crate::db::{establish_connection, SortType}; +use crate::db::user_mention_view::UserMentionView; +use crate::db::{establish_connection, ListingType, SortType}; use crate::Settings; use actix_web::body::Body; use actix_web::{web, HttpResponse, Result}; @@@ -81,258 -66,71 +81,217 @@@ fn get_sort_type(info: web::Query, -) -> Result { +fn get_feed_all_data(sort_type: &SortType) -> Result { let conn = establish_connection(); - let mut community_id: Option = None; - let mut creator_id: Option = None; + let site_view = SiteView::read(&conn)?; + - let posts = PostView::list( - &conn, - ListingType::All, - sort_type, - None, - None, - None, - None, - None, - true, - false, - false, - None, - None, - )?; ++ let posts = PostQueryBuilder::create(&conn) ++ .listing_type(ListingType::All) ++ .sort(sort_type) ++ .list()?; + + let items = create_post_items(posts); + + let mut channel_builder = ChannelBuilder::default(); + channel_builder + .title(&format!("{} - All", site_view.name)) + .link(format!("https://{}", Settings::get().hostname)) + .items(items); + + if let Some(site_desc) = site_view.description { + channel_builder.description(&site_desc); + } + + Ok(channel_builder.build().unwrap().to_string()) +} + +fn get_feed_user(sort_type: &SortType, user_name: String) -> Result { + let conn = establish_connection(); let site_view = SiteView::read(&conn)?; + let user = User_::find_by_email_or_username(&conn, &user_name)?; + let user_url = format!("https://{}/u/{}", Settings::get().hostname, user.name); + - let posts = PostView::list( - &conn, - ListingType::All, - sort_type, - None, - Some(user.id), - None, - None, - None, - true, - false, - false, - None, - None, - )?; ++ let posts = PostQueryBuilder::create(&conn) ++ .listing_type(ListingType::All) ++ .sort(sort_type) ++ .for_creator_id(user.id) ++ .list()?; + + let items = create_post_items(posts); let mut channel_builder = ChannelBuilder::default(); + channel_builder + .title(&format!("{} - {}", site_view.name, user.name)) + .link(user_url) + .items(items); - // TODO do channel image, need to externalize + Ok(channel_builder.build().unwrap().to_string()) +} - match request_type { - RequestType::All => { - channel_builder - .title(htmlescape::encode_minimal(&site_view.name)) - .link(format!("https://{}", Settings::get().hostname)); +fn get_feed_community(sort_type: &SortType, community_name: String) -> Result { + let conn = establish_connection(); - if let Some(site_desc) = site_view.description { - channel_builder.description(htmlescape::encode_minimal(&site_desc)); - } - } - RequestType::Community => { - let community = Community::read_from_name(&conn, name.unwrap())?; - community_id = Some(community.id); - - let community_url = format!("https://{}/c/{}", Settings::get().hostname, community.name); - - channel_builder - .title(htmlescape::encode_minimal(&format!( - "{} - {}", - site_view.name, community.name - ))) - .link(community_url); - - if let Some(community_desc) = community.description { - channel_builder.description(htmlescape::encode_minimal(&community_desc)); - } - } - RequestType::User => { - let creator = User_::find_by_email_or_username(&conn, &name.unwrap())?; - creator_id = Some(creator.id); - - let creator_url = format!("https://{}/u/{}", Settings::get().hostname, creator.name); - - channel_builder - .title(htmlescape::encode_minimal(&format!( - "{} - {}", - site_view.name, creator.name - ))) - .link(creator_url); - } + let site_view = SiteView::read(&conn)?; + let community = Community::read_from_name(&conn, community_name)?; + let community_url = format!("https://{}/c/{}", Settings::get().hostname, community.name); + - let posts = PostView::list( - &conn, - ListingType::All, - sort_type, - Some(community.id), - None, - None, - None, - None, - true, - false, - false, - None, - None, - )?; ++ let posts = PostQueryBuilder::create(&conn) ++ .listing_type(ListingType::All) ++ .sort(sort_type) ++ .for_community_id(community.id) ++ .list()?; + + let items = create_post_items(posts); + + let mut channel_builder = ChannelBuilder::default(); + channel_builder + .title(&format!("{} - {}", site_view.name, community.name)) + .link(community_url) + .items(items); + + if let Some(community_desc) = community.description { + channel_builder.description(&community_desc); } + Ok(channel_builder.build().unwrap().to_string()) +} + +fn get_feed_front(sort_type: &SortType, jwt: String) -> Result { + let conn = establish_connection(); + + let site_view = SiteView::read(&conn)?; + let user_id = db::user::Claims::decode(&jwt)?.claims.id; + - let posts = PostView::list( - &conn, - ListingType::Subscribed, - sort_type, - None, - None, - None, - None, - Some(user_id), - true, - false, - false, - None, - None, - )?; + let posts = PostQueryBuilder::create(&conn) ++ .listing_type(ListingType::Subscribed) + .sort(sort_type) - .show_nsfw(true) - .for_community_id_optional(community_id) - .for_creator_id_optional(creator_id) ++ .my_user_id(user_id) + .list()?; + let items = create_post_items(posts); + + let mut channel_builder = ChannelBuilder::default(); + channel_builder + .title(&format!("{} - Subscribed", site_view.name)) + .link(format!("https://{}", Settings::get().hostname)) + .items(items); + + if let Some(site_desc) = site_view.description { + channel_builder.description(&site_desc); + } + + Ok(channel_builder.build().unwrap().to_string()) +} + +fn get_feed_inbox(jwt: String) -> Result { + let conn = establish_connection(); + + let site_view = SiteView::read(&conn)?; + let user_id = db::user::Claims::decode(&jwt)?.claims.id; + + let sort = SortType::New; + + let replies = ReplyView::get_replies(&conn, user_id, &sort, false, None, None)?; + + let mentions = UserMentionView::get_mentions(&conn, user_id, &sort, false, None, None)?; + + let items = create_reply_and_mention_items(replies, mentions); + + let mut channel_builder = ChannelBuilder::default(); + channel_builder + .title(&format!("{} - Inbox", site_view.name)) + .link(format!("https://{}/inbox", Settings::get().hostname)) + .items(items); + + if let Some(site_desc) = site_view.description { + channel_builder.description(&site_desc); + } + + Ok(channel_builder.build().unwrap().to_string()) +} + +fn create_reply_and_mention_items( + replies: Vec, + mentions: Vec, +) -> Vec { + let mut items: Vec = Vec::new(); + + for r in replies { + let mut i = ItemBuilder::default(); + + i.title(format!("Reply from {}", r.creator_name)); + + let author_url = format!("https://{}/u/{}", Settings::get().hostname, r.creator_name); + i.author(format!( + "/u/{} (link)", + r.creator_name, author_url + )); + + let dt = DateTime::::from_utc(r.published, Utc); + i.pub_date(dt.to_rfc2822()); + + let reply_url = format!( + "https://{}/post/{}/comment/{}", + Settings::get().hostname, + r.post_id, + r.id + ); + i.comments(reply_url.to_owned()); + let guid = GuidBuilder::default() + .permalink(true) + .value(&reply_url) + .build(); + i.guid(guid.unwrap()); + + i.link(reply_url); + + // TODO find a markdown to html parser here, do images, etc + i.description(r.content); + + items.push(i.build().unwrap()); + } + + for m in mentions { + let mut i = ItemBuilder::default(); + + i.title(format!("Mention from {}", m.creator_name)); + + let author_url = format!("https://{}/u/{}", Settings::get().hostname, m.creator_name); + i.author(format!( + "/u/{} (link)", + m.creator_name, author_url + )); + + let dt = DateTime::::from_utc(m.published, Utc); + i.pub_date(dt.to_rfc2822()); + + let mention_url = format!( + "https://{}/post/{}/comment/{}", + Settings::get().hostname, + m.post_id, + m.id + ); + i.comments(mention_url.to_owned()); + let guid = GuidBuilder::default() + .permalink(true) + .value(&mention_url) + .build(); + i.guid(guid.unwrap()); + + i.link(mention_url); + + // TODO find a markdown to html parser here, do images, etc + i.description(m.content); + + items.push(i.build().unwrap()); + } + + items +} + +fn create_post_items(posts: Vec) -> Vec { let mut items: Vec = Vec::new(); for p in posts { diff --cc server/src/feeds.rs.orig index 00000000,00000000..e304665c new file mode 100644 --- /dev/null +++ b/server/src/feeds.rs.orig @@@ -1,0 -1,0 +1,413 @@@ ++extern crate rss; ++ ++use super::*; ++use crate::db::comment_view::ReplyView; ++use crate::db::community::Community; ++use crate::db::community_view::SiteView; ++use crate::db::post_view::PostQueryBuilder; ++use crate::db::user::User_; ++<<<<<<< HEAD ++use crate::db::user_mention_view::UserMentionView; ++use crate::db::{establish_connection, ListingType, SortType}; ++======= ++use crate::db::{establish_connection, SortType}; ++>>>>>>> teromene-config_dif_addr ++use crate::Settings; ++use actix_web::body::Body; ++use actix_web::{web, HttpResponse, Result}; ++use failure::Error; ++use rss::{CategoryBuilder, ChannelBuilder, GuidBuilder, Item, ItemBuilder}; ++use serde::Deserialize; ++use std::str::FromStr; ++use strum::ParseError; ++ ++#[derive(Deserialize)] ++pub struct Params { ++ sort: Option, ++} ++ ++enum RequestType { ++ Community, ++ User, ++ Front, ++ Inbox, ++} ++ ++pub fn get_all_feed(info: web::Query) -> HttpResponse { ++ let sort_type = match get_sort_type(info) { ++ Ok(sort_type) => sort_type, ++ Err(_) => return HttpResponse::BadRequest().finish(), ++ }; ++ ++ let feed_result = get_feed_all_data(&sort_type); ++ ++ match feed_result { ++ Ok(rss) => HttpResponse::Ok() ++ .content_type("application/rss+xml") ++ .body(rss), ++ Err(_) => HttpResponse::NotFound().finish(), ++ } ++} ++ ++pub fn get_feed(path: web::Path<(String, String)>, info: web::Query) -> HttpResponse { ++ let sort_type = match get_sort_type(info) { ++ Ok(sort_type) => sort_type, ++ Err(_) => return HttpResponse::BadRequest().finish(), ++ }; ++ ++ let request_type = match path.0.as_ref() { ++ "u" => RequestType::User, ++ "c" => RequestType::Community, ++ "front" => RequestType::Front, ++ "inbox" => RequestType::Inbox, ++ _ => return HttpResponse::NotFound().finish(), ++ }; ++ ++ let param = path.1.to_owned(); ++ ++ let feed_result = match request_type { ++ RequestType::User => get_feed_user(&sort_type, param), ++ RequestType::Community => get_feed_community(&sort_type, param), ++ RequestType::Front => get_feed_front(&sort_type, param), ++ RequestType::Inbox => get_feed_inbox(param), ++ }; ++ ++ match feed_result { ++ Ok(rss) => HttpResponse::Ok() ++ .content_type("application/rss+xml") ++ .body(rss), ++ Err(_) => HttpResponse::NotFound().finish(), ++ } ++} ++ ++fn get_sort_type(info: web::Query) -> Result { ++ let sort_query = info.sort.to_owned().unwrap_or(SortType::Hot.to_string()); ++ SortType::from_str(&sort_query) ++} ++ ++fn get_feed_all_data(sort_type: &SortType) -> Result { ++ let conn = establish_connection(); ++ ++ let site_view = SiteView::read(&conn)?; ++ ++ let posts = PostView::list( ++ &conn, ++ ListingType::All, ++ sort_type, ++ None, ++ None, ++ None, ++ None, ++ None, ++ true, ++ false, ++ false, ++ None, ++ None, ++ )?; ++ ++ let items = create_post_items(posts); ++ ++ let mut channel_builder = ChannelBuilder::default(); ++ channel_builder ++ .title(&format!("{} - All", site_view.name)) ++ .link(format!("https://{}", Settings::get().hostname)) ++ .items(items); ++ ++ if let Some(site_desc) = site_view.description { ++ channel_builder.description(&site_desc); ++ } ++ ++ Ok(channel_builder.build().unwrap().to_string()) ++} ++ ++fn get_feed_user(sort_type: &SortType, user_name: String) -> Result { ++ let conn = establish_connection(); ++ ++ let site_view = SiteView::read(&conn)?; ++ let user = User_::find_by_email_or_username(&conn, &user_name)?; ++ let user_url = format!("https://{}/u/{}", Settings::get().hostname, user.name); ++ ++ let posts = PostView::list( ++ &conn, ++ ListingType::All, ++ sort_type, ++ None, ++ Some(user.id), ++ None, ++ None, ++ None, ++ true, ++ false, ++ false, ++ None, ++ None, ++ )?; ++ ++ let items = create_post_items(posts); ++ ++ let mut channel_builder = ChannelBuilder::default(); ++ channel_builder ++ .title(&format!("{} - {}", site_view.name, user.name)) ++ .link(user_url) ++ .items(items); ++ ++ Ok(channel_builder.build().unwrap().to_string()) ++} ++ ++fn get_feed_community(sort_type: &SortType, community_name: String) -> Result { ++ let conn = establish_connection(); ++ ++ let site_view = SiteView::read(&conn)?; ++ let community = Community::read_from_name(&conn, community_name)?; ++ let community_url = format!("https://{}/c/{}", Settings::get().hostname, community.name); ++ ++<<<<<<< HEAD ++ let posts = PostView::list( ++ &conn, ++ ListingType::All, ++ sort_type, ++ Some(community.id), ++ None, ++ None, ++ None, ++ None, ++ true, ++ false, ++ false, ++ None, ++ None, ++ )?; ++======= ++ let posts = PostQueryBuilder::create(&conn) ++ .sort(sort_type) ++ .show_nsfw(true) ++ .for_community_id_optional(community_id) ++ .for_creator_id_optional(creator_id) ++ .list()?; ++>>>>>>> teromene-config_dif_addr ++ ++ let items = create_post_items(posts); ++ ++ let mut channel_builder = ChannelBuilder::default(); ++ channel_builder ++ .title(&format!("{} - {}", site_view.name, community.name)) ++ .link(community_url) ++ .items(items); ++ ++ if let Some(community_desc) = community.description { ++ channel_builder.description(&community_desc); ++ } ++ ++ Ok(channel_builder.build().unwrap().to_string()) ++} ++ ++fn get_feed_front(sort_type: &SortType, jwt: String) -> Result { ++ let conn = establish_connection(); ++ ++ let site_view = SiteView::read(&conn)?; ++ let user_id = db::user::Claims::decode(&jwt)?.claims.id; ++ ++ let posts = PostView::list( ++ &conn, ++ ListingType::Subscribed, ++ sort_type, ++ None, ++ None, ++ None, ++ None, ++ Some(user_id), ++ true, ++ false, ++ false, ++ None, ++ None, ++ )?; ++ ++ let items = create_post_items(posts); ++ ++ let mut channel_builder = ChannelBuilder::default(); ++ channel_builder ++ .title(&format!("{} - Subscribed", site_view.name)) ++ .link(format!("https://{}", Settings::get().hostname)) ++ .items(items); ++ ++ if let Some(site_desc) = site_view.description { ++ channel_builder.description(&site_desc); ++ } ++ ++ Ok(channel_builder.build().unwrap().to_string()) ++} ++ ++fn get_feed_inbox(jwt: String) -> Result { ++ let conn = establish_connection(); ++ ++ let site_view = SiteView::read(&conn)?; ++ let user_id = db::user::Claims::decode(&jwt)?.claims.id; ++ ++ let sort = SortType::New; ++ ++ let replies = ReplyView::get_replies(&conn, user_id, &sort, false, None, None)?; ++ ++ let mentions = UserMentionView::get_mentions(&conn, user_id, &sort, false, None, None)?; ++ ++ let items = create_reply_and_mention_items(replies, mentions); ++ ++ let mut channel_builder = ChannelBuilder::default(); ++ channel_builder ++ .title(&format!("{} - Inbox", site_view.name)) ++ .link(format!("https://{}/inbox", Settings::get().hostname)) ++ .items(items); ++ ++ if let Some(site_desc) = site_view.description { ++ channel_builder.description(&site_desc); ++ } ++ ++ Ok(channel_builder.build().unwrap().to_string()) ++} ++ ++fn create_reply_and_mention_items( ++ replies: Vec, ++ mentions: Vec, ++) -> Vec { ++ let mut items: Vec = Vec::new(); ++ ++ for r in replies { ++ let mut i = ItemBuilder::default(); ++ ++ i.title(format!("Reply from {}", r.creator_name)); ++ ++ let author_url = format!("https://{}/u/{}", Settings::get().hostname, r.creator_name); ++ i.author(format!( ++ "/u/{} (link)", ++ r.creator_name, author_url ++ )); ++ ++ let dt = DateTime::::from_utc(r.published, Utc); ++ i.pub_date(dt.to_rfc2822()); ++ ++ let reply_url = format!( ++ "https://{}/post/{}/comment/{}", ++ Settings::get().hostname, ++ r.post_id, ++ r.id ++ ); ++ i.comments(reply_url.to_owned()); ++ let guid = GuidBuilder::default() ++ .permalink(true) ++ .value(&reply_url) ++ .build(); ++ i.guid(guid.unwrap()); ++ ++ i.link(reply_url); ++ ++ // TODO find a markdown to html parser here, do images, etc ++ i.description(r.content); ++ ++ items.push(i.build().unwrap()); ++ } ++ ++ for m in mentions { ++ let mut i = ItemBuilder::default(); ++ ++ i.title(format!("Mention from {}", m.creator_name)); ++ ++ let author_url = format!("https://{}/u/{}", Settings::get().hostname, m.creator_name); ++ i.author(format!( ++ "/u/{} (link)", ++ m.creator_name, author_url ++ )); ++ ++ let dt = DateTime::::from_utc(m.published, Utc); ++ i.pub_date(dt.to_rfc2822()); ++ ++ let mention_url = format!( ++ "https://{}/post/{}/comment/{}", ++ Settings::get().hostname, ++ m.post_id, ++ m.id ++ ); ++ i.comments(mention_url.to_owned()); ++ let guid = GuidBuilder::default() ++ .permalink(true) ++ .value(&mention_url) ++ .build(); ++ i.guid(guid.unwrap()); ++ ++ i.link(mention_url); ++ ++ // TODO find a markdown to html parser here, do images, etc ++ i.description(m.content); ++ ++ items.push(i.build().unwrap()); ++ } ++ ++ items ++} ++ ++fn create_post_items(posts: Vec) -> Vec { ++ let mut items: Vec = Vec::new(); ++ ++ for p in posts { ++ let mut i = ItemBuilder::default(); ++ ++ i.title(p.name); ++ ++ let author_url = format!("https://{}/u/{}", Settings::get().hostname, p.creator_name); ++ i.author(format!( ++ "/u/{} (link)", ++ p.creator_name, author_url ++ )); ++ ++ let dt = DateTime::::from_utc(p.published, Utc); ++ i.pub_date(dt.to_rfc2822()); ++ ++ let post_url = format!("https://{}/post/{}", Settings::get().hostname, p.id); ++ i.comments(post_url.to_owned()); ++ let guid = GuidBuilder::default() ++ .permalink(true) ++ .value(&post_url) ++ .build(); ++ i.guid(guid.unwrap()); ++ ++ let community_url = format!( ++ "https://{}/c/{}", ++ Settings::get().hostname, ++ p.community_name ++ ); ++ ++ let category = CategoryBuilder::default() ++ .name(format!( ++ "/c/{} (link)", ++ p.community_name, community_url ++ )) ++ .domain(Settings::get().hostname) ++ .build(); ++ i.categories(vec![category.unwrap()]); ++ ++ if let Some(url) = p.url { ++ i.link(url); ++ } ++ ++ // TODO find a markdown to html parser here, do images, etc ++ let mut description = format!(" ++ submitted by {} to {}
{} points | {} comments", ++ author_url, ++ p.creator_name, ++ community_url, ++ p.community_name, ++ p.score, ++ post_url, ++ p.number_of_comments); ++ ++ if let Some(body) = p.body { ++ description.push_str(&format!("

{}", body)); ++ } ++ ++ i.description(description); ++ ++ items.push(i.build().unwrap()); ++ } ++ ++ items ++}