crabmail

Static HTML email archive viewer in Rust
git clone git://git.alexwennerberg.com/crabmail
Log | Files | Refs | README | LICENSE

commit acfadda4fd8cbd83b593670036cdad920be31709
parent 6c2decf8bd0a9a66a17d1e8f1ec1fd56d7f22650
Author: alex wennerberg <alex@alexwennerberg.com>
Date:   Sun, 27 Mar 2022 10:01:32 -0700

xml continue

Diffstat:
Msrc/main.rs | 20++++++++++++--------
Msrc/models.rs | 29+++++++++++++++++++++++------
Msrc/templates/xml.rs | 65++++++++++++++++++++++++++++++++++++++++++++++-------------------
3 files changed, 81 insertions(+), 33 deletions(-)

diff --git a/src/main.rs b/src/main.rs @@ -118,9 +118,18 @@ impl List { std::fs::create_dir_all(&message_dir).ok(); for thread_ids in &self.thread_idx.threads { // Load thread - let thread = Thread::new(thread_ids, &self.config.name, &self.config.email); + let mut thread = Thread::new(thread_ids, &self.config.name, &self.config.email); let basepath = thread_dir.join(&thread.messages[0].pathescape_msg_id()); - // hacky + // this is a bit awkward + let summary = ThreadSummary { + message: thread.messages[0].clone(), + reply_count: (thread.messages.len() - 1) as u64, + last_reply: thread_ids[thread_ids.len() - 1].time, + }; + for msg in &mut thread.messages { + msg.set_url(&self, &summary); // awkward) // hacky + } + self.thread_topics.push(summary); if Config::global().include_html { let html = append_ext("html", &basepath); write_if_unchanged(&html, thread.to_html().as_bytes()); @@ -134,12 +143,7 @@ impl List { write_if_unchanged(&gmi, thread.to_gmi().as_bytes()); files_written.insert(gmi); } - // this is a bit awkward - self.thread_topics.push(ThreadSummary { - message: thread.messages[0].clone(), - reply_count: (thread.messages.len() - 1) as u64, - last_reply: thread_ids[thread_ids.len() - 1].time, - }); + for msg in thread.messages { let eml = append_ext("eml", &message_dir.join(&msg.pathescape_msg_id())); write_if_unchanged(&eml, &msg.export_eml()); diff --git a/src/models.rs b/src/models.rs @@ -57,18 +57,25 @@ pub struct ThreadSummary { pub struct Thread { pub messages: Vec<StrMessage>, + pub url: String, } impl Thread { pub fn new(thread_idx: &Vec<Msg>, list_name: &str, list_email: &str) -> Self { - let mut out = vec![]; + let mut messages = vec![]; for m in thread_idx { let data = std::fs::read(&m.path).unwrap(); let mut msg = StrMessage::new(&Message::parse(&data).unwrap()); msg.mailto = msg.mailto(list_name, list_email); - out.push(msg); + messages.push(msg); } - Thread { messages: out } + let url = format!( + "{}/{}/{}", + Config::global().base_url, + list_name, + messages[0].pathescape_msg_id().to_str().unwrap(), + ); + Thread { url, messages } } } @@ -89,9 +96,7 @@ pub struct StrMessage { pub in_reply_to: Option<String>, pub to: Vec<MailAddress>, pub cc: Vec<MailAddress>, - // url: Cow<'a, str>, - // reply-to string - // download_path: PathBuf, // TODO + pub url: String, } // i suck at Cow and strings @@ -187,6 +192,17 @@ impl StrMessage { url.into() } + // only place that depends on list and thread. hmm + pub fn set_url(&mut self, list: &List, thread: &ThreadSummary) { + self.url = format!( + "{}/{}/{}#{}", + Config::global().base_url, + list.config.name, + thread.message.pathescape_msg_id().to_str().unwrap(), + self.id + ); + } + pub fn new(msg: &Message) -> StrMessage { let id = msg.get_message_id().unwrap_or(""); // TODO duplicate in threading.rs @@ -254,6 +270,7 @@ impl StrMessage { preview, to, cc, + url: String::new(), thread_subject: thread_subject.to_owned(), date: date.to_owned(), body: body.to_string(), diff --git a/src/templates/xml.rs b/src/templates/xml.rs @@ -34,28 +34,34 @@ const MESSAGE_TEMPLATE: &str = r#"<entry> </entry> "#; +impl StrMessage { + pub fn to_xml(&self) -> String { + let msg = self; + template( + MESSAGE_TEMPLATE, + &[ + ("title", &x(&msg.subject)), + ("item_link", &x(&self.url)), + ("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() + } +} + +// TODO dedup 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(), - ); + entry_list.push_str(&msg.to_xml()); } // Sometimes its unclear whether to do stuff like this in models.rs or here. could refactor let last_updated = self @@ -81,6 +87,27 @@ impl List { impl Thread { pub fn to_xml(&self) -> String { - String::new() + let mut entry_list = String::new(); + for msg in &self.messages { + entry_list.push_str(&msg.to_xml()); + } + // Sometimes its unclear whether to do stuff like this in models.rs or here. could refactor + let root = &self.messages[0]; + template( + FEED_TEMPLATE, + &[ + ("feed_link", &self.url), + ("feed_id", &self.url), + ("feed_title", &root.subject), + ("last_updated", &Date::from(root.received).rfc3339()), + ("entry_list", &entry_list), + ( + "author_name", + &root.from.name.clone().unwrap_or(root.from.address.clone()), + ), + ("author_email", &root.from.address), + ], + ) + .unwrap() } }