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