commit c93eb96badc02d14e109211ea15ed26866cad74c
parent 1e897d05d0458b33c9e0a48232e558c6a9b95c01
Author: alex wennerberg <alex@alexwennerberg.com>
Date: Tue, 14 Dec 2021 19:31:44 -0800
Add configuration
Diffstat:
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] %}