use anyhow::{anyhow, Result}; use atom_syndication as atom; use maj::gemini::Node; use rustls::ClientConfig; use std::io::{self, BufReader, Cursor, Write}; use std::ops::Sub; use std::str; use chrono::{Duration, prelude::*}; mod tls; fn gem_to_md(tcana: Vec, out: &mut impl Write) -> io::Result<()> { use Node::*; for tcan in tcana { match tcan { Text(body) => { if body == "---" { break; } write!(out, "{}\n", body)?; } Link { to, name } => match name { Some(name) => write!(out, "[{}]({})\n\n", name, to)?, None => write!(out, "[{0}]({0})", to)?, }, Preformatted(body) => write!(out, "```\n{}\n```\n\n", body)?, Heading { level, body } => { write!(out, "##{} {}\n\n", "#".repeat(level as usize), body)? } ListItem(body) => write!(out, "* {}\n", body)?, Quote(body) => write!(out, "> {}\n\n", body)?, } } Ok(()) } async fn read_feed(gurl: String, cfg: ClientConfig) -> Result { let resp = maj::get(gurl, cfg).await?; if resp.status != maj::StatusCode::Success { Err(anyhow!( "expected success, got: {} {}", resp.status as u8, resp.meta ))?; } let body = Cursor::new(resp.body); let feed = atom::Feed::read_from(BufReader::new(body))?; Ok(feed) } pub async fn run(nuzyurli: Vec, seldei: usize) -> Result<()> { let cfg = tls::config(); let ca: atom::FixedDateTime = Utc::now().into(); for urli in nuzyurli { let feed = read_feed(urli.clone(), cfg.clone()).await?; log::info!("reading entries for {}: {}", urli, feed.title); println!("## {}\nBy {}\n\n", feed.title, feed.authors[0].name); println!("[Site link]({})\n\n", feed.id); for entry in feed.entries { if ca.sub(entry.updated) > Duration::days(seldei as i64) { continue; } let href: String = entry.links()[0].href.clone(); let resp = maj::get(href, cfg.clone()).await?; if resp.status != maj::StatusCode::Success { return Err(anyhow!( "expected success, got: {} {}", resp.status as u8, resp.meta )); } let body = str::from_utf8(&resp.body)?; let body = maj::gemini::parse(body); let mut buf: Vec = vec![]; gem_to_md(body, &mut buf)?; println!("{}", str::from_utf8(&buf)?); } } Ok(()) }