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.
async
Manos Pitsidianakis 2020-07-17 13:12:57 +03:00
parent c6c2865a54
commit 996abd323f
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
8 changed files with 212 additions and 7 deletions

View File

@ -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<String, AccountConf>,
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<String> {
if !path.is_empty() {
Err(MeliError::new(format!(
"{} has no fields, it is of type {}",
parent_field,
std::any::type_name::<Self>()
)))
} 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<T: DotAddressable> DotAddressable for Option<T> {}
impl<K: DotAddressable + std::cmp::Eq + std::hash::Hash, V: DotAddressable> DotAddressable
for HashMap<K, V>
{
}
impl<K: DotAddressable + std::cmp::Eq + std::hash::Hash> DotAddressable for HashSet<K> {}
impl DotAddressable for LogSettings {
fn lookup(&self, parent_field: &str, path: &[&str]) -> Result<String> {
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<String> {
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())?),
}
}
}

View File

@ -19,8 +19,9 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
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<String> {
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())?),
}
}
}

View File

@ -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<String> {
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())?),
}
}
}

View File

@ -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::<HashMap<u64, Color>>()
.into())
}
impl DotAddressable for TagsSettings {
fn lookup(&self, parent_field: &str, path: &[&str]) -> Result<String> {
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())?),
}
}
}

View File

@ -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<String> {
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())?),
}
}
}

View File

@ -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> {

View File

@ -91,6 +91,7 @@ pub enum ComposeAction {
#[derive(Debug)]
pub enum AccountAction {
ReIndex,
PrintSetting(String),
}
#[derive(Debug)]

View File

@ -909,6 +909,18 @@ impl State {
Some(NotificationType::ERROR),
));
}
AccountAction(ref _account_name, PrintSetting(ref setting)) => {
let path = setting.split(".").collect::<SmallVec<[&str; 16]>>();
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));
}