crabmail

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

commit bfe4fb431b732af7bded8975bfa0ec8deca8679d
parent 2e914d0a7100e000768d1ff57f86a0a525ae4c20
Author: alex wennerberg <alex@alexwennerberg.com>
Date:   Sat, 19 Mar 2022 11:09:14 -0700

fix up index

Diffstat:
MCargo.lock | 77+----------------------------------------------------------------------------
MCargo.toml | 3++-
Msrc/main.rs | 10++++++++--
Msrc/models.rs | 9+++++----
Msrc/templates/html.rs | 21+++++++++++++--------
Msrc/threading.rs | 3+--
Msrc/time.rs | 10+++++++---
Dsrc/utils.rs | 39---------------------------------------
8 files changed, 37 insertions(+), 135 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -9,12 +9,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" [[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] name = "crabmail" version = "0.1.0" dependencies = [ @@ -28,15 +22,6 @@ dependencies = [ ] [[package]] -name = "encoding_rs" -version = "0.8.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" -dependencies = [ - "cfg-if", -] - -[[package]] name = "linkify" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -48,12 +33,7 @@ dependencies = [ [[package]] name = "mail-parser" version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545e2643e5f923cdea238bb826277e68967797f7d50523932d10a87dfcc940ac" -dependencies = [ - "encoding_rs", - "serde", -] +source = "git+https://github.com/alexwennerberg/mail-parser#75604396cb2168ada0e37b705ab68b51e14332b5" [[package]] name = "memchr" @@ -80,61 +60,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" [[package]] -name = "proc-macro2" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "quote" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "serde" -version = "1.0.136" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.136" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "syn" -version = "1.0.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd69e719f31e88618baa1eaa6ee2de5c9a1c004f1e9ecdb58e8352a13f20a01" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] name = "urlencoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml @@ -10,7 +10,8 @@ default = [] [dependencies] anyhow = "1.0.52" nanotemplate = "*" -mail-parser = "*" # TODO no-default +# using my fork just to upstream patches faster +mail-parser = {git = "https://github.com/alexwennerberg/mail-parser", default-features=false} # Small, effective dependencies, little benefit to vendoring linkify = "0.8.0" diff --git a/src/main.rs b/src/main.rs @@ -6,6 +6,7 @@ use anyhow::{Context, Result}; use maildir::Maildir; use std::path::PathBuf; use std::str; +use time::Date; use models::*; use std::io::prelude::*; @@ -18,7 +19,6 @@ mod models; mod templates; mod threading; mod time; -mod utils; const ATOM_ENTRY_LIMIT: i32 = 100; const PAGE_SIZE: i32 = 100; @@ -123,10 +123,16 @@ impl List { write_if_unchanged(&append_ext("gmi", &basepath), thread.to_gmi().as_bytes()); } // this is a bit awkward - self.thread_topics.push(thread.messages[0].clone()); + 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 file in thread // write raw file } + self.thread_topics.sort_by_key(|t| t.last_reply); + self.thread_topics.reverse(); self.write_index(); } } diff --git a/src/models.rs b/src/models.rs @@ -1,5 +1,6 @@ use crate::config::{Config, Subsection}; use crate::threading::{Msg, ThreadIdx}; +use crate::time::Date; use mail_parser::MimeHeaders; use mail_parser::{Addr, HeaderValue, Message}; use std::borrow::Cow; @@ -37,16 +38,16 @@ impl Lists { } pub struct List { pub thread_idx: ThreadIdx, - pub thread_topics: Vec<StrMessage>, // TODO - pub config: Subsection, // path + pub thread_topics: Vec<ThreadSummary>, // TODO + pub config: Subsection, // path pub out_dir: PathBuf, } // doesnt include full msg data pub struct ThreadSummary { - pub subject: String, + pub message: StrMessage, pub reply_count: u64, - pub last_reply: u64, // date + pub last_reply: i64, // unix } impl List { diff --git a/src/templates/html.rs b/src/templates/html.rs @@ -1,6 +1,7 @@ use super::util::xml_escape; use super::util::xml_safe as x; use crate::models::*; +use crate::time::Date; use linkify::{LinkFinder, LinkKind}; use nanotemplate::template; use std::borrow::Cow; @@ -49,22 +50,26 @@ impl List { <a class="bigger" href="threads/{path_id}.html">{subject}</a> <br> {preview}<br> - {from} | n replies | last-reply-date + {from} | {replies} replies | {date} "#, &[ - ("path_id", &x(thread.pathescape_msg_id().to_str().unwrap())), - ("subject", &x(&thread.subject)), - ("date", &x(&thread.date)), + ( + "path_id", + &x(thread.message.pathescape_msg_id().to_str().unwrap()), + ), + ("subject", &x(&thread.message.subject)), + ("replies", &thread.reply_count.to_string()), + ("date", &x(&Date::from(thread.last_reply).ymd())), ( "from", - &x(&thread // awkawrd - .from + &x(&thread. // awkawrd + message.from .name .clone() - .unwrap_or(thread.from.address.clone()) + .unwrap_or(thread.message.from.address.clone()) .clone()), ), - ("preview", &x(&thread.preview)), + ("preview", &x(&thread.message.preview)), ], ) .unwrap(), diff --git a/src/threading.rs b/src/threading.rs @@ -3,7 +3,6 @@ // Assumes msg can be found on disk at `path` -- should be made more abstract to handle other mail // stores -use crate::utils::epoch_time; use mail_parser::parsers::fields::thread::thread_name; use mail_parser::{DateTime, Message}; use std::collections::HashMap; @@ -41,7 +40,7 @@ impl ThreadIdx { .as_datetime_ref() .or_else(|| msg.get_date()) .unwrap(); // TODO fix unwrap - let time = epoch_time(&t); + let time = t.to_timestamp().unwrap_or(-1); // todo unwrap. shouldnt occur. trying to change upstream https://github.com/stalwartlabs/mail-parser/pull/15 let in_reply_to = msg.get_in_reply_to().as_text_ref(); let last_reference = msg.get_in_reply_to().as_text_ref(); let thread_name = thread_name(msg.get_subject().unwrap_or("(No Subject)")); diff --git a/src/time.rs b/src/time.rs @@ -46,6 +46,10 @@ impl Date { self.year, self.month, self.day_of_month, self.hour, self.minute, self.second ) } + + pub fn from(t: i64) -> Self { + secs_to_date(t) + } } pub fn current_time_rfc3339() -> String { @@ -53,15 +57,15 @@ pub fn current_time_rfc3339() -> String { .duration_since(UNIX_EPOCH) .unwrap() .as_secs(); - return secs_to_date(current_time).rfc3339(); + return secs_to_date(current_time as i64).rfc3339(); } // from http://git.musl-libc.org/cgit/musl/tree/src/time/__secs_to_tm.c // with a slightly different API // this is a line-for-line copy, not idiomatic rust // UTC // TODO handle 64-bit overflow -pub fn secs_to_date(unixtime: u64) -> Date { - let secs = unixtime as i64 - LEAP_EPOCH; +fn secs_to_date(unixtime: i64) -> Date { + let secs = unixtime - LEAP_EPOCH; let mut days = secs / 86400; let mut remsecs = secs % 86400; if remsecs < 0 { diff --git a/src/utils.rs b/src/utils.rs @@ -1,39 +0,0 @@ -use mail_parser::DateTime; -use std::fs::{read, write}; -use std::io::prelude::*; -use std::path::PathBuf; - -// from https://github.com/protocolbuffers/upb/blob/22182e6e/upb/json_decode.c#L982-L992 -// TODO make rust-idiomatic -pub fn epoch_days(y: u32, m: u32, d: u32) -> i64 { - let year_base = 4800; - let m_adj = match m < 3 { - true => 0, - false => m - 3, - }; - let carry = match m_adj > m { - true => 1, - false => 0, - }; - let adjust = carry * 12; - let y_adj = m + year_base - carry; - let month_days = ((m_adj + adjust) * 62719 + 769) / 2048; - let leap_days = y_adj / 4 - y_adj / 100 + y_adj / 400; - y_adj as i64 * 365 + leap_days as i64 + month_days as i64 + (d as i64 - 1) - 2472632 -} - -// leap seconds? -// TODO test -pub fn epoch_time(dt: &DateTime) -> i64 { - let mut h = dt.hour as i64; - let mut m = dt.minute as i64; - let s = dt.second; - let adj = match dt.tz_before_gmt { - true => 1, - false => -1, - }; - h += dt.tz_hour as i64 * adj; - m += dt.tz_minute as i64 * adj; - - return epoch_days(dt.year, dt.month, dt.day) * 86400 + h * 3600 + m * 60 + dt.second as i64; -}