crabmail

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

commit 7fba680b0fa2fbd90b8c2741b7c555a89e765c3d
parent d8a85409d4ad8c79fc7f302691251444dda0c70d
Author: alex wennerberg <alex@alexwennerberg.com>
Date:   Wed, 12 Jan 2022 15:23:09 -0800

Add subsections to subscription config

Diffstat:
Mcrabmail.conf | 12++++++++++--
Msrc/arg.rs | 1-
Msrc/config.rs | 59+++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/main.rs | 45++++++++++++++++++++++++++++++++++++---------
4 files changed, 89 insertions(+), 28 deletions(-)

diff --git a/crabmail.conf b/crabmail.conf @@ -1,5 +1,13 @@ +base_url=https://lists.flounder.online # Use %s to represent list name email_fmt=lists+%s@flounder.online -base_url=https://lists.flounder.online -[somelist] +# optionally wirte/overwrite config for specific lists +[smallweb] +title=Very cool list +description=The coolest list ever +email=coollist@example.com + +[whatever] +title=asdfadsf +description=The coolest list ever diff --git a/src/arg.rs b/src/arg.rs @@ -30,7 +30,6 @@ ARGS: ); exit(1) } - pub struct Args { pub maildir: String, pub config: PathBuf, diff --git a/src/config.rs b/src/config.rs @@ -3,9 +3,13 @@ use std::fs::File; use std::io::{self, BufRead}; use std::path::{Path, PathBuf}; -// Config file structure is very simple: -// key=value\n -#[derive(Debug)] +// Ini-like configuration, with sections. +// Global config first, then config for each subsection +// key=value +// +// [section] +// key2=value2 +#[derive(Default, Debug)] pub struct Config { pub email_fmt: String, pub base_url: String, @@ -13,8 +17,10 @@ pub struct Config { pub relative_times: bool, pub include_raw: bool, pub now: String, + pub subsections: Vec<Subsection>, } +#[derive(Default, Debug)] pub struct Subsection { pub name: String, pub title: String, // "something mailing list" @@ -29,37 +35,58 @@ impl Config { INSTANCE.get().expect("Config is not initialized") } + pub fn default_subsection(&self, name: &str) -> Subsection { + Subsection { + name: name.to_owned(), + title: format!("{} mailing list", name), + email: self.email_fmt.replace("%s", name), + description: String::new(), + } + } + pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Config, std::io::Error> { let file = File::open(path)?; // let sub_sections = vec![]; - let mut email_fmt = "lists+%s@example.com".to_string(); - let mut base_url = "https://example.com".to_string(); + let mut conf = Config::default(); + let mut current_section = None; for l in io::BufReader::new(file).lines() { let line = l?; - if line.starts_with("[") && line.ends_with("]") {} + if line.starts_with("[") && line.ends_with("]") { + let name = &line[1..line.len() - 1]; + // Defaults from global config + if current_section.is_some() { + conf.subsections.push(current_section.unwrap()); + } + current_section = Some(conf.default_subsection(name)) + } if line.len() == 0 { continue; } if let Some(i) = line.find('=') { let key = &line[..i]; let value = &line[i + 1..]; + if let Some(ref mut s) = current_section { + match key { + "title" => s.title = value.to_string(), + "email" => s.email = value.to_string(), + "description" => s.description = value.to_string(), + _ => {} + } + } match key { - "email_fmt" => email_fmt = value.to_string(), - "base_url" => base_url = value.to_string(), + "email_fmt" => conf.email_fmt = value.to_string(), + "base_url" => conf.base_url = value.to_string(), _ => {} } } else { // panic!("Invalid config") } } - Ok(Config { - email_fmt, - base_url, - out_dir: PathBuf::from(""), - relative_times: false, - include_raw: false, - now: crate::time::current_time_rfc3339(), - }) + if current_section.is_some() { + conf.subsections.push(current_section.unwrap()); + } + conf.now = crate::time::current_time_rfc3339(); + Ok(conf) } } diff --git a/src/main.rs b/src/main.rs @@ -1,3 +1,7 @@ +// this code is not good +// i am not very good at rust +// that is ok though + use anyhow::{Context, Result}; use horrorshow::helper::doctype; use horrorshow::owned_html; @@ -81,6 +85,8 @@ struct ThreadList<'a> { threads: Vec<MailThread<'a>>, name: String, email: String, + description: String, + title: String, url: String, // URL? } @@ -94,10 +100,20 @@ fn short_name(s: &SingleInfo) -> &str { impl<'a> ThreadList<'a> { fn new(threads: Vec<MailThread<'a>>, list_name: &str) -> Self { + let config = Config::global(); + let d = config.default_subsection(&list_name); + let subsection_config = config + .subsections + .iter() + .find(|s| s.name == list_name) + .unwrap_or(&d); + ThreadList { threads, - name: list_name.to_owned(), - email: Config::global().email_fmt.replace("%s", &list_name), + name: list_name.to_owned(), // TODO handle ownership + email: subsection_config.email.to_owned(), + title: subsection_config.title.to_owned(), + description: subsection_config.description.to_owned(), url: format!("{}/{}", Config::global().base_url, &list_name), } } @@ -155,13 +171,17 @@ impl<'a> ThreadList<'a> { }; let tmp = html! { h1(class="page-title") { - : format!("{} Mailing List", &self.name); + : &self.title; : Raw(" "); a(href="atom.xml") { img(alt="Atom feed", src=utils::RSS_SVG); } } + : Raw(&self.description); + @if self.description.len() > 1 { + br; + } a(href=format!("mailto:{}", &self.email)) { : &self.email } @@ -355,10 +375,15 @@ impl<'a> MailThread<'a> { impl Email { // mailto:... populated with everything you need pub fn mailto(&self, thread_subject: &str, list_name: &str) -> String { - let mut url = format!( - "mailto:{}?", - Config::global().email_fmt.replace("%s", list_name) // not ideal - ); + let config = Config::global(); + let d = config.default_subsection(&list_name); + let subsection_config = config + .subsections + .iter() + .find(|s| s.name == list_name) + .unwrap_or(&d); + + let mut url = format!("mailto:{}?", subsection_config.email); let from = self.from.to_string(); // make sure k is already urlencoded @@ -546,15 +571,17 @@ fn main() -> Result<()> { let mut message_count = 0; for maildir in std::fs::read_dir(&args.maildir).unwrap() { let maildir = maildir?; - let dirreader = Maildir::from(maildir.path().to_str().unwrap()); let file_name = maildir.file_name(); - let out_dir = &Config::global().out_dir.join(&file_name); + let config = Config::global(); + let out_dir = config.out_dir.join(&file_name); std::fs::create_dir(&out_dir).ok(); + let dirreader = Maildir::from(maildir.path().to_str().unwrap()); let list_name = file_name.into_string().unwrap(); // filter out maildir internal junk if list_name.as_bytes()[0] == b'.' || ["cur", "new", "tmp"].contains(&list_name.as_str()) { continue; } + let path = out_dir.join("messages"); std::fs::remove_dir_all(&path).ok(); names.push(list_name.clone());