crabmail

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

gmi.rs (4838B) - raw


      1 // WIP
      2 //
      3 use crate::models::*;
      4 use crate::templates::PAGE_SIZE;
      5 use crate::time::Date;
      6 use crate::util::*;
      7 use nanotemplate::template;
      8 
      9 impl Lists {
     10     pub fn to_gmi(&self) -> String {
     11         let mut lists = String::new();
     12         for list in &self.lists {
     13             lists.push_str(&format!("=> ./{0}/ {0}\n", &h(&list.config.name)));
     14         }
     15         // this looks stupid ok I know
     16         template(
     17             r#"# Mail Archives
     18 {lists}
     19          "#,
     20             &[("lists", &lists)],
     21         )
     22         .unwrap()
     23     }
     24 }
     25 
     26 impl List {
     27     pub fn to_gmi(&self) -> Vec<String> {
     28         // TODO paginate
     29         let page_count = self.thread_topics.len() / PAGE_SIZE + 1;
     30         self.thread_topics
     31             .chunks(PAGE_SIZE)
     32             .enumerate()
     33             .map(|(n, thread_topics)| {
     34                 let mut threads = format!(
     35                     "## {0}\n{1}\n=>mailto:{2} {2}\n",
     36                     self.config.name, self.config.description, self.config.email
     37                 );
     38                 for thread in thread_topics {
     39                     threads.push_str(
     40                         // TODO reuse with html templates?
     41                         &template(
     42                             r#"
     43 => threads/{path_id}.gmi {subject}
     44 {preview}
     45 {from} | {replies} replies | {date}
     46 "#,
     47                             &[
     48                                 (
     49                                     "path_id",
     50                                     &h(thread.message.pathescape_msg_id().to_str().unwrap()),
     51                                 ),
     52                                 ("subject", &h(&thread.message.subject)),
     53                                 ("replies", &thread.reply_count.to_string()),
     54                                 ("preview", &h(&thread.message.preview)),
     55                                 ("date", &h(&Date::from(thread.last_reply).ymd())),
     56                                 (
     57                                     "from",
     58                                     &h(&thread. // awkawrd
     59                                 message.from
     60                                 .name
     61                                 .clone()
     62                                 .unwrap_or(thread.message.from.address.clone())
     63                                 .clone()),
     64                                 ),
     65                             ],
     66                         )
     67                         .unwrap(),
     68                     );
     69                 }
     70                 if n + 1 <= page_count {
     71                     threads.push_str(&format!("\n=> index-{}.gmi next", n + 1));
     72                 }
     73                 threads
     74             })
     75             .collect()
     76     }
     77 }
     78 
     79 impl Thread {
     80     pub fn to_gmi(&self) -> String {
     81         let mut out = format!(
     82             r#"# {}
     83         "#,
     84             self.messages[0].subject.replace("\n", " ")
     85         );
     86         for msg in &self.messages {
     87             let mut optional_headers = String::new();
     88             if let Some(irt) = &msg.in_reply_to {
     89                 optional_headers.push_str(&format!("\nIn-Reply-To: {}", &h(&irt)));
     90             }
     91             // TODO no copy pasta
     92             let cc_string = &h(&msg
     93                 .cc
     94                 .iter()
     95                 .map(|t| t.to_string())
     96                 .collect::<Vec<String>>()
     97                 .join(", "));
     98             if msg.cc.len() > 0 {
     99                 optional_headers.push_str(&format!("\nCc: {}", cc_string));
    100             }
    101             let body = match msg.flowed {
    102                 true => unformat_flowed(&msg.body),
    103                 false => msg.body.clone(),
    104             };
    105             let msg = template(
    106                 r#"
    107 ## {subject}
    108 From: {from}
    109 Date: {date}
    110 Message-Id: {msg_id}
    111 To: {to}{optional_headers}
    112 => {mailto} Reply
    113 => ../messages/{msg_path}.mbox Export
    114 --------------------------------------
    115 {body}
    116 "#,
    117                 &[
    118                     ("subject", &h(&msg.subject)),
    119                     ("date", &h(&msg.date)),
    120                     ("msg_id", &h(&msg.id)),
    121                     (
    122                         "to",
    123                         &h(&msg
    124                             .to
    125                             .iter()
    126                             .map(|t| t.to_string())
    127                             .collect::<Vec<String>>()
    128                             .join(", ")),
    129                     ),
    130                     ("optional_headers", &optional_headers),
    131                     ("from", &h(&msg.from.address)),
    132                     ("mailto", &h(&msg.mailto)),
    133                     ("msg_path", &h(msg.pathescape_msg_id().to_str().unwrap())),
    134                     // TODO escape # in body?
    135                     // TODO only unformat flowed if flowed is true
    136                     ("body", &body),
    137                 ],
    138             )
    139             .unwrap();
    140             out.push_str(&msg);
    141         }
    142         out
    143     }
    144 }
    145 
    146 // escape header
    147 fn h(s: &str) -> String {
    148     s.replace("\n", " ")
    149 }