commit 335532a2bf61068989d327bff02d6aefad748e81
parent ca0d100118acbee6d9a7040d2fc49613bb0d8f48
Author: alex wennerberg <alex@alexwennerberg.com>
Date: Sun, 20 Mar 2022 15:36:22 -0700
WIP xml
Diffstat:
6 files changed, 109 insertions(+), 95 deletions(-)
diff --git a/TODO b/TODO
@@ -2,11 +2,14 @@ TODO
====
atom feeds working -> pull last x into threads
paginate list home
+only unformat flowed if we are format flowed in gmi, html, xml
+
+feature water line
+----------
+test a ton
reference mblaze command, add examples to readme
fix docs
check for html escape bugz
-
Duplicate ID verification: warn on duplicate ID, use first received-date. This
is to prevent someone overwriting old emails secretly
-
Color highlight on anchor select
diff --git a/src/main.rs b/src/main.rs
@@ -3,6 +3,7 @@
// that is ok though
#[forbid(unsafe_code)]
use anyhow::{Context, Result};
+use mail_parser::Message;
use maildir::Maildir;
use std::collections::HashSet;
use std::fs;
@@ -22,6 +23,8 @@ mod util;
use std::ffi::{OsStr, OsString};
+const ATOM_ENTRY_LIMIT: usize = 100;
+
// stole it from the internet
pub fn append_ext(ext: impl AsRef<OsStr>, path: &PathBuf) -> PathBuf {
let mut os_string: OsString = path.into();
@@ -89,7 +92,21 @@ impl List {
write_if_unchanged(&index.with_extension("html"), html.as_bytes());
}
}
- // write_if_unchanged(&self.out_dir.join("atom.xml"), self.to_xml().as_bytes());
+ write_if_unchanged(&self.out_dir.join("atom.xml"), self.to_xml().as_bytes());
+ }
+
+ // Used with atom
+ fn get_recent_messages(&self) -> Vec<StrMessage> {
+ let mut out = Vec::new();
+ let mut msgs: Vec<&threading::Msg> = self.thread_idx.threads.iter().flatten().collect();
+ msgs.sort_by_key(|x| x.time);
+ msgs.reverse();
+ for m in msgs.iter().take(ATOM_ENTRY_LIMIT) {
+ let data = std::fs::read(&m.path).unwrap();
+ let msg = StrMessage::new(&Message::parse(&data).unwrap());
+ out.push(msg);
+ }
+ out
}
fn write_threads(&mut self) {
@@ -99,7 +116,6 @@ impl List {
let message_dir = self.out_dir.join("messages");
std::fs::create_dir_all(&thread_dir).ok();
std::fs::create_dir_all(&message_dir).ok();
- // Used for atom
for thread_ids in &self.thread_idx.threads {
// Load thread
let thread = Thread::new(thread_ids, &self.config.name);
@@ -132,6 +148,7 @@ impl List {
}
self.thread_topics.sort_by_key(|t| t.last_reply);
self.thread_topics.reverse();
+ self.recent_messages = self.get_recent_messages();
// Remove deleted stuff
for dir in vec![message_dir, thread_dir] {
diff --git a/src/models.rs b/src/models.rs
@@ -32,6 +32,7 @@ impl Lists {
thread_idx,
config,
thread_topics: vec![],
+ recent_messages: vec![],
out_dir: self.out_dir.join(name),
})
}
@@ -39,7 +40,8 @@ impl Lists {
pub struct List {
pub thread_idx: ThreadIdx,
pub thread_topics: Vec<ThreadSummary>, // TODO
- pub config: Subsection, // path
+ pub recent_messages: Vec<StrMessage>,
+ pub config: Subsection, // path
pub out_dir: PathBuf,
}
@@ -74,6 +76,7 @@ pub struct StrMessage {
pub id: String,
pub subject: String,
pub thread_subject: String,
+ pub received: i64,
pub preview: String,
pub from: MailAddress,
pub date: String, // TODO better dates
@@ -183,6 +186,14 @@ impl StrMessage {
pub fn new(msg: &Message) -> StrMessage {
let id = msg.get_message_id().unwrap_or("");
+ // TODO duplicate in threading.rs
+ let received = msg
+ .get_received()
+ .as_datetime_ref()
+ .or_else(|| msg.get_date())
+ .unwrap()
+ .to_timestamp()
+ .unwrap_or(-1);
let subject = msg.get_subject().unwrap_or("(No Subject)");
let thread_subject = msg.get_thread_name().unwrap_or("(No Subject)");
let invalid_email = Addr::new(None, "invalid-email");
@@ -236,6 +247,7 @@ impl StrMessage {
id: id.to_owned(),
subject: subject.to_owned(),
from: from,
+ received,
preview,
to,
cc,
diff --git a/src/templates/gmi.rs b/src/templates/gmi.rs
@@ -114,6 +114,7 @@ To: {to}{optional_headers}
("mailto", &h(&msg.mailto)),
("msg_path", &h(msg.pathescape_msg_id().to_str().unwrap())),
// TODO escape # in body?
+ // TODO only unformat flowed if flowed is true
("body", &unformat_flowed(&msg.body)),
],
)
diff --git a/src/templates/xml.rs b/src/templates/xml.rs
@@ -1,100 +1,81 @@
-// use super::util::xml_escape;
+use super::util::xml_safe as x;
use crate::models::*;
+use crate::time::Date;
+use crate::util::unformat_flowed;
// use crate::templates::util::xml_safe;
// use anyhow::{Context, Result};
-// use nanotemplate::template;
+use nanotemplate::template;
-// const ATOM_ENTRY_LIMIT: i32 = 100;
+const FEED_TEMPLATE: &str = r#"<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+<title>{feed_title}</title>
+<link href="{feed_link}"/>
+<updated>{last_updated}</updated>
+<author>
+<name>{author_name}</name>
+<email>{author_email}</email>
+</author>
+<id>{feed_id}</id>
+{entry_list}
+</feed>"#;
-// impl List {
-// fn to_xml(&self) {
-// template(
-// r#"<?xml version="1.0" encoding="utf-8"?>
-// <feed xmlns="http://www.w3.org/2005/Atom">
-// <title>{feed_title}</title>
-// <link href="{feed_link}"/>
-// <updated>{last_updated}</updated>
-// <author>
-// <name>{author_name}</name>
-// <email>{author_email}</email>
-// </author>
-// <id>{feed_id}</id>
-// {entry_list}
-// </feed>"#,
-// &[],
-// // feed_title = &self.name,
-// // feed_link = &self.url,
-// // last_updated = time::secs_to_date(last_updated).rfc3339(),
-// // author_name = &self.email,
-// // author_email = &self.email,
-// // feed_id = &self.url,
-// // entry_list = entries_str,
-// )
-// }
-// }
+const MESSAGE_TEMPLATE: &str = r#"<entry>
+<title>{title}</title>
+<link href="tbd"/>
+<id>{entry_id}</id>
+<updated>{updated_at}</updated>
+<author>
+<name>{author_name}</name>
+<email>{author_email}</email>
+</author>
+<content type="text/plain">
+{content}
+</content>
+</entry>
+"#;
+
+impl List {
+ pub fn to_xml(&self) -> String {
+ let mut entry_list = String::new();
+ for msg in &self.recent_messages {
+ entry_list.push_str(
+ &template(
+ MESSAGE_TEMPLATE,
+ &[
+ ("title", &x(&msg.subject)),
+ // ("item_link", "sdf"),
+ ("entry_id", &x(&msg.id)),
+ ("updated_at", &Date::from(msg.received).rfc3339()),
+ (
+ "author_name",
+ &x(&msg.from.clone().name.unwrap_or(msg.from.clone().address)),
+ ),
+ ("author_email", &x(&msg.from.address)),
+ ("content", &x(&unformat_flowed(&msg.body))),
+ ],
+ )
+ .unwrap(),
+ );
+ }
+ template(
+ FEED_TEMPLATE,
+ &[
+ ("feed_link", "asdf"),
+ ("feed_id", "asdf"),
+ ("feed_title", "asdf"),
+ ("last_updated", "adf"),
+ ("entry_list", &entry_list),
+ ("author_name", ""),
+ ("author_email", ""),
+ ],
+ )
+ .unwrap()
+ // last_updated = time::secs_to_date(last_updated).rfc3339(),
+ }
+}
impl Thread {
pub fn to_xml(&self) -> String {
String::new()
}
}
-// return ""
-// for message in &self.messages {
-// let tmpl = self.build_msg_atom(message);
-// entries.push_str(&tmpl);
-// }
-// let root = self.messages[0];
-// let atom = format!(
-// r#"<?xml version="1.0" encoding="utf-8"?>
-// <feed xmlns="http://www.w3.org/2005/Atom">
-// <title>{feed_title}</title>
-// <link rel="self" href="{feed_link}"/>
-// <updated>{last_updated}</updated>
-// <author>
-// <name>{author_name}</name>
-// <email>{author_email}</email>
-// </author>
-// <id>{feed_id}</id>
-// {entry_list}
-// </feed>"#,
-// feed_title = xml_safe(&root.subject),
-// feed_link = self.url(),
-// last_updated = time::secs_to_date(self.last_reply()).rfc3339(),
-// author_name = xml_safe(short_name(&root.from)),
-// author_email = xml_safe(&root.from.addr),
-// feed_id = self.url(),
-// entry_list = entries,
-// );
-// }
-// }
-
-// impl<'a> StrMessage<'a> {
-// fn to_xml(&self) -> String {
-// template(
-// r#"<entry>
-// <title>{title}</title>
-// <link href="{item_link}"/>
-// <id>{entry_id}</id>
-// <updated>{updated_at}</updated>
-// <author>
-// <name>{author_name}</name>
-// <email>{author_email}</email>
-// </author>
-// <content type="text/plain">
-// {content}
-// </content>
-// </entry>
-// "#,
-// &[
-// ("title", self.subject.as_ref()),
-// // ("item_link", "TBD"), -> this introduces filesystem dependency
-// // ("entry_id", self.id),
-// ("updated_at", "TBD"),
-// // ("author_name", self.from.name),
-// // ("author_email", self.from.address),
-// // ("content", self.body),
-// ],
-// )
-// .unwrap()
-// }
-// }
diff --git a/src/threading.rs b/src/threading.rs
@@ -4,7 +4,7 @@
// stores
use mail_parser::parsers::fields::thread::thread_name;
-use mail_parser::{Message};
+use mail_parser::Message;
use std::collections::HashMap;
use std::path::PathBuf;