crabmail

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

commit c93eb96badc02d14e109211ea15ed26866cad74c
parent 1e897d05d0458b33c9e0a48232e558c6a9b95c01
Author: alex wennerberg <alex@alexwennerberg.com>
Date:   Tue, 14 Dec 2021 19:31:44 -0800

Add configuration

Diffstat:
MCargo.lock | 7+++++++
MCargo.toml | 1+
Acrabmail.conf | 3+++
Asrc/config.rs | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib.rs | 0
Msrc/main.rs | 51++++++++++++++++++++++++++++++++++++---------------
Mtemplates/base.html | 4++--
Mtemplates/threadlist.html | 2+-
8 files changed, 104 insertions(+), 18 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -155,6 +155,7 @@ dependencies = [ "hex", "mailparse", "mbox-reader", + "once_cell", "pico-args", "quick-xml", "sha3", @@ -423,6 +424,12 @@ dependencies = [ ] [[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + +[[package]] name = "percent-encoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml @@ -13,6 +13,7 @@ html = ["ammonia"] [dependencies] mailparse = "0.13" url = "2" +once_cell = "1.9" hex = "0.4" sha3 = "0.10" mbox-reader = "0.2.0" #unamaintained, should remove dep diff --git a/crabmail.conf b/crabmail.conf @@ -0,0 +1,3 @@ +list_email=~aw/flounder@lists.sr.ht +list_name=Flounder Mailing List +url=https://lists.flounder.online/flounder diff --git a/src/config.rs b/src/config.rs @@ -0,0 +1,54 @@ +use once_cell::sync::OnceCell; +use std::fs::File; +use std::io::{self, BufRead}; +use std::path::Path; + +// Config file structure is very simple: +// key=value\n +#[derive(Debug)] +pub struct Config { + pub list_name: String, + pub list_email: String, + pub url: String, +} + +pub static INSTANCE: OnceCell<Config> = OnceCell::new(); + +impl Config { + pub fn global() -> &'static Config { + INSTANCE.get().expect("Config is not initialized") + } + + pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Config, std::io::Error> { + let file = File::open(path)?; + let mut list_name = "Crabmail Mailing List".to_string(); + let mut list_email = "setme@foo.local".to_string(); + let mut url = "flounder.online".to_string(); + + for l in io::BufReader::new(file).lines() { + let line = l?; + if line.len() == 0 { + continue; + } + if let Some(i) = line.find('=') { + let key = &line[..i]; + let value = &line[i + 1..]; + match key { + "list_name" => list_name = value.to_string(), + "list_email" => list_email = value.to_string(), + "url" => url = value.to_string(), + _ => {} + } + // Debug line + } else { + // Replace with whatever you want to do on malformed config lines + panic!("Invalid config") + } + } + Ok(Config { + list_name, + list_email, + url, + }) + } +} diff --git a/src/lib.rs b/src/lib.rs diff --git a/src/main.rs b/src/main.rs @@ -12,17 +12,10 @@ use std::fs::OpenOptions; use std::io::prelude::*; use url::Url; +use config::{Config, INSTANCE}; +mod config; mod filters; -const TITLE: &str = "Flounder Mailing List" -const LIST_EMAIL: &str = "~aw/flounder@lists.sr.ht" - -const HELP: &str = "\ -Usage: crabmail - --m --mbox input mbox file -"; - // TODO be more clear about the expected input types // maildi @@ -61,7 +54,7 @@ impl Email { // mailto:... populated with everything you need pub fn mailto(&self) -> String { // TODO configurable - let mut url = Url::parse(&format!("mailto:{}", LIST_EMAIL)).unwrap(); + let mut url = Url::parse(&format!("mailto:{}", Config::global().list_email)).unwrap(); url.query_pairs_mut() .append_pair("cc", &self.from.to_string()); url.query_pairs_mut().append_pair("in-reply-to", &self.id); @@ -153,7 +146,13 @@ fn local_parse_email(data: &[u8]) -> Result<Email> { }); } -// TODO refactor +const HELP: &str = "\ +Usage: crabmail + +-m --mbox input mbox file +-c --config config file [crabmail.conf] +"; + fn main() -> Result<()> { let mut pargs = pico_args::Arguments::from_env(); @@ -165,8 +164,14 @@ fn main() -> Result<()> { let out_dir = pargs .opt_value_from_os_str(["-d", "--dir"], parse_path)? .unwrap_or("site".into()); + let config_file = pargs + .opt_value_from_os_str(["-c", "--config"], parse_path)? + .unwrap_or("crabmail.conf".into()); let in_mbox = pargs.value_from_os_str(["-m", "--mbox"], parse_path)?; + let config = Config::from_file(&config_file).unwrap(); // TODO better err handling + INSTANCE.set(config).unwrap(); + let mbox = MboxFile::from_file(&in_mbox)?; let mut thread_index: HashMap<String, Vec<String>> = HashMap::new(); @@ -243,8 +248,15 @@ fn main() -> Result<()> { .write(true) .truncate(true) .open(thread_dir.join(format!("{}.html", thread.hash)))?; - file.write(Thread { thread: &thread }.render()?.as_bytes()) - .ok(); + file.write( + Thread { + thread: &thread, + config: Config::global(), + } + .render()? + .as_bytes(), + ) + .ok(); threads.push(thread); } @@ -256,8 +268,15 @@ fn main() -> Result<()> { .write(true) .truncate(true) .open(out_dir.join("index.html"))?; - file.write(ThreadList { threads: threads }.render()?.as_bytes()) - .ok(); + file.write( + ThreadList { + threads: threads, + config: Config::global(), + } + .render()? + .as_bytes(), + ) + .ok(); Ok(()) } @@ -274,6 +293,7 @@ fn parse_path(s: &std::ffi::OsStr) -> Result<std::path::PathBuf, &'static str> { #[template(path = "thread.html")] struct Thread<'a> { thread: &'a MailThread<'a>, + config: &'a Config, } #[derive(Template)] @@ -281,4 +301,5 @@ struct Thread<'a> { struct ThreadList<'a> { // message root threads: Vec<MailThread<'a>>, + config: &'a Config, // Not ideal repetition } diff --git a/templates/base.html b/templates/base.html @@ -6,8 +6,8 @@ <link rel="stylesheet" type="text/css" href="../style.css" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0,user-scalable=0" /> <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>📧</text></svg>"> - <meta name="description" content="Crabmail mailing list"> - <title>Crabmail Mailing List</title> + <meta name="description" content="{{config.list_name}}"> + <title>{{config.list_name}}</title> {% block head %}{% endblock %} </head> <body> diff --git a/templates/threadlist.html b/templates/threadlist.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% block content %} - <div class="page-title"><h1>Crabmail Mailing List</h1></div> +<div class="page-title"><h1>{{config.list_name}}</h1></div> <hr class="thin"> {% for thread in threads %} {% let message = thread.messages[0] %}