diff --git a/Cargo.lock b/Cargo.lock
index a78ca088..bb0cf013 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -795,6 +795,8 @@ dependencies = [
"notify",
"notify-rust",
"pcre2",
+ "proc-macro2",
+ "quote",
"rmp",
"rmp-serde",
"rmpv",
@@ -806,6 +808,7 @@ dependencies = [
"smallvec",
"structopt",
"svg",
+ "syn",
"termion",
"toml",
"unicode-segmentation",
@@ -1622,9 +1625,9 @@ checksum = "0b65a64d32a41db2a8081aa03c1ccca26f246ff681add693f8b01307b137da79"
[[package]]
name = "syn"
-version = "1.0.30"
+version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2"
+checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6"
dependencies = [
"proc-macro2",
"quote",
diff --git a/Cargo.toml b/Cargo.toml
index f26581ef..36822e4c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -52,6 +52,10 @@ pcre2 = { version = "0.2.3", optional = true }
structopt = { version = "0.3.14", default-features = false }
svg_crate = { version = "0.8.0", optional = true, package = "svg" }
+[build-dependencies]
+syn = { version = "1.0.31", features = [] }
+quote = "^1.0"
+proc-macro2 = "1.0.18"
[profile.release]
lto = true
diff --git a/build.rs b/build.rs
index c027a271..4d225696 100644
--- a/build.rs
+++ b/build.rs
@@ -19,8 +19,22 @@
* along with meli. If not, see .
*/
+extern crate proc_macro;
+extern crate quote;
+extern crate syn;
+mod config_macros;
+
fn main() {
println!("cargo:rerun-if-changed=build.rs");
+ config_macros::override_derive(&[
+ ("src/conf/pager.rs", "PagerSettings"),
+ ("src/conf/listing.rs", "ListingSettings"),
+ ("src/conf/notifications.rs", "NotificationsSettings"),
+ ("src/conf/shortcuts.rs", "Shortcuts"),
+ ("src/conf/composing.rs", "ComposingSettings"),
+ ("src/conf/tags.rs", "TagsSettings"),
+ ("src/conf/pgp.rs", "PGPSettings"),
+ ]);
#[cfg(feature = "cli-docs")]
{
const MANDOC_OPTS: &[&'static str] = &["-T", "utf8", "-I", "os=Generated by mandoc(1)"];
diff --git a/config_macros.rs b/config_macros.rs
new file mode 100644
index 00000000..7ce5a0ce
--- /dev/null
+++ b/config_macros.rs
@@ -0,0 +1,185 @@
+/*
+ * meli -
+ *
+ * Copyright Manos Pitsidianakis
+ *
+ * This file is part of meli.
+ *
+ * meli is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * meli is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with meli. If not, see .
+ */
+
+use std::fs::File;
+use std::io::prelude::*;
+use std::process::{Command, Stdio};
+
+use quote::{format_ident, quote};
+
+// Write ConfigStructOverride to overrides.rs
+pub fn override_derive(filenames: &[(&str, &str)]) {
+ let mut output_file =
+ File::create("src/conf/overrides.rs").expect("Unable to open output file");
+ let mut output_string = r##"/*
+ * meli - conf/overrides.rs
+ *
+ * Copyright 2020 Manos Pitsidianakis
+ *
+ * This file is part of meli.
+ *
+ * meli is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * meli is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with meli. If not, see .
+ */
+
+//! This module is automatically generated by build.rs.
+use super::*;
+
+"##
+ .to_string();
+
+ 'file_loop: for (filename, ident) in filenames {
+ println!("cargo:rerun-if-changed={}", filename);
+ let mut file = File::open(&filename).expect(&format!("Unable to open file `{}`", filename));
+
+ let mut src = String::new();
+ file.read_to_string(&mut src).expect("Unable to read file");
+
+ let syntax = syn::parse_file(&src).expect("Unable to parse file");
+ if syntax.items.iter().any(|item| {
+ if let syn::Item::Struct(s) = item {
+ if s.ident.to_string().ends_with("Override") {
+ println!("ident {} exists, skipping {}", ident, filename);
+ return true;
+ }
+ }
+ false
+ }) {
+ continue 'file_loop;
+ }
+
+ for item in syntax.items.iter() {
+ if let syn::Item::Struct(s) = item {
+ if s.ident != ident {
+ continue;
+ }
+ if s.ident.to_string().ends_with("Override") {
+ unreachable!();
+ }
+ let override_ident: syn::Ident = format_ident!("{}Override", s.ident);
+ let mut field_tokentrees = vec![];
+ let mut field_idents = vec![];
+ for f in &s.fields {
+ let ident = &f.ident;
+ let ty = &f.ty;
+ let attrs = f
+ .attrs
+ .iter()
+ .filter_map(|f| {
+ let mut new_attr = f.clone();
+ if let quote::__private::TokenTree::Group(g) =
+ f.tokens.clone().into_iter().next().unwrap()
+ {
+ let attr_inner_value = f.tokens.to_string();
+ if !attr_inner_value.starts_with("( default")
+ && !attr_inner_value.starts_with("( default =")
+ {
+ return None;
+ }
+ if attr_inner_value.starts_with("( default =") {
+ let rest = g.stream().clone().into_iter().skip(4);
+ new_attr.tokens = quote! { ( #(#rest)*) };
+ if new_attr.tokens.to_string().as_str() == "( )" {
+ return None;
+ }
+ } else if attr_inner_value.starts_with("( default") {
+ let rest = g.stream().clone().into_iter().skip(2);
+ new_attr.tokens = quote! { ( #(#rest)*) };
+ if new_attr.tokens.to_string().as_str() == "( )" {
+ return None;
+ }
+ }
+ }
+
+ Some(new_attr)
+ })
+ .collect::>();
+ let t = quote! {
+ #(#attrs)*
+ #[serde(default)]
+ pub #ident : Option<#ty>
+ };
+ field_idents.push(ident);
+ field_tokentrees.push(t);
+ }
+ //let fields = &s.fields;
+
+ let literal_struct = quote! {
+ #[derive(Debug, Serialize, Deserialize, Clone)]
+ pub struct #override_ident {
+ #(#field_tokentrees),*
+ }
+
+
+ impl Default for #override_ident {
+ fn default() -> Self {
+ #override_ident {
+ #(#field_idents: None),*
+ }
+ }
+ }
+ };
+ output_string.extend(literal_struct.to_string().chars());
+ output_string.push_str("\n\n");
+ }
+ }
+ }
+
+ /*
+ let mut rustfmt = Command::new("rustfmt")
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .expect("failed to execute rustfmt");
+
+ {
+ // limited borrow of stdin
+ let stdin = rustfmt
+ .stdin
+ .as_mut()
+ .expect("failed to get rustfmt stdin");
+ stdin
+ .write_all(output_string.as_bytes())
+ .expect("failed to write to rustfmit stdin");
+ }
+
+ let output = rustfmt
+ .wait_with_output()
+ .expect("failed to wait on rustfmt child");
+ if !output.stderr.is_empty() {
+ panic!(format!("{}", String::from_utf8_lossy(&output.stderr)));
+ }
+
+ output_file.write_all(&output.stdout).unwrap();
+ */
+ output_file.write_all(output_string.as_bytes()).unwrap();
+}
diff --git a/src/conf.rs b/src/conf.rs
index 73dbfd66..3ac3811d 100644
--- a/src/conf.rs
+++ b/src/conf.rs
@@ -26,6 +26,12 @@ extern crate serde;
extern crate toml;
extern crate xdg;
+use crate::conf::deserializers::non_empty_string;
+use crate::terminal::Color;
+use melib::search::Query;
+use std::collections::HashSet;
+mod overrides;
+pub use overrides::*;
pub mod composing;
pub mod notifications;
pub mod pager;
@@ -46,10 +52,10 @@ pub use self::shortcuts::*;
pub use self::tags::*;
use self::default_vals::*;
-use self::listing::{ListingSettings, ListingSettingsOverride};
-use self::notifications::{NotificationsSettings, NotificationsSettingsOverride};
+use self::listing::ListingSettings;
+use self::notifications::NotificationsSettings;
use self::terminal::TerminalSettings;
-use crate::pager::{PagerSettings, PagerSettingsOverride};
+use crate::pager::PagerSettings;
use crate::plugins::Plugin;
use melib::conf::{AccountSettings, MailboxConf, ToggleFlag};
use melib::error::*;
@@ -101,38 +107,6 @@ macro_rules! mailbox_settings {
}};
}
-#[macro_export]
-macro_rules! override_def {
- ($override_name:ident,
- $(#[$outer:meta])*
- pub struct $name:ident { $( $(#[$fouter:meta])* $fname:ident : $ft:ty),*,
- }) => {
- $(#[$outer])*
- pub struct $name {
- $(
- $(#[$fouter])*
- pub $fname : $ft
- ),*
- }
- $(#[$outer])*
- pub struct $override_name {
- $(
- #[serde(default = "crate::conf::default_vals::none")]
- pub $fname : Option<$ft>
- ),*
- }
- impl Default for $override_name {
- fn default() -> Self {
- $override_name {
- $(
- $fname : None
- ),*
- }
- }
- }
- }
-}
-
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct MailUIConf {
#[serde(default)]
diff --git a/src/conf/composing.rs b/src/conf/composing.rs
index 1846e472..9dfa3d9b 100644
--- a/src/conf/composing.rs
+++ b/src/conf/composing.rs
@@ -21,34 +21,30 @@
//! Configuration for composing email.
use super::default_vals::{false_val, none, true_val};
-use crate::override_def;
use std::collections::HashMap;
-override_def!(
- ComposingSettingsOverride,
- /// Settings for writing and sending new e-mail
- #[derive(Debug, Serialize, Deserialize, Clone)]
- pub struct ComposingSettings {
- /// A command to pipe new emails to
- /// Required
- #[serde(alias = "mailer-cmd")]
- mailer_cmd: String,
- /// Command to launch editor. Can have arguments. Draft filename is given as the last argument. If it's missing, the environment variable $EDITOR is looked up.
- #[serde(default = "none", alias = "editor-cmd")]
- editor_cmd: Option,
- /// Embed editor (for terminal interfaces) instead of forking and waiting.
- #[serde(default = "false_val")]
- embed: bool,
- /// Set "format=flowed" in plain text attachments.
- /// Default: true
- #[serde(default = "true_val", alias = "format-flowed")]
- format_flowed: bool,
- /// Set default header values for new drafts
- /// Default: empty
- #[serde(default, alias = "default-header-values")]
- default_header_values: HashMap,
- }
-);
+/// Settings for writing and sending new e-mail
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct ComposingSettings {
+ /// A command to pipe new emails to
+ /// Required
+ #[serde(alias = "mailer-cmd")]
+ pub mailer_cmd: String,
+ /// Command to launch editor. Can have arguments. Draft filename is given as the last argument. If it's missing, the environment variable $EDITOR is looked up.
+ #[serde(default = "none", alias = "editor-cmd")]
+ pub editor_cmd: Option,
+ /// Embed editor (for terminal interfaces) instead of forking and waiting.
+ #[serde(default = "false_val")]
+ pub embed: bool,
+ /// Set "format=flowed" in plain text attachments.
+ /// Default: true
+ #[serde(default = "true_val", alias = "format-flowed")]
+ pub format_flowed: bool,
+ /// Set default header values for new drafts
+ /// Default: empty
+ #[serde(default, alias = "default-header-values")]
+ pub default_header_values: HashMap,
+}
impl Default for ComposingSettings {
fn default() -> Self {
diff --git a/src/conf/listing.rs b/src/conf/listing.rs
index 513342e0..d67a7291 100644
--- a/src/conf/listing.rs
+++ b/src/conf/listing.rs
@@ -20,38 +20,34 @@
*/
use super::{default_vals::*, IndexStyle};
-use crate::override_def;
use melib::search::Query;
-override_def!(
- ListingSettingsOverride,
- /// Settings for mail listings
- #[derive(Debug, Deserialize, Clone, Serialize)]
- pub struct ListingSettings {
- /// Number of context lines when going to next page.
- /// Default: 0
- #[serde(default = "zero_val", alias = "context-lines")]
- context_lines: usize,
+/// Settings for mail listings
+#[derive(Debug, Deserialize, Clone, Serialize)]
+pub struct ListingSettings {
+ /// Number of context lines when going to next page.
+ /// Default: 0
+ #[serde(default = "zero_val", alias = "context-lines")]
+ pub context_lines: usize,
- /// Datetime formatting passed verbatim to strftime(3).
- /// Default: %Y-%m-%d %T
- #[serde(default = "none", alias = "datetime-fmt")]
- datetime_fmt: Option,
+ /// Datetime formatting passed verbatim to strftime(3).
+ /// Default: %Y-%m-%d %T
+ #[serde(default = "none", alias = "datetime-fmt")]
+ pub datetime_fmt: Option,
- /// Show recent dates as `X {minutes,hours,days} ago`, up to 7 days.
- /// Default: true
- #[serde(default = "true_val", alias = "recent-dates")]
- recent_dates: bool,
+ /// Show recent dates as `X {minutes,hours,days} ago`, up to 7 days.
+ /// Default: true
+ #[serde(default = "true_val", alias = "recent-dates")]
+ pub recent_dates: bool,
- /// Show only envelopes that match this query
- /// Default: None
- #[serde(default = "none")]
- filter: Option,
+ /// Show only envelopes that match this query
+ /// Default: None
+ #[serde(default = "none")]
+ pub filter: Option,
- #[serde(default, alias = "index-style")]
- index_style: IndexStyle,
- }
-);
+ #[serde(default, alias = "index-style")]
+ pub index_style: IndexStyle,
+}
impl Default for ListingSettings {
fn default() -> Self {
diff --git a/src/conf/notifications.rs b/src/conf/notifications.rs
index 70182f53..f600440f 100644
--- a/src/conf/notifications.rs
+++ b/src/conf/notifications.rs
@@ -20,28 +20,24 @@
*/
use super::default_vals::{internal_value_false, none};
-use crate::override_def;
-override_def!(
- NotificationsSettingsOverride,
- /// Settings for the notifications function.
- #[derive(Debug, Serialize, Deserialize, Clone)]
- pub struct NotificationsSettings {
- /// A command to pipe notifications through
- /// Default: None
- #[serde(default = "none")]
- script: Option,
- /// A file location which has its size changed when new mail arrives (max 128 bytes). Can be
- /// used to trigger new mail notifications eg with `xbiff(1)`
- /// Default: None
- #[serde(default = "none", alias = "xbiff-file-path")]
- xbiff_file_path: Option,
- #[serde(default = "internal_value_false", alias = "play-sound")]
- play_sound: super::ToggleFlag,
- #[serde(default = "none", alias = "sound-file")]
- sound_file: Option,
- }
-);
+/// Settings for the notifications function.
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct NotificationsSettings {
+ /// A command to pipe notifications through
+ /// Default: None
+ #[serde(default = "none")]
+ pub script: Option,
+ /// A file location which has its size changed when new mail arrives (max 128 bytes). Can be
+ /// used to trigger new mail notifications eg with `xbiff(1)`
+ /// Default: None
+ #[serde(default = "none", alias = "xbiff-file-path")]
+ pub xbiff_file_path: Option,
+ #[serde(default = "internal_value_false", alias = "play-sound")]
+ pub play_sound: super::ToggleFlag,
+ #[serde(default = "none", alias = "sound-file")]
+ pub sound_file: Option,
+}
impl Default for NotificationsSettings {
fn default() -> Self {
diff --git a/src/conf/overrides.rs b/src/conf/overrides.rs
new file mode 100644
index 00000000..733897cc
--- /dev/null
+++ b/src/conf/overrides.rs
@@ -0,0 +1,281 @@
+/*
+ * meli - conf/overrides.rs
+ *
+ * Copyright 2020 Manos Pitsidianakis
+ *
+ * This file is part of meli.
+ *
+ * meli is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * meli is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with meli. If not, see .
+ */
+
+//! This module is automatically generated by build.rs.
+use super::*;
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct PagerSettingsOverride {
+ #[doc = " Number of context lines when going to next page."]
+ #[doc = " Default: 0"]
+ #[serde(alias = "pager-context")]
+ #[serde(default)]
+ pub pager_context: Option,
+ #[doc = " Stop at the end instead of displaying next mail."]
+ #[doc = " Default: false"]
+ #[serde(alias = "pager-stop")]
+ #[serde(default)]
+ pub pager_stop: Option,
+ #[doc = " Always show headers when scrolling."]
+ #[doc = " Default: true"]
+ #[serde(alias = "headers-sticky")]
+ #[serde(default)]
+ pub headers_sticky: Option,
+ #[doc = " The height of the pager in mail view, in percent."]
+ #[doc = " Default: 80"]
+ #[serde(alias = "pager-ratio")]
+ #[serde(default)]
+ pub pager_ratio: Option,
+ #[doc = " A command to pipe mail output through for viewing in pager."]
+ #[doc = " Default: None"]
+ #[serde(deserialize_with = "non_empty_string")]
+ #[serde(default)]
+ pub filter: Option