Add conf_override! macro
conf_override! wraps struct definitions and defines a secondary Override struct that wraps each field in an Option. The macro mailbox_settings! is used to select settings from an account & mailbox index. If a user defines an overriding setting, the macro returns the override instead of the immediately next in the hierarchy setting. The selection is done for a specific field as follows: if per-folder override is defined, return per-folder override else if per-account override is defined, return per-account override else return global setting field value.memfd
parent
a8c1016f37
commit
9ff54f236b
|
@ -39,7 +39,7 @@ mod status;
|
||||||
pub use self::status::*;
|
pub use self::status::*;
|
||||||
|
|
||||||
fn get_display_name(context: &Context, idx: usize) -> String {
|
fn get_display_name(context: &Context, idx: usize) -> String {
|
||||||
let settings = context.accounts[idx].runtime_settings.account();
|
let settings = context.accounts[idx].settings.account();
|
||||||
if let Some(d) = settings.display_name.as_ref() {
|
if let Some(d) = settings.display_name.as_ref() {
|
||||||
format!("{} <{}>", d, settings.identity)
|
format!("{} <{}>", d, settings.identity)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -147,7 +147,9 @@ impl Composer {
|
||||||
id: ComponentId::new_v4(),
|
id: ComponentId::new_v4(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
for (h, v) in context.settings.composing.default_header_values.iter() {
|
for (h, v) in
|
||||||
|
mailbox_acc_settings!(context[account_cursor].composing.default_header_values).iter()
|
||||||
|
{
|
||||||
if v.is_empty() {
|
if v.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -298,12 +300,9 @@ impl Composer {
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
&format!(
|
&format!(
|
||||||
"☑ sign with {}",
|
"☑ sign with {}",
|
||||||
context
|
mailbox_acc_settings!(context[self.account_cursor].pgp.key)
|
||||||
.settings
|
|
||||||
.pgp
|
|
||||||
.key
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(String::as_str)
|
.map(|s| s.as_str())
|
||||||
.unwrap_or("default key")
|
.unwrap_or("default key")
|
||||||
),
|
),
|
||||||
grid,
|
grid,
|
||||||
|
@ -392,7 +391,9 @@ impl Component for Composer {
|
||||||
|
|
||||||
if !self.initialized {
|
if !self.initialized {
|
||||||
if self.sign_mail.is_unset() {
|
if self.sign_mail.is_unset() {
|
||||||
self.sign_mail = ToggleFlag::InternalVal(context.settings.pgp.auto_sign);
|
self.sign_mail = ToggleFlag::InternalVal(*mailbox_acc_settings!(
|
||||||
|
context[self.account_cursor].pgp.auto_sign
|
||||||
|
));
|
||||||
}
|
}
|
||||||
if !self.draft.headers().contains_key("From") || self.draft.headers()["From"].is_empty()
|
if !self.draft.headers().contains_key("From") || self.draft.headers()["From"].is_empty()
|
||||||
{
|
{
|
||||||
|
@ -859,8 +860,10 @@ impl Component for Composer {
|
||||||
&& shortcut!(key == shortcuts[Self::DESCRIPTION]["edit_mail"]) =>
|
&& shortcut!(key == shortcuts[Self::DESCRIPTION]["edit_mail"]) =>
|
||||||
{
|
{
|
||||||
/* Edit draft in $EDITOR */
|
/* Edit draft in $EDITOR */
|
||||||
let settings = &context.settings;
|
let editor = if let Some(editor_cmd) =
|
||||||
let editor = if let Some(editor_cmd) = settings.composing.editor_cmd.as_ref() {
|
mailbox_acc_settings!(context[self.account_cursor].composing.editor_cmd)
|
||||||
|
.as_ref()
|
||||||
|
{
|
||||||
editor_cmd.to_string()
|
editor_cmd.to_string()
|
||||||
} else {
|
} else {
|
||||||
match std::env::var("EDITOR") {
|
match std::env::var("EDITOR") {
|
||||||
|
@ -884,7 +887,7 @@ impl Component for Composer {
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
if settings.composing.embed {
|
if *mailbox_acc_settings!(context[self.account_cursor].composing.embed) {
|
||||||
self.embed = Some(EmbedStatus::Running(
|
self.embed = Some(EmbedStatus::Running(
|
||||||
crate::terminal::embed::create_pty(
|
crate::terminal::embed::create_pty(
|
||||||
width!(self.embed_area),
|
width!(self.embed_area),
|
||||||
|
@ -1124,7 +1127,8 @@ impl Component for Composer {
|
||||||
Default::default()
|
Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let our_map: ShortcutMap = context.settings.shortcuts.composing.key_values();
|
let our_map: ShortcutMap =
|
||||||
|
mailbox_acc_settings!(context[self.account_cursor].shortcuts.composing).key_values();
|
||||||
map.insert(Composer::DESCRIPTION, our_map);
|
map.insert(Composer::DESCRIPTION, our_map);
|
||||||
|
|
||||||
map
|
map
|
||||||
|
@ -1178,9 +1182,10 @@ pub fn send_draft(
|
||||||
) -> bool {
|
) -> bool {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
let settings = &context.settings;
|
let format_flowed = *mailbox_acc_settings!(context[account_cursor].composing.format_flowed);
|
||||||
let format_flowed = settings.composing.format_flowed;
|
let parts = split_command!(mailbox_acc_settings!(
|
||||||
let parts = split_command!(settings.composing.mailer_cmd);
|
context[account_cursor].composing.mailer_cmd
|
||||||
|
));
|
||||||
if parts.is_empty() {
|
if parts.is_empty() {
|
||||||
context.replies.push_back(UIEvent::Notification(
|
context.replies.push_back(UIEvent::Notification(
|
||||||
None,
|
None,
|
||||||
|
@ -1233,8 +1238,12 @@ pub fn send_draft(
|
||||||
}
|
}
|
||||||
let output = crate::components::mail::pgp::sign(
|
let output = crate::components::mail::pgp::sign(
|
||||||
body.into(),
|
body.into(),
|
||||||
context.settings.pgp.gpg_binary.as_ref().map(String::as_str),
|
mailbox_acc_settings!(context[account_cursor].pgp.gpg_binary)
|
||||||
context.settings.pgp.key.as_ref().map(String::as_str),
|
.as_ref()
|
||||||
|
.map(|s| s.as_str()),
|
||||||
|
mailbox_acc_settings!(context[account_cursor].pgp.key)
|
||||||
|
.as_ref()
|
||||||
|
.map(|s| s.as_str()),
|
||||||
);
|
);
|
||||||
if let Err(e) = &output {
|
if let Err(e) = &output {
|
||||||
debug!("{:?} could not sign draft msg", e);
|
debug!("{:?} could not sign draft msg", e);
|
||||||
|
|
|
@ -576,8 +576,9 @@ impl Component for Listing {
|
||||||
{
|
{
|
||||||
/* Account might have no mailboxes yet if it's offline */
|
/* Account might have no mailboxes yet if it's offline */
|
||||||
/* Check if per-mailbox configuration overrides general configuration */
|
/* Check if per-mailbox configuration overrides general configuration */
|
||||||
let index_style =
|
let index_style = mailbox_settings!(
|
||||||
mailbox_acc_settings!(context[self.cursor_pos.0][mailbox_hash].index_style);
|
context[self.cursor_pos.0][mailbox_hash].listing.index_style
|
||||||
|
);
|
||||||
self.component.set_style(*index_style);
|
self.component.set_style(*index_style);
|
||||||
}
|
}
|
||||||
context
|
context
|
||||||
|
@ -1200,7 +1201,7 @@ impl Listing {
|
||||||
/* Check if per-mailbox configuration overrides general configuration */
|
/* Check if per-mailbox configuration overrides general configuration */
|
||||||
|
|
||||||
let index_style =
|
let index_style =
|
||||||
mailbox_acc_settings!(context[self.cursor_pos.0][mailbox_hash].index_style);
|
mailbox_settings!(context[self.cursor_pos.0][mailbox_hash].listing.index_style);
|
||||||
self.component.set_style(*index_style);
|
self.component.set_style(*index_style);
|
||||||
} else {
|
} else {
|
||||||
/* Set to dummy */
|
/* Set to dummy */
|
||||||
|
|
|
@ -650,31 +650,27 @@ impl CompactListing {
|
||||||
hash: ThreadHash,
|
hash: ThreadHash,
|
||||||
) -> EntryStrings {
|
) -> EntryStrings {
|
||||||
let thread = threads.thread_ref(hash);
|
let thread = threads.thread_ref(hash);
|
||||||
let mailbox = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf;
|
|
||||||
let mut tags = String::new();
|
let mut tags = String::new();
|
||||||
let mut colors: SmallVec<[_; 8]> = SmallVec::new();
|
let mut colors: SmallVec<[_; 8]> = SmallVec::new();
|
||||||
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
||||||
if let Some(t) = backend_lck.tags() {
|
if let Some(t) = backend_lck.tags() {
|
||||||
let tags_lck = t.read().unwrap();
|
let tags_lck = t.read().unwrap();
|
||||||
for t in e.labels().iter() {
|
for t in e.labels().iter() {
|
||||||
if mailbox
|
if mailbox_settings!(
|
||||||
.conf_override
|
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||||
.tags
|
.tags
|
||||||
.as_ref()
|
.ignore_tags
|
||||||
.map(|s| s.ignore_tags.contains(t))
|
)
|
||||||
.unwrap_or(false)
|
.contains(t)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
tags.push(' ');
|
tags.push(' ');
|
||||||
tags.push_str(tags_lck.get(t).as_ref().unwrap());
|
tags.push_str(tags_lck.get(t).as_ref().unwrap());
|
||||||
tags.push(' ');
|
tags.push(' ');
|
||||||
if let Some(&c) = mailbox
|
if let Some(&c) =
|
||||||
.conf_override
|
mailbox_settings!(context[self.cursor_pos.0][&self.cursor_pos.1].tags.colors)
|
||||||
.tags
|
.get(t)
|
||||||
.as_ref()
|
|
||||||
.map(|s| s.colors.get(t))
|
|
||||||
.unwrap_or(None)
|
|
||||||
{
|
{
|
||||||
colors.push(c);
|
colors.push(c);
|
||||||
} else {
|
} else {
|
||||||
|
@ -761,10 +757,12 @@ impl CompactListing {
|
||||||
.collection
|
.collection
|
||||||
.get_env(root_env_hash);
|
.get_env(root_env_hash);
|
||||||
use crate::cache::QueryTrait;
|
use crate::cache::QueryTrait;
|
||||||
if let Some(filter_query) =
|
if let Some(filter_query) = mailbox_settings!(
|
||||||
mailbox_settings!(context[self.cursor_pos.0][&self.cursor_pos.1].listing)
|
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||||
|
.listing
|
||||||
.filter
|
.filter
|
||||||
.as_ref()
|
)
|
||||||
|
.as_ref()
|
||||||
{
|
{
|
||||||
if !root_envelope.is_match(filter_query) {
|
if !root_envelope.is_match(filter_query) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -593,20 +593,28 @@ impl ConversationsListing {
|
||||||
hash: ThreadHash,
|
hash: ThreadHash,
|
||||||
) -> EntryStrings {
|
) -> EntryStrings {
|
||||||
let thread = threads.thread_ref(hash);
|
let thread = threads.thread_ref(hash);
|
||||||
let settings = mailbox_settings!(context[self.cursor_pos.0][&self.cursor_pos.1].tags);
|
|
||||||
let mut tags = String::new();
|
let mut tags = String::new();
|
||||||
let mut colors = SmallVec::new();
|
let mut colors = SmallVec::new();
|
||||||
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
||||||
if let Some(t) = backend_lck.tags() {
|
if let Some(t) = backend_lck.tags() {
|
||||||
let tags_lck = t.read().unwrap();
|
let tags_lck = t.read().unwrap();
|
||||||
for t in e.labels().iter() {
|
for t in e.labels().iter() {
|
||||||
if settings.ignore_tags.contains(t) {
|
if mailbox_settings!(
|
||||||
|
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||||
|
.tags
|
||||||
|
.ignore_tags
|
||||||
|
)
|
||||||
|
.contains(t)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
tags.push(' ');
|
tags.push(' ');
|
||||||
tags.push_str(tags_lck.get(t).as_ref().unwrap());
|
tags.push_str(tags_lck.get(t).as_ref().unwrap());
|
||||||
tags.push(' ');
|
tags.push(' ');
|
||||||
if let Some(&c) = settings.colors.get(t) {
|
if let Some(&c) =
|
||||||
|
mailbox_settings!(context[self.cursor_pos.0][&self.cursor_pos.1].tags.colors)
|
||||||
|
.get(t)
|
||||||
|
{
|
||||||
colors.push(c);
|
colors.push(c);
|
||||||
} else {
|
} else {
|
||||||
colors.push(Color::Byte(8));
|
colors.push(Color::Byte(8));
|
||||||
|
@ -698,10 +706,12 @@ impl ConversationsListing {
|
||||||
.collection
|
.collection
|
||||||
.get_env(root_env_hash);
|
.get_env(root_env_hash);
|
||||||
use crate::cache::QueryTrait;
|
use crate::cache::QueryTrait;
|
||||||
if let Some(filter_query) =
|
if let Some(filter_query) = mailbox_settings!(
|
||||||
mailbox_settings!(context[self.cursor_pos.0][&self.cursor_pos.1].listing)
|
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||||
|
.listing
|
||||||
.filter
|
.filter
|
||||||
.as_ref()
|
)
|
||||||
|
.as_ref()
|
||||||
{
|
{
|
||||||
if !root_envelope.is_match(filter_query) {
|
if !root_envelope.is_match(filter_query) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -612,21 +612,30 @@ impl PlainListing {
|
||||||
id: ComponentId::new_v4(),
|
id: ComponentId::new_v4(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_entry_string(&self, e: EnvelopeRef, context: &Context) -> EntryStrings {
|
fn make_entry_string(&self, e: EnvelopeRef, context: &Context) -> EntryStrings {
|
||||||
let settings = mailbox_settings!(context[self.cursor_pos.0][&self.cursor_pos.1].tags);
|
|
||||||
let mut tags = String::new();
|
let mut tags = String::new();
|
||||||
let mut colors = SmallVec::new();
|
let mut colors = SmallVec::new();
|
||||||
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
||||||
if let Some(t) = backend_lck.tags() {
|
if let Some(t) = backend_lck.tags() {
|
||||||
let tags_lck = t.read().unwrap();
|
let tags_lck = t.read().unwrap();
|
||||||
for t in e.labels().iter() {
|
for t in e.labels().iter() {
|
||||||
if settings.ignore_tags.contains(t) {
|
if mailbox_settings!(
|
||||||
|
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||||
|
.tags
|
||||||
|
.ignore_tags
|
||||||
|
)
|
||||||
|
.contains(t)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
tags.push(' ');
|
tags.push(' ');
|
||||||
tags.push_str(tags_lck.get(t).as_ref().unwrap());
|
tags.push_str(tags_lck.get(t).as_ref().unwrap());
|
||||||
tags.push(' ');
|
tags.push(' ');
|
||||||
if let Some(&c) = settings.colors.get(t) {
|
if let Some(&c) =
|
||||||
|
mailbox_settings!(context[self.cursor_pos.0][&self.cursor_pos.1].tags.colors)
|
||||||
|
.get(t)
|
||||||
|
{
|
||||||
colors.push(c);
|
colors.push(c);
|
||||||
} else {
|
} else {
|
||||||
colors.push(Color::Byte(8));
|
colors.push(Color::Byte(8));
|
||||||
|
@ -705,10 +714,12 @@ impl PlainListing {
|
||||||
}
|
}
|
||||||
let envelope: EnvelopeRef = context.accounts[self.cursor_pos.0].collection.get_env(i);
|
let envelope: EnvelopeRef = context.accounts[self.cursor_pos.0].collection.get_env(i);
|
||||||
use crate::cache::QueryTrait;
|
use crate::cache::QueryTrait;
|
||||||
if let Some(filter_query) =
|
if let Some(filter_query) = mailbox_settings!(
|
||||||
mailbox_settings!(context[self.cursor_pos.0][&self.cursor_pos.1].listing)
|
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||||
|
.listing
|
||||||
.filter
|
.filter
|
||||||
.as_ref()
|
)
|
||||||
|
.as_ref()
|
||||||
{
|
{
|
||||||
if !envelope.is_match(filter_query) {
|
if !envelope.is_match(filter_query) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -290,7 +290,7 @@ impl StatusPanel {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
&a.runtime_settings.account().identity,
|
&a.settings.account().identity,
|
||||||
&mut self.content,
|
&mut self.content,
|
||||||
self.theme_default.fg,
|
self.theme_default.fg,
|
||||||
self.theme_default.bg,
|
self.theme_default.bg,
|
||||||
|
|
|
@ -161,10 +161,15 @@ impl MailView {
|
||||||
Some(Box::new(move |a: &'closure Attachment, v: &mut Vec<u8>| {
|
Some(Box::new(move |a: &'closure Attachment, v: &mut Vec<u8>| {
|
||||||
if a.content_type().is_text_html() {
|
if a.content_type().is_text_html() {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
let settings =
|
|
||||||
mailbox_settings!(context[self.coordinates.0][&self.coordinates.1].pager);
|
|
||||||
/* FIXME: duplication with view/html.rs */
|
/* FIXME: duplication with view/html.rs */
|
||||||
if let Some(filter_invocation) = settings.html_filter.as_ref() {
|
if let Some(filter_invocation) = mailbox_settings!(
|
||||||
|
context[self.coordinates.0][&self.coordinates.1]
|
||||||
|
.pager
|
||||||
|
.html_filter
|
||||||
|
)
|
||||||
|
.as_ref()
|
||||||
|
{
|
||||||
let parts = split_command!(filter_invocation);
|
let parts = split_command!(filter_invocation);
|
||||||
let (cmd, args) = (parts[0], &parts[1..]);
|
let (cmd, args) = (parts[0], &parts[1..]);
|
||||||
let command_obj = Command::new(cmd)
|
let command_obj = Command::new(cmd)
|
||||||
|
@ -400,10 +405,11 @@ impl Component for MailView {
|
||||||
|
|
||||||
self.headers_no = 0;
|
self.headers_no = 0;
|
||||||
let mut skip_header_ctr = self.headers_cursor;
|
let mut skip_header_ctr = self.headers_cursor;
|
||||||
let sticky =
|
let sticky = *mailbox_settings!(
|
||||||
mailbox_settings!(context[self.coordinates.0][&self.coordinates.1].pager)
|
context[self.coordinates.0][&self.coordinates.1]
|
||||||
|
.pager
|
||||||
.headers_sticky
|
.headers_sticky
|
||||||
|| height_p < height;
|
) || height_p < height;
|
||||||
let (_, mut y) = upper_left;
|
let (_, mut y) = upper_left;
|
||||||
macro_rules! print_header {
|
macro_rules! print_header {
|
||||||
($($string:expr)+) => {
|
($($string:expr)+) => {
|
||||||
|
@ -573,9 +579,11 @@ impl Component for MailView {
|
||||||
context
|
context
|
||||||
.dirty_areas
|
.dirty_areas
|
||||||
.push_back((upper_left, set_y(bottom_right, y + 3)));
|
.push_back((upper_left, set_y(bottom_right, y + 3)));
|
||||||
if !mailbox_settings!(context[self.coordinates.0][&self.coordinates.1].pager)
|
if !*mailbox_settings!(
|
||||||
.headers_sticky
|
context[self.coordinates.0][&self.coordinates.1]
|
||||||
{
|
.pager
|
||||||
|
.headers_sticky
|
||||||
|
) {
|
||||||
let height_p = self.pager.size().1;
|
let height_p = self.pager.size().1;
|
||||||
|
|
||||||
let height = height!(area).saturating_sub(y).saturating_sub(1);
|
let height = height!(area).saturating_sub(y).saturating_sub(1);
|
||||||
|
@ -644,9 +652,10 @@ impl Component for MailView {
|
||||||
}
|
}
|
||||||
ViewMode::Normal
|
ViewMode::Normal
|
||||||
if mailbox_settings!(
|
if mailbox_settings!(
|
||||||
context[self.coordinates.0][&self.coordinates.1].pager
|
context[self.coordinates.0][&self.coordinates.1]
|
||||||
|
.pager
|
||||||
|
.auto_choose_multipart_alternative
|
||||||
)
|
)
|
||||||
.auto_choose_multipart_alternative
|
|
||||||
.is_true()
|
.is_true()
|
||||||
&& match body.content_type {
|
&& match body.content_type {
|
||||||
ContentType::Multipart {
|
ContentType::Multipart {
|
||||||
|
@ -813,10 +822,11 @@ impl Component for MailView {
|
||||||
_ => match event {
|
_ => match event {
|
||||||
UIEvent::Input(ref key)
|
UIEvent::Input(ref key)
|
||||||
if shortcut!(key == shortcuts[Pager::DESCRIPTION]["scroll_up"])
|
if shortcut!(key == shortcuts[Pager::DESCRIPTION]["scroll_up"])
|
||||||
&& !mailbox_settings!(
|
&& !*mailbox_settings!(
|
||||||
context[self.coordinates.0][&self.coordinates.1].pager
|
context[self.coordinates.0][&self.coordinates.1]
|
||||||
|
.pager
|
||||||
|
.headers_sticky
|
||||||
)
|
)
|
||||||
.headers_sticky
|
|
||||||
&& self.headers_cursor <= self.headers_no =>
|
&& self.headers_cursor <= self.headers_no =>
|
||||||
{
|
{
|
||||||
self.force_draw_headers = true;
|
self.force_draw_headers = true;
|
||||||
|
@ -832,10 +842,11 @@ impl Component for MailView {
|
||||||
}
|
}
|
||||||
UIEvent::Input(ref key)
|
UIEvent::Input(ref key)
|
||||||
if shortcut!(key == shortcuts[Pager::DESCRIPTION]["scroll_down"])
|
if shortcut!(key == shortcuts[Pager::DESCRIPTION]["scroll_down"])
|
||||||
&& !mailbox_settings!(
|
&& !*mailbox_settings!(
|
||||||
context[self.coordinates.0][&self.coordinates.1].pager
|
context[self.coordinates.0][&self.coordinates.1]
|
||||||
|
.pager
|
||||||
|
.headers_sticky
|
||||||
)
|
)
|
||||||
.headers_sticky
|
|
||||||
&& self.headers_cursor < self.headers_no =>
|
&& self.headers_cursor < self.headers_no =>
|
||||||
{
|
{
|
||||||
self.force_draw_headers = true;
|
self.force_draw_headers = true;
|
||||||
|
|
127
src/conf.rs
127
src/conf.rs
|
@ -46,10 +46,10 @@ pub use self::shortcuts::*;
|
||||||
pub use self::tags::*;
|
pub use self::tags::*;
|
||||||
|
|
||||||
use self::default_vals::*;
|
use self::default_vals::*;
|
||||||
use self::listing::ListingSettings;
|
use self::listing::{ListingSettings, ListingSettingsOverride};
|
||||||
use self::notifications::NotificationsSettings;
|
use self::notifications::{NotificationsSettings, NotificationsSettingsOverride};
|
||||||
use self::terminal::TerminalSettings;
|
use self::terminal::TerminalSettings;
|
||||||
use crate::pager::PagerSettings;
|
use crate::pager::{PagerSettings, PagerSettingsOverride};
|
||||||
use crate::plugins::Plugin;
|
use crate::plugins::Plugin;
|
||||||
use melib::conf::{AccountSettings, MailboxConf, ToggleFlag};
|
use melib::conf::{AccountSettings, MailboxConf, ToggleFlag};
|
||||||
use melib::error::*;
|
use melib::error::*;
|
||||||
|
@ -72,38 +72,87 @@ macro_rules! split_command {
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! mailbox_acc_settings {
|
macro_rules! mailbox_acc_settings {
|
||||||
($context:ident[$account_idx:expr][$mailbox_path:expr].$field:ident) => {{
|
($context:ident[$account_idx:expr].$setting:ident.$field:ident) => {{
|
||||||
$context.accounts[$account_idx][$mailbox_path]
|
$context.accounts[$account_idx]
|
||||||
.conf
|
.settings
|
||||||
.conf_override
|
.conf_override
|
||||||
|
.$setting
|
||||||
.$field
|
.$field
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap_or(&$context.accounts[$account_idx].settings.conf.$field)
|
.unwrap_or(&$context.settings.$setting.$field)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! mailbox_settings {
|
macro_rules! mailbox_settings {
|
||||||
($context:ident[$account_idx:expr][$mailbox_path:expr].$field:ident) => {{
|
($context:ident[$account_idx:expr][$mailbox_path:expr].$setting:ident.$field:ident) => {{
|
||||||
$context.accounts[$account_idx][$mailbox_path]
|
$context.accounts[$account_idx][$mailbox_path]
|
||||||
.conf
|
.conf
|
||||||
.conf_override
|
.conf_override
|
||||||
|
.$setting
|
||||||
.$field
|
.$field
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap_or(&$context.settings.$field)
|
.or($context.accounts[$account_idx]
|
||||||
|
.settings
|
||||||
|
.conf_override
|
||||||
|
.$setting
|
||||||
|
.$field
|
||||||
|
.as_ref())
|
||||||
|
.unwrap_or(&$context.settings.$setting.$field)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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 {
|
||||||
|
$(
|
||||||
|
$(#[$fouter])*
|
||||||
|
pub $fname : Option<$ft>
|
||||||
|
),*
|
||||||
|
}
|
||||||
|
impl Default for $override_name {
|
||||||
|
fn default() -> Self {
|
||||||
|
$override_name {
|
||||||
|
$(
|
||||||
|
$fname : None
|
||||||
|
),*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct MailUIConf {
|
pub struct MailUIConf {
|
||||||
pub pager: Option<PagerSettings>,
|
#[serde(default)]
|
||||||
pub listing: Option<ListingSettings>,
|
pub pager: PagerSettingsOverride,
|
||||||
pub notifications: Option<NotificationsSettings>,
|
#[serde(default)]
|
||||||
pub shortcuts: Option<Shortcuts>,
|
pub listing: ListingSettingsOverride,
|
||||||
pub composing: Option<ComposingSettings>,
|
#[serde(default)]
|
||||||
|
pub notifications: NotificationsSettingsOverride,
|
||||||
|
#[serde(default)]
|
||||||
|
pub shortcuts: ShortcutsOverride,
|
||||||
|
#[serde(default)]
|
||||||
|
pub composing: ComposingSettingsOverride,
|
||||||
|
#[serde(default)]
|
||||||
pub identity: Option<String>,
|
pub identity: Option<String>,
|
||||||
pub index_style: Option<IndexStyle>,
|
#[serde(default)]
|
||||||
pub tags: Option<TagsSettings>,
|
pub tags: TagsSettingsOverride,
|
||||||
|
#[serde(default)]
|
||||||
pub theme: Option<Theme>,
|
pub theme: Option<Theme>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub pgp: PGPSettingsOverride,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -133,7 +182,6 @@ pub struct FileAccount {
|
||||||
identity: String,
|
identity: String,
|
||||||
#[serde(default = "none")]
|
#[serde(default = "none")]
|
||||||
display_name: Option<String>,
|
display_name: Option<String>,
|
||||||
pub index_style: IndexStyle,
|
|
||||||
|
|
||||||
#[serde(default = "false_val")]
|
#[serde(default = "false_val")]
|
||||||
read_only: bool,
|
read_only: bool,
|
||||||
|
@ -148,6 +196,8 @@ pub struct FileAccount {
|
||||||
#[serde(default = "none")]
|
#[serde(default = "none")]
|
||||||
pub refresh_command: Option<String>,
|
pub refresh_command: Option<String>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
pub conf_override: MailUIConf,
|
||||||
|
#[serde(flatten)]
|
||||||
#[serde(deserialize_with = "extra_settings")]
|
#[serde(deserialize_with = "extra_settings")]
|
||||||
pub extra: HashMap<String, String>, /* use custom deserializer to convert any given value (eg bool, number, etc) to string */
|
pub extra: HashMap<String, String>, /* use custom deserializer to convert any given value (eg bool, number, etc) to string */
|
||||||
}
|
}
|
||||||
|
@ -180,6 +230,7 @@ impl From<FileAccount> for AccountConf {
|
||||||
let mailbox_confs = x.mailboxes.clone();
|
let mailbox_confs = x.mailboxes.clone();
|
||||||
AccountConf {
|
AccountConf {
|
||||||
account: acc,
|
account: acc,
|
||||||
|
conf_override: x.conf_override.clone(),
|
||||||
conf: x,
|
conf: x,
|
||||||
mailbox_confs,
|
mailbox_confs,
|
||||||
}
|
}
|
||||||
|
@ -195,10 +246,6 @@ impl FileAccount {
|
||||||
&self.root_mailbox
|
&self.root_mailbox
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn index_style(&self) -> IndexStyle {
|
|
||||||
self.index_style
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cache_type(&self) -> &CacheType {
|
pub fn cache_type(&self) -> &CacheType {
|
||||||
&self.cache_type
|
&self.cache_type
|
||||||
}
|
}
|
||||||
|
@ -230,6 +277,7 @@ pub struct FileSettings {
|
||||||
pub struct AccountConf {
|
pub struct AccountConf {
|
||||||
pub(crate) account: AccountSettings,
|
pub(crate) account: AccountSettings,
|
||||||
pub(crate) conf: FileAccount,
|
pub(crate) conf: FileAccount,
|
||||||
|
pub conf_override: MailUIConf,
|
||||||
pub(crate) mailbox_confs: HashMap<String, FileMailboxConf>,
|
pub(crate) mailbox_confs: HashMap<String, FileMailboxConf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,6 +285,9 @@ impl AccountConf {
|
||||||
pub fn account(&self) -> &AccountSettings {
|
pub fn account(&self) -> &AccountSettings {
|
||||||
&self.account
|
&self.account
|
||||||
}
|
}
|
||||||
|
pub fn account_mut(&mut self) -> &mut AccountSettings {
|
||||||
|
&mut self.account
|
||||||
|
}
|
||||||
pub fn conf(&self) -> &FileAccount {
|
pub fn conf(&self) -> &FileAccount {
|
||||||
&self.conf
|
&self.conf
|
||||||
}
|
}
|
||||||
|
@ -375,8 +426,8 @@ impl FileSettings {
|
||||||
extra,
|
extra,
|
||||||
manual_refresh,
|
manual_refresh,
|
||||||
refresh_command: _,
|
refresh_command: _,
|
||||||
index_style: _,
|
|
||||||
cache_type: _,
|
cache_type: _,
|
||||||
|
conf_override: _,
|
||||||
} = acc.clone();
|
} = acc.clone();
|
||||||
|
|
||||||
let lowercase_format = format.to_lowercase();
|
let lowercase_format = format.to_lowercase();
|
||||||
|
@ -448,48 +499,48 @@ impl Default for IndexStyle {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
mod default_vals {
|
mod default_vals {
|
||||||
pub(in crate::conf) fn false_val() -> bool {
|
pub(in crate::conf) fn false_val<T: std::convert::From<bool>>() -> T {
|
||||||
false
|
false.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in crate::conf) fn true_val() -> bool {
|
pub(in crate::conf) fn true_val<T: std::convert::From<bool>>() -> T {
|
||||||
true
|
true.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in crate::conf) fn zero_val() -> usize {
|
pub(in crate::conf) fn zero_val<T: std::convert::From<usize>>() -> T {
|
||||||
0
|
0.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in crate::conf) fn eighty_percent() -> usize {
|
pub(in crate::conf) fn eighty_val<T: std::convert::From<usize>>() -> T {
|
||||||
80
|
80.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in crate::conf) fn none<T>() -> Option<T> {
|
pub(in crate::conf) fn none<T>() -> Option<T> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in crate::conf) fn internal_value_false() -> super::ToggleFlag {
|
pub(in crate::conf) fn internal_value_false<T: std::convert::From<super::ToggleFlag>>() -> T {
|
||||||
super::ToggleFlag::InternalVal(false)
|
super::ToggleFlag::InternalVal(false).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in crate::conf) fn internal_value_true() -> super::ToggleFlag {
|
pub(in crate::conf) fn internal_value_true<T: std::convert::From<super::ToggleFlag>>() -> T {
|
||||||
super::ToggleFlag::InternalVal(true)
|
super::ToggleFlag::InternalVal(true).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod deserializers {
|
mod deserializers {
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
pub(in crate::conf) fn non_empty_string<'de, D>(
|
pub(in crate::conf) fn non_empty_string<'de, D, T: std::convert::From<Option<String>>>(
|
||||||
deserializer: D,
|
deserializer: D,
|
||||||
) -> std::result::Result<Option<String>, D::Error>
|
) -> std::result::Result<T, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let s = <String>::deserialize(deserializer)?;
|
let s = <String>::deserialize(deserializer)?;
|
||||||
if s.is_empty() {
|
if s.is_empty() {
|
||||||
Ok(None)
|
Ok(None.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(Some(s))
|
Ok(Some(s).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,6 @@ pub struct Account {
|
||||||
pub(crate) address_book: AddressBook,
|
pub(crate) address_book: AddressBook,
|
||||||
pub(crate) work_context: WorkContext,
|
pub(crate) work_context: WorkContext,
|
||||||
pub(crate) settings: AccountConf,
|
pub(crate) settings: AccountConf,
|
||||||
pub(crate) runtime_settings: AccountConf,
|
|
||||||
pub(crate) backend: Arc<RwLock<Box<dyn MailBackend>>>,
|
pub(crate) backend: Arc<RwLock<Box<dyn MailBackend>>>,
|
||||||
|
|
||||||
sender: Sender<ThreadEvent>,
|
sender: Sender<ThreadEvent>,
|
||||||
|
@ -242,7 +241,6 @@ impl Account {
|
||||||
sent_mailbox: Default::default(),
|
sent_mailbox: Default::default(),
|
||||||
collection: Default::default(),
|
collection: Default::default(),
|
||||||
work_context,
|
work_context,
|
||||||
runtime_settings: settings.clone(),
|
|
||||||
settings,
|
settings,
|
||||||
backend: Arc::new(RwLock::new(backend)),
|
backend: Arc::new(RwLock::new(backend)),
|
||||||
notify_fn,
|
notify_fn,
|
||||||
|
|
|
@ -21,29 +21,34 @@
|
||||||
|
|
||||||
//! Configuration for composing email.
|
//! Configuration for composing email.
|
||||||
use super::default_vals::{false_val, none, true_val};
|
use super::default_vals::{false_val, none, true_val};
|
||||||
|
use crate::override_def;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// Settings for writing and sending new e-mail
|
override_def!(
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
ComposingSettingsOverride,
|
||||||
pub struct ComposingSettings {
|
/// Settings for writing and sending new e-mail
|
||||||
/// A command to pipe new emails to
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
/// Required
|
pub struct ComposingSettings {
|
||||||
pub mailer_cmd: String,
|
/// A command to pipe new emails to
|
||||||
/// 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.
|
/// Required
|
||||||
#[serde(default = "none")]
|
#[serde(alias = "mailer-cmd")]
|
||||||
pub editor_cmd: Option<String>,
|
mailer_cmd: String,
|
||||||
/// Embed editor (for terminal interfaces) instead of forking and waiting.
|
/// 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 = "false_val")]
|
#[serde(default = "none", alias = "editor-cmd")]
|
||||||
pub embed: bool,
|
editor_cmd: Option<String>,
|
||||||
/// Set "format=flowed" in plain text attachments.
|
/// Embed editor (for terminal interfaces) instead of forking and waiting.
|
||||||
/// Default: true
|
#[serde(default = "false_val")]
|
||||||
#[serde(default = "true_val")]
|
embed: bool,
|
||||||
pub format_flowed: bool,
|
/// Set "format=flowed" in plain text attachments.
|
||||||
/// Set default header values for new drafts
|
/// Default: true
|
||||||
/// Default: empty
|
#[serde(default = "true_val", alias = "format-flowed")]
|
||||||
#[serde(default)]
|
format_flowed: bool,
|
||||||
pub default_header_values: HashMap<String, String>,
|
/// Set default header values for new drafts
|
||||||
}
|
/// Default: empty
|
||||||
|
#[serde(default, alias = "default-header-values")]
|
||||||
|
default_header_values: HashMap<String, String>,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
impl Default for ComposingSettings {
|
impl Default for ComposingSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
|
|
@ -19,29 +19,48 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::default_vals::*;
|
use super::{default_vals::*, IndexStyle};
|
||||||
use crate::cache::Query;
|
use crate::cache::Query;
|
||||||
|
use crate::override_def;
|
||||||
|
|
||||||
/// Settings for mail listings
|
override_def!(
|
||||||
#[derive(Debug, Deserialize, Clone, Default, Serialize)]
|
ListingSettingsOverride,
|
||||||
pub struct ListingSettings {
|
/// Settings for mail listings
|
||||||
/// Number of context lines when going to next page.
|
#[derive(Debug, Deserialize, Clone, Serialize)]
|
||||||
/// Default: 0
|
pub struct ListingSettings {
|
||||||
#[serde(default = "zero_val")]
|
/// Number of context lines when going to next page.
|
||||||
pub context_lines: usize,
|
/// Default: 0
|
||||||
|
#[serde(default = "zero_val", alias = "context-lines")]
|
||||||
|
context_lines: usize,
|
||||||
|
|
||||||
/// Datetime formatting passed verbatim to strftime(3).
|
/// Datetime formatting passed verbatim to strftime(3).
|
||||||
/// Default: %Y-%m-%d %T
|
/// Default: %Y-%m-%d %T
|
||||||
#[serde(default = "none")]
|
#[serde(default = "none", alias = "datetime-fmt")]
|
||||||
pub datetime_fmt: Option<String>,
|
datetime_fmt: Option<String>,
|
||||||
|
|
||||||
/// Show recent dates as `X {minutes,hours,days} ago`, up to 7 days.
|
/// Show recent dates as `X {minutes,hours,days} ago`, up to 7 days.
|
||||||
/// Default: true
|
/// Default: true
|
||||||
#[serde(default = "true_val")]
|
#[serde(default = "true_val", alias = "recent-dates")]
|
||||||
pub recent_dates: bool,
|
recent_dates: bool,
|
||||||
|
|
||||||
/// Show only envelopes that match this query
|
/// Show only envelopes that match this query
|
||||||
/// Default: None
|
/// Default: None
|
||||||
#[serde(default = "none")]
|
#[serde(default = "none")]
|
||||||
pub filter: Option<Query>,
|
filter: Option<Query>,
|
||||||
|
|
||||||
|
#[serde(default, alias = "index-style")]
|
||||||
|
index_style: IndexStyle,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
impl Default for ListingSettings {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
context_lines: 0,
|
||||||
|
datetime_fmt: None,
|
||||||
|
recent_dates: true,
|
||||||
|
filter: None,
|
||||||
|
index_style: IndexStyle::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,26 +19,37 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::default_vals::internal_value_false;
|
use super::default_vals::{internal_value_false, none};
|
||||||
|
use crate::override_def;
|
||||||
|
|
||||||
fn none() -> Option<String> {
|
override_def!(
|
||||||
None
|
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<String>,
|
||||||
|
/// 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<String>,
|
||||||
|
#[serde(default = "internal_value_false", alias = "play-sound")]
|
||||||
|
play_sound: super::ToggleFlag,
|
||||||
|
#[serde(default = "none", alias = "sound-file")]
|
||||||
|
sound_file: Option<String>,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
/// Settings for the notifications function.
|
impl Default for NotificationsSettings {
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
fn default() -> Self {
|
||||||
pub struct NotificationsSettings {
|
Self {
|
||||||
/// A command to pipe notifications through
|
script: None,
|
||||||
/// Default: None
|
xbiff_file_path: None,
|
||||||
#[serde(default = "none")]
|
play_sound: super::ToggleFlag::InternalVal(false),
|
||||||
pub script: Option<String>,
|
sound_file: None,
|
||||||
/// 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")]
|
|
||||||
pub xbiff_file_path: Option<String>,
|
|
||||||
#[serde(default = "internal_value_false")]
|
|
||||||
pub play_sound: super::ToggleFlag,
|
|
||||||
#[serde(default = "none")]
|
|
||||||
pub sound_file: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,63 +23,87 @@
|
||||||
|
|
||||||
use super::default_vals::*;
|
use super::default_vals::*;
|
||||||
use super::deserializers::*;
|
use super::deserializers::*;
|
||||||
|
use crate::override_def;
|
||||||
use melib::ToggleFlag;
|
use melib::ToggleFlag;
|
||||||
|
|
||||||
/// Settings for the pager function.
|
override_def!(
|
||||||
#[derive(Debug, Deserialize, Clone, Default, Serialize)]
|
PagerSettingsOverride,
|
||||||
pub struct PagerSettings {
|
/// Settings for the pager function.
|
||||||
/// Number of context lines when going to next page.
|
#[derive(Debug, Deserialize, Clone, Serialize)]
|
||||||
/// Default: 0
|
pub struct PagerSettings {
|
||||||
#[serde(default = "zero_val")]
|
/// Number of context lines when going to next page.
|
||||||
pub pager_context: usize,
|
/// Default: 0
|
||||||
|
#[serde(default = "zero_val", alias = "pager-context")]
|
||||||
|
pager_context: usize,
|
||||||
|
|
||||||
/// Stop at the end instead of displaying next mail.
|
/// Stop at the end instead of displaying next mail.
|
||||||
/// Default: false
|
/// Default: false
|
||||||
#[serde(default = "false_val")]
|
#[serde(default = "false_val", alias = "pager-stop")]
|
||||||
pub pager_stop: bool,
|
pager_stop: bool,
|
||||||
|
|
||||||
/// Always show headers when scrolling.
|
/// Always show headers when scrolling.
|
||||||
/// Default: true
|
/// Default: true
|
||||||
#[serde(default = "true_val")]
|
#[serde(default = "true_val", alias = "headers-sticky")]
|
||||||
pub headers_sticky: bool,
|
headers_sticky: bool,
|
||||||
|
|
||||||
/// The height of the pager in mail view, in percent.
|
/// The height of the pager in mail view, in percent.
|
||||||
/// Default: 80
|
/// Default: 80
|
||||||
#[serde(default = "eighty_percent")]
|
#[serde(default = "eighty_val", alias = "pager-ratio")]
|
||||||
pub pager_ratio: usize,
|
pager_ratio: usize,
|
||||||
|
|
||||||
/// A command to pipe mail output through for viewing in pager.
|
/// A command to pipe mail output through for viewing in pager.
|
||||||
/// Default: None
|
/// Default: None
|
||||||
#[serde(default = "none", deserialize_with = "non_empty_string")]
|
#[serde(default = "none", deserialize_with = "non_empty_string")]
|
||||||
pub filter: Option<String>,
|
filter: Option<String>,
|
||||||
|
|
||||||
/// A command to pipe html output before displaying it in a pager
|
/// A command to pipe html output before displaying it in a pager
|
||||||
/// Default: None
|
/// Default: None
|
||||||
#[serde(default = "none", deserialize_with = "non_empty_string")]
|
#[serde(
|
||||||
pub html_filter: Option<String>,
|
default = "none",
|
||||||
|
deserialize_with = "non_empty_string",
|
||||||
|
alias = "html-filter"
|
||||||
|
)]
|
||||||
|
html_filter: Option<String>,
|
||||||
|
|
||||||
/// Respect "format=flowed"
|
/// Respect "format=flowed"
|
||||||
/// Default: true
|
/// Default: true
|
||||||
#[serde(default = "true_val")]
|
#[serde(default = "true_val", alias = "format-flowed")]
|
||||||
pub format_flowed: bool,
|
format_flowed: bool,
|
||||||
|
|
||||||
/// Split long lines that would overflow on the x axis.
|
/// Split long lines that would overflow on the x axis.
|
||||||
/// Default: true
|
/// Default: true
|
||||||
#[serde(default = "true_val")]
|
#[serde(default = "true_val", alias = "split-long-lines")]
|
||||||
pub split_long_lines: bool,
|
split_long_lines: bool,
|
||||||
|
|
||||||
/// Minimum text width in columns.
|
/// Minimum text width in columns.
|
||||||
/// Default: 80
|
/// Default: 80
|
||||||
#[serde(default = "eighty_val")]
|
#[serde(default = "eighty_val", alias = "minimum-width")]
|
||||||
pub minimum_width: usize,
|
minimum_width: usize,
|
||||||
|
|
||||||
/// Choose `text/html` alternative if `text/plain` is empty in `multipart/alternative`
|
/// Choose `text/html` alternative if `text/plain` is empty in `multipart/alternative`
|
||||||
/// attachments.
|
/// attachments.
|
||||||
/// Default: true
|
/// Default: true
|
||||||
#[serde(default = "internal_value_true")]
|
#[serde(
|
||||||
pub auto_choose_multipart_alternative: ToggleFlag,
|
default = "internal_value_true",
|
||||||
}
|
alias = "auto-choose-multipart-alternative"
|
||||||
|
)]
|
||||||
fn eighty_val() -> usize {
|
auto_choose_multipart_alternative: ToggleFlag,
|
||||||
80
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
impl Default for PagerSettings {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
pager_context: 0,
|
||||||
|
pager_stop: false,
|
||||||
|
headers_sticky: true,
|
||||||
|
pager_ratio: 80,
|
||||||
|
filter: None,
|
||||||
|
html_filter: None,
|
||||||
|
format_flowed: true,
|
||||||
|
split_long_lines: true,
|
||||||
|
minimum_width: 80,
|
||||||
|
auto_choose_multipart_alternative: ToggleFlag::InternalVal(true),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,26 +20,30 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::default_vals::*;
|
use super::default_vals::*;
|
||||||
|
use crate::override_def;
|
||||||
|
|
||||||
/// Settings for digital signing and encryption
|
override_def!(
|
||||||
#[derive(Debug, Deserialize, Clone, Serialize)]
|
PGPSettingsOverride,
|
||||||
pub struct PGPSettings {
|
/// Settings for digital signing and encryption
|
||||||
/// auto verify signed e-mail according to RFC3156
|
#[derive(Debug, Deserialize, Clone, Serialize)]
|
||||||
#[serde(default = "true_val")]
|
pub struct PGPSettings {
|
||||||
pub auto_verify_signatures: bool,
|
/// auto verify signed e-mail according to RFC3156
|
||||||
|
#[serde(default = "true_val", alias = "auto-verify-signatures")]
|
||||||
|
auto_verify_signatures: bool,
|
||||||
|
|
||||||
/// always sign sent messages
|
/// always sign sent messages
|
||||||
#[serde(default = "false_val")]
|
#[serde(default = "false_val", alias = "auto-sign")]
|
||||||
pub auto_sign: bool,
|
auto_sign: bool,
|
||||||
|
|
||||||
// https://tools.ietf.org/html/rfc4880#section-12.2
|
// https://tools.ietf.org/html/rfc4880#section-12.2
|
||||||
#[serde(default = "none")]
|
#[serde(default = "none")]
|
||||||
pub key: Option<String>,
|
key: Option<String>,
|
||||||
|
|
||||||
/// gpg binary name or file location to use
|
/// gpg binary name or file location to use
|
||||||
#[serde(default)]
|
#[serde(default, alias = "gpg-binary")]
|
||||||
pub gpg_binary: Option<String>,
|
gpg_binary: Option<String>,
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
impl Default for PGPSettings {
|
impl Default for PGPSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use crate::override_def;
|
||||||
use crate::terminal::Key;
|
use crate::terminal::Key;
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
|
|
||||||
|
@ -32,24 +33,42 @@ macro_rules! shortcut {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
override_def!(
|
||||||
pub struct Shortcuts {
|
ShortcutsOverride,
|
||||||
#[serde(default)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub general: GeneralShortcuts,
|
pub struct Shortcuts {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub listing: ListingShortcuts,
|
general: GeneralShortcuts,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub composing: ComposingShortcuts,
|
listing: ListingShortcuts,
|
||||||
#[serde(default, alias = "compact-listing")]
|
#[serde(default)]
|
||||||
pub compact_listing: CompactListingShortcuts,
|
composing: ComposingShortcuts,
|
||||||
#[serde(default, alias = "contact-list")]
|
#[serde(default, alias = "compact-listing")]
|
||||||
pub contact_list: ContactListShortcuts,
|
compact_listing: CompactListingShortcuts,
|
||||||
#[serde(default, alias = "envelope-view")]
|
#[serde(default, alias = "contact-list")]
|
||||||
pub envelope_view: EnvelopeViewShortcuts,
|
contact_list: ContactListShortcuts,
|
||||||
#[serde(default, alias = "thread-view")]
|
#[serde(default, alias = "envelope-view")]
|
||||||
pub thread_view: ThreadViewShortcuts,
|
envelope_view: EnvelopeViewShortcuts,
|
||||||
#[serde(default)]
|
#[serde(default, alias = "thread-view")]
|
||||||
pub pager: PagerShortcuts,
|
thread_view: ThreadViewShortcuts,
|
||||||
|
#[serde(default)]
|
||||||
|
pager: PagerShortcuts,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
impl Default for Shortcuts {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
general: GeneralShortcuts::default(),
|
||||||
|
listing: ListingShortcuts::default(),
|
||||||
|
composing: ComposingShortcuts::default(),
|
||||||
|
compact_listing: CompactListingShortcuts::default(),
|
||||||
|
contact_list: ContactListShortcuts::default(),
|
||||||
|
envelope_view: EnvelopeViewShortcuts::default(),
|
||||||
|
thread_view: ThreadViewShortcuts::default(),
|
||||||
|
pager: PagerShortcuts::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a struct holding all of a Component's shortcuts.
|
/// Create a struct holding all of a Component's shortcuts.
|
||||||
|
|
|
@ -21,18 +21,22 @@
|
||||||
|
|
||||||
//! E-mail tag configuration and {de,}serializing.
|
//! E-mail tag configuration and {de,}serializing.
|
||||||
|
|
||||||
|
use crate::override_def;
|
||||||
use crate::terminal::Color;
|
use crate::terminal::Color;
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
use std::collections::{hash_map::DefaultHasher, HashMap, HashSet};
|
use std::collections::{hash_map::DefaultHasher, HashMap, HashSet};
|
||||||
use std::hash::Hasher;
|
use std::hash::Hasher;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, Serialize)]
|
override_def!(
|
||||||
pub struct TagsSettings {
|
TagsSettingsOverride,
|
||||||
#[serde(default, deserialize_with = "tag_color_de")]
|
#[derive(Debug, Deserialize, Clone, Serialize)]
|
||||||
pub colors: HashMap<u64, Color>,
|
pub struct TagsSettings {
|
||||||
#[serde(default, deserialize_with = "tag_set_de")]
|
#[serde(default, deserialize_with = "tag_color_de")]
|
||||||
pub ignore_tags: HashSet<u64>,
|
colors: HashMap<u64, Color>,
|
||||||
}
|
#[serde(default, deserialize_with = "tag_set_de", alias = "ignore-tags")]
|
||||||
|
ignore_tags: HashSet<u64>,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
impl Default for TagsSettings {
|
impl Default for TagsSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
@ -43,7 +47,9 @@ impl Default for TagsSettings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tag_set_de<'de, D>(deserializer: D) -> std::result::Result<HashSet<u64>, D::Error>
|
pub fn tag_set_de<'de, D, T: std::convert::From<HashSet<u64>>>(
|
||||||
|
deserializer: D,
|
||||||
|
) -> std::result::Result<T, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
|
@ -54,10 +60,13 @@ where
|
||||||
hasher.write(tag.as_bytes());
|
hasher.write(tag.as_bytes());
|
||||||
hasher.finish()
|
hasher.finish()
|
||||||
})
|
})
|
||||||
.collect())
|
.collect::<HashSet<u64>>()
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tag_color_de<'de, D>(deserializer: D) -> std::result::Result<HashMap<u64, Color>, D::Error>
|
pub fn tag_color_de<'de, D, T: std::convert::From<HashMap<u64, Color>>>(
|
||||||
|
deserializer: D,
|
||||||
|
) -> std::result::Result<T, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
|
@ -81,5 +90,6 @@ where
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect())
|
.collect::<HashMap<u64, Color>>()
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue