use chrono::{DateTime, FixedOffset}; use std::convert::identity; pub trait ToMd { fn to_md(&self) -> String; } #[derive(Debug)] pub struct Doc<'src> { pub entries: Vec>, } impl<'src> Doc<'src> { // TODO: better parsing errors? pub fn new(f: &'src str) -> Option { let entries = f .split("\n## ") .map(|s| s.split_once("\n")) .skip(1) .filter_map(identity) .map(|(title, content)| (title.split_once(": "), content)) .map(|(title, content)| { if let Some((ts, title)) = title { Some(Entry { timestamp: DateTime::parse_from_rfc3339(ts).unwrap(), title, content: content.trim_matches('\n'), }) } else { None } }) .collect::>(); entries.iter().all(|it| it.is_some()).then_some(Self { entries: entries.into_iter().filter_map(identity).collect(), }) } } impl ToMd for Doc<'_> { fn to_md(&self) -> String { let mut r = "# Journal\n\n".to_owned(); self.entries.iter().fold(r, |mut r, it| r + &it.to_md()) } } #[derive(Debug, Clone)] pub struct Entry<'src> { pub timestamp: DateTime, pub title: &'src str, pub content: &'src str, } impl ToMd for Entry<'_> { fn to_md(&self) -> String { format!( "## {}: {}\n\n{}\n\n", self.timestamp .fixed_offset() .to_rfc3339_opts(chrono::SecondsFormat::Secs, false), self.title, self.content ) } }