From 996abd323f5fa00c79f05c5e618ea79797b1644c Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Fri, 17 Jul 2020 13:12:57 +0300 Subject: [PATCH] Add print setting action Add experimental print setting action. The command is of the form: print account_name listing.index_style account_name is currently ignored. The path, e.g. listing.index_style is split by "." and fed to DotAddressable lookup trait method. The method checks the first segment in the path if it matches any of the struct's fields, and then calls the field's lookup method. --- src/conf.rs | 85 +++++++++++++++++++++++++++++++++++++++++- src/conf/listing.rs | 25 ++++++++++++- src/conf/pager.rs | 32 +++++++++++++++- src/conf/tags.rs | 21 +++++++++++ src/conf/terminal.rs | 25 ++++++++++++- src/execute.rs | 18 ++++++++- src/execute/actions.rs | 1 + src/state.rs | 12 ++++++ 8 files changed, 212 insertions(+), 7 deletions(-) diff --git a/src/conf.rs b/src/conf.rs index 8e149a99c..21d316aa2 100644 --- a/src/conf.rs +++ b/src/conf.rs @@ -249,7 +249,7 @@ pub struct FileSettings { pub log: LogSettings, } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, Serialize)] pub struct AccountConf { pub(crate) account: AccountSettings, pub(crate) conf: FileAccount, @@ -272,7 +272,7 @@ impl AccountConf { } } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, Serialize)] pub struct Settings { pub accounts: HashMap, pub pager: PagerSettings, @@ -827,3 +827,84 @@ pub struct LogSettings { #[serde(default)] maximum_level: melib::LoggingLevel, } + +pub trait DotAddressable: Serialize { + fn lookup(&self, parent_field: &str, path: &[&str]) -> Result { + if !path.is_empty() { + Err(MeliError::new(format!( + "{} has no fields, it is of type {}", + parent_field, + std::any::type_name::() + ))) + } else { + Ok(toml::to_string(self).map_err(|err| err.to_string())?) + } + } +} + +impl DotAddressable for bool {} + +impl DotAddressable for String {} +impl DotAddressable for IndexStyle {} +impl DotAddressable for u64 {} +impl DotAddressable for crate::terminal::Color {} +impl DotAddressable for crate::terminal::Attr {} +impl DotAddressable for usize {} +impl DotAddressable for Query {} +impl DotAddressable for melib::LoggingLevel {} +impl DotAddressable for PathBuf {} +impl DotAddressable for ToggleFlag {} +impl DotAddressable for Option {} +impl DotAddressable + for HashMap +{ +} +impl DotAddressable for HashSet {} + +impl DotAddressable for LogSettings { + fn lookup(&self, parent_field: &str, path: &[&str]) -> Result { + match path.first() { + Some(field) => { + let tail = &path[1..]; + match *field { + "log_file" => self.log_file.lookup(field, tail), + "maximum_level" => self.maximum_level.lookup(field, tail), + + other => Err(MeliError::new(format!( + "{} has no field named {}", + parent_field, other + ))), + } + } + None => Ok(toml::to_string(self).map_err(|err| err.to_string())?), + } + } +} +impl DotAddressable for Settings { + fn lookup(&self, parent_field: &str, path: &[&str]) -> Result { + match path.first() { + Some(field) => { + let tail = &path[1..]; + match *field { + "accounts" => Err(MeliError::new("unimplemented")), + "pager" => self.pager.lookup(field, tail), + "listing" => self.listing.lookup(field, tail), + "notifications" => Err(MeliError::new("unimplemented")), + "shortcuts" => Err(MeliError::new("unimplemented")), + "tags" => Err(MeliError::new("unimplemented")), + "composing" => Err(MeliError::new("unimplemented")), + "pgp" => Err(MeliError::new("unimplemented")), + "terminal" => self.terminal.lookup(field, tail), + "plugins" => Err(MeliError::new("unimplemented")), + "log" => self.log.lookup(field, tail), + + other => Err(MeliError::new(format!( + "{} has no field named {}", + parent_field, other + ))), + } + } + None => Ok(toml::to_string(self).map_err(|err| err.to_string())?), + } + } +} diff --git a/src/conf/listing.rs b/src/conf/listing.rs index d67a7291f..2904e5c74 100644 --- a/src/conf/listing.rs +++ b/src/conf/listing.rs @@ -19,8 +19,9 @@ * along with meli. If not, see . */ -use super::{default_vals::*, IndexStyle}; +use super::{default_vals::*, DotAddressable, IndexStyle}; use melib::search::Query; +use melib::{MeliError, Result}; /// Settings for mail listings #[derive(Debug, Deserialize, Clone, Serialize)] @@ -60,3 +61,25 @@ impl Default for ListingSettings { } } } + +impl DotAddressable for ListingSettings { + fn lookup(&self, parent_field: &str, path: &[&str]) -> Result { + match path.first() { + Some(field) => { + let tail = &path[1..]; + match *field { + "context_lines" => self.context_lines.lookup(field, tail), + "datetime_fmt" => self.datetime_fmt.lookup(field, tail), + "recent_dates" => self.recent_dates.lookup(field, tail), + "filter" => self.filter.lookup(field, tail), + "index_style" => self.index_style.lookup(field, tail), + other => Err(MeliError::new(format!( + "{} has no field named {}", + parent_field, other + ))), + } + } + None => Ok(toml::to_string(self).map_err(|err| err.to_string())?), + } + } +} diff --git a/src/conf/pager.rs b/src/conf/pager.rs index 5365423a2..226bc3176 100644 --- a/src/conf/pager.rs +++ b/src/conf/pager.rs @@ -23,7 +23,8 @@ use super::default_vals::*; use super::deserializers::*; -use melib::ToggleFlag; +use super::DotAddressable; +use melib::{MeliError, Result, ToggleFlag}; /// Settings for the pager function. #[derive(Debug, Deserialize, Clone, Serialize)] @@ -103,3 +104,32 @@ impl Default for PagerSettings { } } } + +impl DotAddressable for PagerSettings { + fn lookup(&self, parent_field: &str, path: &[&str]) -> Result { + match path.first() { + Some(field) => { + let tail = &path[1..]; + match *field { + "pager_context" => self.pager_context.lookup(field, tail), + "pager_stop" => self.pager_stop.lookup(field, tail), + "headers_sticky" => self.headers_sticky.lookup(field, tail), + "pager_ratio" => self.pager_ratio.lookup(field, tail), + "filter" => self.filter.lookup(field, tail), + "html_filter" => self.html_filter.lookup(field, tail), + "format_flowed" => self.format_flowed.lookup(field, tail), + "split_long_lines" => self.split_long_lines.lookup(field, tail), + "minimum_width" => self.minimum_width.lookup(field, tail), + "auto_choose_multipart_alternative" => { + self.auto_choose_multipart_alternative.lookup(field, tail) + } + other => Err(MeliError::new(format!( + "{} has no field named {}", + parent_field, other + ))), + } + } + None => Ok(toml::to_string(self).map_err(|err| err.to_string())?), + } + } +} diff --git a/src/conf/tags.rs b/src/conf/tags.rs index a9d2bc6a1..cee7a55fe 100644 --- a/src/conf/tags.rs +++ b/src/conf/tags.rs @@ -21,7 +21,9 @@ //! E-mail tag configuration and {de,}serializing. +use super::DotAddressable; use crate::terminal::Color; +use melib::{MeliError, Result}; use serde::{Deserialize, Deserializer}; use std::collections::{hash_map::DefaultHasher, HashMap, HashSet}; use std::hash::Hasher; @@ -89,3 +91,22 @@ where .collect::>() .into()) } + +impl DotAddressable for TagsSettings { + fn lookup(&self, parent_field: &str, path: &[&str]) -> Result { + match path.first() { + Some(field) => { + let tail = &path[1..]; + match *field { + "colors" => self.colors.lookup(field, tail), + "ignore_tags" => self.ignore_tags.lookup(field, tail), + other => Err(MeliError::new(format!( + "{} has no field named {}", + parent_field, other + ))), + } + } + None => Ok(toml::to_string(self).map_err(|err| err.to_string())?), + } + } +} diff --git a/src/conf/terminal.rs b/src/conf/terminal.rs index ef6ee7149..df3a4f830 100644 --- a/src/conf/terminal.rs +++ b/src/conf/terminal.rs @@ -22,8 +22,9 @@ //! Settings for terminal display use super::deserializers::non_empty_string; +use super::DotAddressable; use super::Themes; -use super::ToggleFlag; +use melib::{MeliError, Result, ToggleFlag}; /// Settings for terminal display #[derive(Debug, Deserialize, Clone, Serialize)] @@ -61,3 +62,25 @@ impl TerminalSettings { || (self.use_color.is_false() && !self.use_color.is_internal())) } } + +impl DotAddressable for TerminalSettings { + fn lookup(&self, parent_field: &str, path: &[&str]) -> Result { + match path.first() { + Some(field) => { + let tail = &path[1..]; + match *field { + "theme" => self.theme.lookup(field, tail), + "themes" => Err(MeliError::new("unimplemented")), + "ascii_drawing" => self.ascii_drawing.lookup(field, tail), + "use_color" => self.use_color.lookup(field, tail), + "window_title" => self.window_title.lookup(field, tail), + other => Err(MeliError::new(format!( + "{} has no field named {}", + parent_field, other + ))), + } + } + None => Ok(toml::to_string(self).map_err(|err| err.to_string())?), + } + } +} diff --git a/src/execute.rs b/src/execute.rs index 810701a91..0da50f6ff 100644 --- a/src/execute.rs +++ b/src/execute.rs @@ -646,7 +646,21 @@ define_commands!([ )(input.trim()) } ) - } + }, + { tags: ["print "], + desc: "print ACCOUNT SETTING", + tokens: &[One(Literal("print")), One(AccountName), One(QuotedStringValue)], + parser:( + fn print_setting(input: &[u8]) -> IResult<&[u8], Action> { + let (input, _) = tag("print")(input.trim())?; + let (input, _) = is_a(" ")(input)?; + let (input, account) = quoted_argument(input)?; + let (input, _) = is_a(" ")(input)?; + let (input, setting) = quoted_argument(input)?; + Ok((input, AccountAction(account.to_string(), PrintSetting(setting.to_string())))) + } + ) + } ]); fn usize_c(input: &[u8]) -> IResult<&[u8], usize> { @@ -705,7 +719,7 @@ fn compose_action(input: &[u8]) -> IResult<&[u8], Action> { } fn account_action(input: &[u8]) -> IResult<&[u8], Action> { - reindex(input) + alt((reindex, print_setting))(input) } fn view(input: &[u8]) -> IResult<&[u8], Action> { diff --git a/src/execute/actions.rs b/src/execute/actions.rs index a95fc2c38..4bc6446c8 100644 --- a/src/execute/actions.rs +++ b/src/execute/actions.rs @@ -91,6 +91,7 @@ pub enum ComposeAction { #[derive(Debug)] pub enum AccountAction { ReIndex, + PrintSetting(String), } #[derive(Debug)] diff --git a/src/state.rs b/src/state.rs index b3cb3173d..2c8a4a9e2 100644 --- a/src/state.rs +++ b/src/state.rs @@ -909,6 +909,18 @@ impl State { Some(NotificationType::ERROR), )); } + AccountAction(ref _account_name, PrintSetting(ref setting)) => { + let path = setting.split(".").collect::>(); + self.context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(format!( + "{}", + self.context + .settings + .lookup("settings", &path) + .unwrap_or_else(|err| err.to_string()) + )))); + } v => { self.rcv_event(UIEvent::Action(v)); }