Info
This post is auto-generated from RSS feed The Rust Programming Language Forum - Latest topics. Source: How would you do this population of a struct?
This code is intended to:
Article
instance from an Entry
instanceOption<T>
fields, so some may be None
Article
should always be definedSo my solution so far was:
Article
always some type U
(not an Option
)Article
.What I am having trouble doing is to "populate the fields needed" (last step above). The reason is that the fields are optional, so it seems to add more repetition than I would like.
Below there is a sample. Note that it's not supposed to compile, just to get your opinion or suggestion in the problem above (or anything worth changing).
It's very beginner code so be patient please.
/*!
Article represents one entry of the feed.
Display is implemented to directly print it to STDOUT.
*/
// feel free to ignore all imports below.
use std::fmt::{self, Display, Formatter};
use colored::Colorize;
use feed_rs::model::{Entry, Text};
use html2text::{self, Error as HtmlError, config};
use unicode_segmentation::UnicodeSegmentation;
use crate::{AppError, AppResult, LINE_WIDTH, N_GRAPHEMES};
#[derive(Debug)]
pub struct Article {
pub title: String,
pub last_updated: String,
pub published: String,
pub url: String,
pub summary: String,
pub related_links: Vec<String>,
}
impl Default for Article {
/// This default article is used for displaying some information
/// When there was no error, but the parsed field is empty/None.
/// That way we also always have a constant printed structure.
fn default() -> Self {
Article {
title: String::from("No title found"),
last_updated: String::from("No update found"),
published: String::from("No date found"),
url: String::from("No url found"),
summary: String::from("No summary found"),
related_links: vec![],
}
}
}
/// Turn the HTML into a string with a specific length.
pub fn parse_summary(s: Option<Text>) -> Result<Option<String>, HtmlError> {
s.map(|mut t| {
t.sanitize();
let mut content: String = t.content.graphemes(true).take(N_GRAPHEMES).collect();
content.push_str("...");
config::rich().string_from_read(content.as_bytes(), LINE_WIDTH)
})
.transpose() // Opt<Res> --> Res<Opt>
}
/// Create a new Article from an Entry.
pub fn new_article(i: &Entry) -> AppResult {
// Could be an Article associated function (AF), but this is fine.
// I don't like to return AppResult from the AF.
let summary = match parse_summary(i.summary.clone()) {
Ok(s) => s,
Err(r) => return Err(AppError::Html2TextError(r)),
};
let title = i.title.clone().map(|mut t| {
t.sanitize();
t.content
});
let last_updated = i.updated.map(|d| d.to_string());
let published = i.updated.map(|d| d.to_string());
let mut default_article = Article::default();
// Below is how it was done prior to using ::default
// I think this would be cleaner
// But I don't know how to populate it now.
// Using if title.is_some {...} else {...} would create a similar
// situation.
Ok(Article {
title: title.unwrap_or(String::from("No title found")),
last_updated: last_updated.unwrap_or(String::from("No update found")),
published: published.unwrap_or(String::from("No date found")),
url: i.base.clone().unwrap_or(String::from("No url found")),
summary: summary.unwrap_or(String::from("No summary found")),
related_links: i.links.clone().into_iter().map(|l| l.href).collect(),
})
}
impl Display for Article {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
// Write the msg and error to the formatter.
let to_print = format!(
"\n{header}\n- Title: {title}\n- Summary: {summary}\n- Url: {url}\n- Links: {links}\n- {date}\n\n",
header = "Next Item".underline().bold(),
// Opt<T> -> new Opt<&T> -> unwrap
title = self.title.bold().yellow(),
summary = self.summary,
url = self.url.blue(),
links = self.related_links.join(",\n").blue().dimmed().underline(),
date = format!("Date: {} | Updated: {}", self.published, self.last_updated)
);
write!(f, "{}", to_print)
}
}
PS: i also thought about if let
but it may still be too much repetition.
3 posts - 2 participants
🏷️ Rust_feed