Skip to content

Instantly share code, notes, and snippets.

@rvolosatovs
Created June 17, 2020 21:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rvolosatovs/7699e96db7f33b84011c9de11ce6b636 to your computer and use it in GitHub Desktop.
Save rvolosatovs/7699e96db7f33b84011c9de11ce6b636 to your computer and use it in GitHub Desktop.
// NOTE: Adapted from https://github.com/rust-syndication/syndication/blob/fb9820a3bb7d3d3d9832c1e3b976ce59b0f0a98c/src/lib.rs#L3-L21.
#[derive(Debug)]
pub enum Feed {
Atom(atom::Feed),
RSS(rss::Channel),
}
#[derive(Error, Debug)]
#[error("failed to parse XML as either Atom or RSS feed")]
pub struct FromStrError;
impl FromStr for Feed {
type Err = FromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match atom_syndication::Feed::from_str(s) {
Ok(feed) => Ok(Feed::Atom(feed)),
_ => match rss::Channel::from_str(s) {
Ok(feed) => Ok(Feed::RSS(feed)),
_ => Err(FromStrError),
},
}
}
}
#[derive(Error, Debug)]
pub enum FromURLError {
#[error("HTTP request failed")]
Reqwest(#[from] reqwest::Error),
#[error("failed to parse request body")]
Parse(#[from] FromStrError),
}
struct FeedFromHTTP<'a> {
pub feed: Feed,
pub etag: Option<&'a str>,
pub last_modified: Option<time::OffsetDateTime>,
}
fn parse_header<T, N, E, F>(headers: &HeaderMap, name: N, parse: F) -> Option<T>
where
N: AsHeaderName + std::fmt::Display,
E: Sized + std::fmt::Display,
F: FnOnce(&HeaderValue) -> Result<T, E>,
{
match headers.get(name).map(parse)? {
Ok(v) => Some(v),
Err(e) => {
error!("Failed to parse `{}` header: {}", name, e);
None
}
}
}
impl Feed {
pub async fn from_url<'a, T: reqwest::IntoUrl>(
url: T,
) -> Result<FeedFromHTTP<'a>, FromURLError> {
let resp = reqwest::get(url).await?;
let feed = resp.text().await?.parse::<Self>()?;
let headers = resp.headers();
Ok(FeedFromHTTP {
feed,
etag: parse_header(headers, "ETag", HeaderValue::to_str),
//last_modified: parse_header(headers, "Last-Modified"),
last_modified: None,
})
}
}
@rvolosatovs
Copy link
Author

Fails with:

error[E0271]: type mismatch resolving `for<'r> <for<'s> fn(&'s http::header::value::HeaderValue) -> std::result::Result<&'s str, http::header::value::ToStrError> {http::header::value::HeaderValue::to_str} as std::ops::FnOnce<(&'r http::header::value::HeaderValue,)>>::Output == std::result::Result<_, _>`
  --> src/main.rs:76:19
   |
52 | fn parse_header<T, N, E, F>(headers: &HeaderMap, name: N, parse: F) -> Option<T>
   |    ------------ required by a bound in this
...
56 |     F: FnOnce(&HeaderValue) -> Result<T, E>,
   |                                ------------ required by this bound in `parse_header`
...
76 |             etag: parse_header(headers, "ETag", HeaderValue::to_str),
   |                   ^^^^^^^^^^^^ expected bound lifetime parameter, found concrete lifetime

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment