Fix all clippy warnings in `meli` crate

pull/144/head
Manos Pitsidianakis 2022-08-25 15:17:18 +03:00
parent d921b3c320
commit 9cb66ef818
39 changed files with 688 additions and 881 deletions

View File

@ -50,7 +50,9 @@ pub fn override_derive(filenames: &[(&str, &str)]) {
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
//! This module is automatically generated by build.rs. #![allow(clippy::derivable_impls)]
//! This module is automatically generated by config_macros.rs.
use super::*; use super::*;
"## "##

View File

@ -42,6 +42,8 @@ extern crate serde_json;
extern crate smallvec; extern crate smallvec;
extern crate termion; extern crate termion;
use structopt::StructOpt;
#[global_allocator] #[global_allocator]
static GLOBAL: System = System; static GLOBAL: System = System;
@ -120,6 +122,7 @@ fn notify(
Ok(r) Ok(r)
} }
#[cfg(feature = "cli-docs")]
fn parse_manpage(src: &str) -> Result<ManPages> { fn parse_manpage(src: &str) -> Result<ManPages> {
match src { match src {
"" | "meli" | "main" => Ok(ManPages::Main), "" | "meli" | "main" => Ok(ManPages::Main),
@ -132,8 +135,7 @@ fn parse_manpage(src: &str) -> Result<ManPages> {
} }
} }
use structopt::StructOpt; #[cfg(feature = "cli-docs")]
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
/// Choose manpage /// Choose manpage
enum ManPages { enum ManPages {
@ -193,9 +195,11 @@ enum SubCommand {
#[derive(Debug, StructOpt)] #[derive(Debug, StructOpt)]
struct ManOpt { struct ManOpt {
#[structopt(default_value = "meli", possible_values=&["meli", "conf", "themes"], value_name="PAGE", parse(try_from_str = parse_manpage))] #[structopt(default_value = "meli", possible_values=&["meli", "conf", "themes"], value_name="PAGE", parse(try_from_str = parse_manpage))]
#[cfg(feature = "cli-docs")]
page: ManPages, page: ManPages,
/// If true, output text in stdout instead of spawning $PAGER. /// If true, output text in stdout instead of spawning $PAGER.
#[structopt(long = "no-raw", alias = "no-raw", value_name = "bool")] #[structopt(long = "no-raw", alias = "no-raw", value_name = "bool")]
#[cfg(feature = "cli-docs")]
no_raw: Option<Option<bool>>, no_raw: Option<Option<bool>>,
} }
@ -243,7 +247,7 @@ fn run_app(opt: Opt) -> Result<()> {
#[cfg(feature = "cli-docs")] #[cfg(feature = "cli-docs")]
Some(SubCommand::Man(manopt)) => { Some(SubCommand::Man(manopt)) => {
let ManOpt { page, no_raw } = manopt; let ManOpt { page, no_raw } = manopt;
const MANPAGES: [&'static [u8]; 3] = [ const MANPAGES: [&[u8]; 3] = [
include_bytes!(concat!(env!("OUT_DIR"), "/meli.txt.gz")), include_bytes!(concat!(env!("OUT_DIR"), "/meli.txt.gz")),
include_bytes!(concat!(env!("OUT_DIR"), "/meli.conf.txt.gz")), include_bytes!(concat!(env!("OUT_DIR"), "/meli.conf.txt.gz")),
include_bytes!(concat!(env!("OUT_DIR"), "/meli-themes.txt.gz")), include_bytes!(concat!(env!("OUT_DIR"), "/meli-themes.txt.gz")),
@ -255,10 +259,9 @@ fn run_app(opt: Opt) -> Result<()> {
str::parse::<usize>(unsafe { str::parse::<usize>(unsafe {
std::str::from_utf8_unchecked(gz.header().unwrap().comment().unwrap()) std::str::from_utf8_unchecked(gz.header().unwrap().comment().unwrap())
}) })
.expect(&format!( .unwrap_or_else(|_| {
"{:?} was not compressed with size comment header", panic!("{:?} was not compressed with size comment header", page)
page }),
)),
); );
gz.read_to_string(&mut v)?; gz.read_to_string(&mut v)?;
@ -271,19 +274,18 @@ fn run_app(opt: Opt) -> Result<()> {
return Ok(()); return Ok(());
} }
} }
} else { } else if unsafe { libc::isatty(libc::STDOUT_FILENO) != 1 } {
if unsafe { libc::isatty(libc::STDOUT_FILENO) != 1 } { println!("{}", &v);
println!("{}", &v); return Ok(());
return Ok(());
}
} }
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
let mut handle = Command::new(std::env::var("PAGER").unwrap_or("more".to_string())) let mut handle =
.stdin(Stdio::piped()) Command::new(std::env::var("PAGER").unwrap_or_else(|_| "more".to_string()))
.stdout(Stdio::inherit()) .stdin(Stdio::piped())
.stderr(Stdio::inherit()) .stdout(Stdio::inherit())
.spawn()?; .stderr(Stdio::inherit())
.spawn()?;
handle.stdin.take().unwrap().write_all(v.as_bytes())?; handle.stdin.take().unwrap().write_all(v.as_bytes())?;
handle.wait()?; handle.wait()?;
@ -314,7 +316,7 @@ fn run_app(opt: Opt) -> Result<()> {
} }
Some(SubCommand::PrintLoadedThemes) => { Some(SubCommand::PrintLoadedThemes) => {
let s = conf::FileSettings::new()?; let s = conf::FileSettings::new()?;
print!("{}", s.terminal.themes.to_string()); print!("{}", s.terminal.themes);
return Ok(()); return Ok(());
} }
Some(SubCommand::PrintDefaultTheme) => { Some(SubCommand::PrintDefaultTheme) => {

View File

@ -116,7 +116,7 @@ impl TokenStream {
} }
*s = &s[ptr..]; *s = &s[ptr..];
//println!("\t before s.is_empty() {:?} {:?}", t, s); //println!("\t before s.is_empty() {:?} {:?}", t, s);
if s.is_empty() || &*s == &" " { if s.is_empty() || *s == " " {
match t.inner() { match t.inner() {
Literal(lit) => { Literal(lit) => {
sugg.insert(format!("{}{}", if s.is_empty() { " " } else { "" }, lit)); sugg.insert(format!("{}{}", if s.is_empty() { " " } else { "" }, lit));
@ -126,7 +126,7 @@ impl TokenStream {
//println!("adding empty suggestions for {:?}", t); //println!("adding empty suggestions for {:?}", t);
let mut _s = *s; let mut _s = *s;
let mut m = t.matches(&mut _s, sugg); let mut m = t.matches(&mut _s, sugg);
tokens.extend(m.drain(..)); tokens.append(&mut m);
} }
} }
Seq(_s) => {} Seq(_s) => {}
@ -167,7 +167,7 @@ impl TokenStream {
let mut _s = *s; let mut _s = *s;
let mut m = t.matches(&mut _s, sugg); let mut m = t.matches(&mut _s, sugg);
if !m.is_empty() { if !m.is_empty() {
tokens.extend(m.drain(..)); tokens.append(&mut m);
//println!("_s is empty {}", _s.is_empty()); //println!("_s is empty {}", _s.is_empty());
cont = !_s.is_empty(); cont = !_s.is_empty();
*s = _s; *s = _s;
@ -262,7 +262,7 @@ define_commands!([
desc: "set [seen/unseen], toggles message's Seen flag.", desc: "set [seen/unseen], toggles message's Seen flag.",
tokens: &[One(Literal("set")), One(Alternatives(&[to_stream!(One(Literal("seen"))), to_stream!(One(Literal("unseen")))]))], tokens: &[One(Literal("set")), One(Alternatives(&[to_stream!(One(Literal("seen"))), to_stream!(One(Literal("unseen")))]))],
parser: ( parser: (
fn seen_flag<'a>(input: &'a [u8]) -> IResult<&'a [u8], Action> { fn seen_flag(input: &'_ [u8]) -> IResult<&'_ [u8], Action> {
let (input, _) = tag("set")(input.trim())?; let (input, _) = tag("set")(input.trim())?;
let (input, _) = is_a(" ")(input)?; let (input, _) = is_a(" ")(input)?;
let (input, ret) = alt((map(tag("seen"), |_| Listing(SetSeen)), map(tag("unseen"), |_| Listing(SetUnseen))))(input)?; let (input, ret) = alt((map(tag("seen"), |_| Listing(SetSeen)), map(tag("unseen"), |_| Listing(SetUnseen))))(input)?;
@ -275,7 +275,7 @@ define_commands!([
desc: "delete message", desc: "delete message",
tokens: &[One(Literal("delete"))], tokens: &[One(Literal("delete"))],
parser: ( parser: (
fn delete_message<'a>(input: &'a [u8]) -> IResult<&'a [u8], Action> { fn delete_message(input: &'_ [u8]) -> IResult<&'_ [u8], Action> {
let (input, ret) = map(preceded(tag("delete"), eof), |_| Listing(Delete))(input)?; let (input, ret) = map(preceded(tag("delete"), eof), |_| Listing(Delete))(input)?;
let (input, _) = eof(input)?; let (input, _) = eof(input)?;
Ok((input, ret)) Ok((input, ret))
@ -536,7 +536,7 @@ define_commands!([
desc: "filter EXECUTABLE ARGS", desc: "filter EXECUTABLE ARGS",
tokens: &[One(Literal("filter")), One(Filepath), ZeroOrMore(QuotedStringValue)], tokens: &[One(Literal("filter")), One(Filepath), ZeroOrMore(QuotedStringValue)],
parser:( parser:(
fn filter<'a>(input: &'a [u8]) -> IResult<&'a [u8], Action> { fn filter(input: &'_ [u8]) -> IResult<&'_ [u8], Action> {
let (input, _) = tag("filter")(input.trim())?; let (input, _) = tag("filter")(input.trim())?;
let (input, _) = is_a(" ")(input)?; let (input, _) = is_a(" ")(input)?;
let (input, cmd) = map_res(not_line_ending, std::str::from_utf8)(input)?; let (input, cmd) = map_res(not_line_ending, std::str::from_utf8)(input)?;
@ -1080,7 +1080,7 @@ pub fn command_completion_suggestions(input: &str) -> Vec<String> {
} }
if let Some((s, Filepath)) = _m.last() { if let Some((s, Filepath)) = _m.last() {
let p = std::path::Path::new(s); let p = std::path::Path::new(s);
sugg.extend(p.complete(true).into_iter().map(|m| m.into())); sugg.extend(p.complete(true).into_iter());
} }
} }
sugg.into_iter() sugg.into_iter()

View File

@ -29,7 +29,7 @@ pub use self::contact_list::*;
#[derive(Debug)] #[derive(Debug)]
enum ViewMode { enum ViewMode {
ReadOnly, ReadOnly,
Discard(UIDialog<char>), Discard(Box<UIDialog<char>>),
Edit, Edit,
//New, //New,
} }
@ -113,27 +113,21 @@ impl ContactManager {
self.form = FormWidget::new(("Save".into(), true)); self.form = FormWidget::new(("Save".into(), true));
self.form.add_button(("Cancel(Esc)".into(), false)); self.form.add_button(("Cancel(Esc)".into(), false));
self.form self.form
.push(("NAME".into(), self.card.name().to_string().into())); .push(("NAME".into(), self.card.name().to_string()));
self.form.push(( self.form.push((
"ADDITIONAL NAME".into(), "ADDITIONAL NAME".into(),
self.card.additionalname().to_string().into(), self.card.additionalname().to_string(),
));
self.form.push((
"NAME PREFIX".into(),
self.card.name_prefix().to_string().into(),
));
self.form.push((
"NAME SUFFIX".into(),
self.card.name_suffix().to_string().into(),
)); ));
self.form self.form
.push(("E-MAIL".into(), self.card.email().to_string().into())); .push(("NAME PREFIX".into(), self.card.name_prefix().to_string()));
self.form self.form
.push(("URL".into(), self.card.url().to_string().into())); .push(("NAME SUFFIX".into(), self.card.name_suffix().to_string()));
self.form self.form
.push(("KEY".into(), self.card.key().to_string().into())); .push(("E-MAIL".into(), self.card.email().to_string()));
self.form.push(("URL".into(), self.card.url().to_string()));
self.form.push(("KEY".into(), self.card.key().to_string()));
for (k, v) in self.card.extra_properties() { for (k, v) in self.card.extra_properties() {
self.form.push((k.to_string().into(), v.to_string().into())); self.form.push((k.to_string().into(), v.to_string()));
} }
} }
@ -168,26 +162,20 @@ impl Component for ContactManager {
(set_y(upper_left, get_y(upper_left) + 2), bottom_right), (set_y(upper_left, get_y(upper_left) + 2), bottom_right),
context, context,
); );
match self.mode { if let ViewMode::Discard(ref mut selector) = self.mode {
ViewMode::Discard(ref mut selector) => { /* Let user choose whether to quit with/without saving or cancel */
/* Let user choose whether to quit with/without saving or cancel */ selector.draw(grid, area, context);
selector.draw(grid, area, context);
}
_ => {}
} }
context.dirty_areas.push_back(area); context.dirty_areas.push_back(area);
} }
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool { fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
match event { if let UIEvent::ConfigReload { old_settings: _ } = event {
UIEvent::ConfigReload { old_settings: _ } => { self.theme_default = crate::conf::value(context, "theme_default");
self.theme_default = crate::conf::value(context, "theme_default"); self.content = CellBuffer::new_with_context(100, 1, None, context);
self.content = CellBuffer::new_with_context(100, 1, None, context); self.initialized = false;
self.initialized = false; self.set_dirty(true);
self.set_dirty(true);
}
_ => {}
} }
match self.mode { match self.mode {
ViewMode::Discard(ref mut selector) => { ViewMode::Discard(ref mut selector) => {
@ -209,9 +197,7 @@ impl Component for ContactManager {
match self.form.buttons_result() { match self.form.buttons_result() {
None => {} None => {}
Some(true) => { Some(true) => {
let fields = std::mem::replace(&mut self.form, FormWidget::default()) let fields = std::mem::take(&mut self.form).collect().unwrap();
.collect()
.unwrap();
let fields: HashMap<String, String> = fields let fields: HashMap<String, String> = fields
.into_iter() .into_iter()
.map(|(s, v)| { .map(|(s, v)| {
@ -290,7 +276,7 @@ impl Component for ContactManager {
let parent_id = self.parent_id; let parent_id = self.parent_id;
/* Play it safe and ask user for confirmation */ /* Play it safe and ask user for confirmation */
self.mode = ViewMode::Discard(UIDialog::new( self.mode = ViewMode::Discard(Box::new(UIDialog::new(
"this contact has unsaved changes", "this contact has unsaved changes",
vec![ vec![
('x', "quit without saving".to_string()), ('x', "quit without saving".to_string()),
@ -305,7 +291,7 @@ impl Component for ContactManager {
_ => None, _ => None,
})), })),
context, context,
)); )));
self.set_dirty(true); self.set_dirty(true);
false false
} }

View File

@ -34,7 +34,7 @@ enum ViewMode {
#[derive(Debug)] #[derive(Debug)]
struct AccountMenuEntry { struct AccountMenuEntry {
name: String, name: String,
hash: AccountHash, _hash: AccountHash,
// Index in the config account vector. // Index in the config account vector.
index: usize, index: usize,
} }
@ -82,7 +82,7 @@ impl ContactList {
.enumerate() .enumerate()
.map(|(i, (h, a))| AccountMenuEntry { .map(|(i, (h, a))| AccountMenuEntry {
name: a.name().to_string(), name: a.name().to_string(),
hash: *h, _hash: *h,
index: i, index: i,
}) })
.collect(); .collect();
@ -216,7 +216,6 @@ impl ContactList {
((0, 0), (message.len() - 1, 0)), ((0, 0), (message.len() - 1, 0)),
None, None,
); );
return;
} }
} }
@ -241,7 +240,7 @@ impl ContactList {
self.dirty = false; self.dirty = false;
let mut y = get_y(upper_left); let mut y = get_y(upper_left);
for a in &self.accounts { for a in &self.accounts {
self.print_account(grid, (set_y(upper_left, y), bottom_right), &a, context); self.print_account(grid, (set_y(upper_left, y), bottom_right), a, context);
y += 1; y += 1;
} }
@ -393,6 +392,7 @@ impl ContactList {
} }
} }
PageMovement::PageDown(multiplier) => { PageMovement::PageDown(multiplier) => {
#[allow(clippy::comparison_chain)]
if self.new_cursor_pos + rows * multiplier < self.length { if self.new_cursor_pos + rows * multiplier < self.length {
self.new_cursor_pos += rows * multiplier; self.new_cursor_pos += rows * multiplier;
} else if self.new_cursor_pos + rows * multiplier > self.length { } else if self.new_cursor_pos + rows * multiplier > self.length {
@ -782,7 +782,7 @@ impl Component for ContactList {
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); .push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
return true; return true;
} }
UIEvent::Input(Key::Char(c)) if c >= '0' && c <= '9' => { UIEvent::Input(Key::Char(c)) if ('0'..='9').contains(&c) => {
self.cmd_buf.push(c); self.cmd_buf.push(c);
context context
.replies .replies

View File

@ -116,19 +116,11 @@ enum ViewMode {
impl ViewMode { impl ViewMode {
fn is_edit(&self) -> bool { fn is_edit(&self) -> bool {
if let ViewMode::Edit = self { matches!(self, ViewMode::Edit)
true
} else {
false
}
} }
fn is_edit_attachments(&self) -> bool { fn is_edit_attachments(&self) -> bool {
if let ViewMode::EditAttachments { .. } = self { matches!(self, ViewMode::EditAttachments { .. })
true
} else {
false
}
} }
} }
@ -160,7 +152,7 @@ impl Composer {
form: FormWidget::default(), form: FormWidget::default(),
mode: ViewMode::Edit, mode: ViewMode::Edit,
#[cfg(feature = "gpgme")] #[cfg(feature = "gpgme")]
gpg_state: gpg::GpgComposeState::new(), gpg_state: gpg::GpgComposeState::default(),
dirty: true, dirty: true,
has_changes: false, has_changes: false,
embed_area: ((0, 0), (0, 0)), embed_area: ((0, 0), (0, 0)),
@ -249,11 +241,7 @@ impl Composer {
ret.draft ret.draft
.set_header("In-Reply-To", envelope.message_id_display().into()); .set_header("In-Reply-To", envelope.message_id_display().into());
if let Some(reply_to) = envelope if let Some(reply_to) = envelope.other_headers().get("To").map(|v| v.as_str()) {
.other_headers()
.get("To")
.and_then(|v| v.as_str().try_into().ok())
{
let to: &str = reply_to; let to: &str = reply_to;
let extra_identities = &account.settings.account.extra_identities; let extra_identities = &account.settings.account.extra_identities;
if let Some(extra) = extra_identities if let Some(extra) = extra_identities
@ -288,30 +276,26 @@ impl Composer {
.and_then(|v| v.as_str().try_into().ok()) .and_then(|v| v.as_str().try_into().ok())
{ {
to.insert(reply_to); to.insert(reply_to);
} else if let Some(reply_to) = envelope
.other_headers()
.get("Reply-To")
.and_then(|v| v.as_str().try_into().ok())
{
to.insert(reply_to);
} else { } else {
if let Some(reply_to) = envelope to.extend(envelope.from().iter().cloned());
.other_headers()
.get("Reply-To")
.and_then(|v| v.as_str().try_into().ok())
{
to.insert(reply_to);
} else {
to.extend(envelope.from().iter().cloned());
}
} }
to.extend(envelope.to().iter().cloned()); to.extend(envelope.to().iter().cloned());
if let Some(ours) = TryInto::<Address>::try_into( if let Ok(ours) = TryInto::<Address>::try_into(
crate::components::mail::get_display_name(context, coordinates.0).as_str(), crate::components::mail::get_display_name(context, coordinates.0).as_str(),
) ) {
.ok()
{
to.remove(&ours); to.remove(&ours);
} }
ret.draft.set_header("To", { ret.draft.set_header("To", {
let mut ret: String = let mut ret: String =
to.into_iter() to.into_iter()
.fold(String::new(), |mut s: String, n: Address| { .fold(String::new(), |mut s: String, n: Address| {
s.extend(n.to_string().chars()); s.push_str(&n.to_string());
s.push_str(", "); s.push_str(", ");
s s
}); });
@ -320,14 +304,12 @@ impl Composer {
ret ret
}); });
ret.draft.set_header("Cc", envelope.field_cc_to_string()); ret.draft.set_header("Cc", envelope.field_cc_to_string());
} else if let Some(reply_to) = envelope.other_headers().get("Mail-Reply-To") {
ret.draft.set_header("To", reply_to.to_string());
} else if let Some(reply_to) = envelope.other_headers().get("Reply-To") {
ret.draft.set_header("To", reply_to.to_string());
} else { } else {
if let Some(reply_to) = envelope.other_headers().get("Mail-Reply-To") { ret.draft.set_header("To", envelope.field_from_to_string());
ret.draft.set_header("To", reply_to.to_string());
} else if let Some(reply_to) = envelope.other_headers().get("Reply-To") {
ret.draft.set_header("To", reply_to.to_string());
} else {
ret.draft.set_header("To", envelope.field_from_to_string());
}
} }
ret.draft.body = { ret.draft.body = {
let mut ret = attribution_string( let mut ret = attribution_string(
@ -481,7 +463,7 @@ To: {}
let header_values = self.form.values_mut(); let header_values = self.form.values_mut();
let draft_header_map = self.draft.headers_mut(); let draft_header_map = self.draft.headers_mut();
for (k, v) in draft_header_map.iter_mut() { for (k, v) in draft_header_map.iter_mut() {
if let Some(ref vn) = header_values.get(k.as_str()) { if let Some(vn) = header_values.get(k.as_str()) {
*v = vn.as_str().to_string(); *v = vn.as_str().to_string();
} }
} }
@ -498,23 +480,22 @@ To: {}
if k == "To" || k == "Cc" || k == "Bcc" { if k == "To" || k == "Cc" || k == "Bcc" {
self.form.push_cl(( self.form.push_cl((
k.into(), k.into(),
headers[k].to_string().into(), headers[k].to_string(),
Box::new(move |c, term| { Box::new(move |c, term| {
let book: &AddressBook = &c.accounts[&account_hash].address_book; let book: &AddressBook = &c.accounts[&account_hash].address_book;
let results: Vec<String> = book.search(term); let results: Vec<String> = book.search(term);
results results
.into_iter() .into_iter()
.map(|r| AutoCompleteEntry::from(r)) .map(AutoCompleteEntry::from)
.collect::<Vec<AutoCompleteEntry>>() .collect::<Vec<AutoCompleteEntry>>()
}), }),
)); ));
} else if k == "From" { } else if k == "From" {
self.form.push_cl(( self.form.push_cl((
k.into(), k.into(),
headers[k].to_string().into(), headers[k].to_string(),
Box::new(move |c, _term| { Box::new(move |c, _term| {
let results: Vec<(String, String)> = c c.accounts
.accounts
.values() .values()
.map(|acc| { .map(|acc| {
let addr = if let Some(display_name) = let addr = if let Some(display_name) =
@ -548,15 +529,12 @@ To: {}
(addr, desc) (addr, desc)
}) })
.collect::<Vec<_>>(); .map(AutoCompleteEntry::from)
results
.into_iter()
.map(|r| AutoCompleteEntry::from(r))
.collect::<Vec<AutoCompleteEntry>>() .collect::<Vec<AutoCompleteEntry>>()
}), }),
)); ));
} else { } else {
self.form.push((k.into(), headers[k].to_string().into())); self.form.push((k.into(), headers[k].to_string()));
} }
} }
} }
@ -858,7 +836,7 @@ impl Component for Composer {
clear_area(grid, embed_area, theme_default); clear_area(grid, embed_area, theme_default);
copy_area( copy_area(
grid, grid,
&guard.grid.buffer(), guard.grid.buffer(),
embed_area, embed_area,
((0, 0), pos_dec(guard.grid.terminal_size, (1, 1))), ((0, 0), pos_dec(guard.grid.terminal_size, (1, 1))),
); );
@ -871,7 +849,7 @@ impl Component for Composer {
let guard = embed_pty.lock().unwrap(); let guard = embed_pty.lock().unwrap();
copy_area( copy_area(
grid, grid,
&guard.grid.buffer(), guard.grid.buffer(),
embed_area, embed_area,
((0, 0), pos_dec(guard.grid.terminal_size, (1, 1))), ((0, 0), pos_dec(guard.grid.terminal_size, (1, 1))),
); );
@ -1120,8 +1098,7 @@ impl Component for Composer {
UIEvent::FinishedUIDialog(id, ref mut result), UIEvent::FinishedUIDialog(id, ref mut result),
) if selector.id() == *id => { ) if selector.id() == *id => {
if let Some(to_val) = result.downcast_mut::<String>() { if let Some(to_val) = result.downcast_mut::<String>() {
self.draft self.draft.set_header("To", std::mem::take(to_val));
.set_header("To", std::mem::replace(to_val, String::new()));
self.update_form(); self.update_form();
} }
self.mode = ViewMode::Edit; self.mode = ViewMode::Edit;
@ -1677,7 +1654,7 @@ impl Component for Composer {
log( log(
format!( format!(
"Executing: sh -c \"{}\"", "Executing: sh -c \"{}\"",
editor_command.replace("\"", "\\\"") editor_command.replace('"', "\\\"")
), ),
DEBUG, DEBUG,
); );
@ -1804,9 +1781,10 @@ impl Component for Composer {
return true; return true;
} }
Action::Compose(ComposeAction::AddAttachmentFilePicker(ref command)) => { Action::Compose(ComposeAction::AddAttachmentFilePicker(ref command)) => {
let command = if let Some(ref cmd) = command let command = if let Some(cmd) =
.as_ref() command
.or_else(|| context.settings.terminal.file_picker_command.as_ref()) .as_ref()
.or(context.settings.terminal.file_picker_command.as_ref())
{ {
cmd.as_str() cmd.as_str()
} else { } else {
@ -1823,7 +1801,7 @@ impl Component for Composer {
} }
log( log(
format!("Executing: sh -c \"{}\"", command.replace("\"", "\\\"")), format!("Executing: sh -c \"{}\"", command.replace('"', "\\\"")),
DEBUG, DEBUG,
); );
match Command::new("sh") match Command::new("sh")
@ -1838,7 +1816,7 @@ impl Component for Composer {
debug!(&String::from_utf8_lossy(&stderr)); debug!(&String::from_utf8_lossy(&stderr));
for path in stderr.split(|c| [b'\0', b'\t', b'\n'].contains(c)) { for path in stderr.split(|c| [b'\0', b'\t', b'\n'].contains(c)) {
match melib::email::compose::attachment_from_file( match melib::email::compose::attachment_from_file(
&String::from_utf8_lossy(&path).as_ref(), &String::from_utf8_lossy(path).as_ref(),
) { ) {
Ok(a) => { Ok(a) => {
self.draft.attachments_mut().push(a); self.draft.attachments_mut().push(a);
@ -1848,7 +1826,7 @@ impl Component for Composer {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification(
Some(format!( Some(format!(
"could not add attachment: {}", "could not add attachment: {}",
String::from_utf8_lossy(&path) String::from_utf8_lossy(path)
)), )),
err.to_string(), err.to_string(),
Some(NotificationType::Error( Some(NotificationType::Error(
@ -1984,7 +1962,7 @@ impl Component for Composer {
Some(Box::new(move |id: ComponentId, results: &[char]| { Some(Box::new(move |id: ComponentId, results: &[char]| {
Some(UIEvent::FinishedUIDialog( Some(UIEvent::FinishedUIDialog(
id, id,
Box::new(results.get(0).map(|c| *c).unwrap_or('n')), Box::new(results.get(0).copied().unwrap_or('n')),
)) ))
})), })),
context, context,
@ -2033,7 +2011,7 @@ impl Component for Composer {
Some(Box::new(move |id: ComponentId, results: &[char]| { Some(Box::new(move |id: ComponentId, results: &[char]| {
Some(UIEvent::FinishedUIDialog( Some(UIEvent::FinishedUIDialog(
id, id,
Box::new(results.get(0).map(|c| *c).unwrap_or('n')), Box::new(results.get(0).copied().unwrap_or('n')),
)) ))
})), })),
context, context,
@ -2136,7 +2114,7 @@ pub fn send_draft(
let body: AttachmentBuilder = Attachment::new( let body: AttachmentBuilder = Attachment::new(
content_type, content_type,
Default::default(), Default::default(),
std::mem::replace(&mut draft.body, String::new()).into_bytes(), std::mem::take(&mut draft.body).into_bytes(),
) )
.into(); .into();
draft.attachments.insert(0, body); draft.attachments.insert(0, body);
@ -2195,6 +2173,7 @@ pub fn send_draft_async(
let format_flowed = *account_settings!(context[account_hash].composing.format_flowed); let format_flowed = *account_settings!(context[account_hash].composing.format_flowed);
let event_sender = context.sender.clone(); let event_sender = context.sender.clone();
#[cfg(feature = "gpgme")] #[cfg(feature = "gpgme")]
#[allow(clippy::type_complexity)]
let mut filters_stack: Vec< let mut filters_stack: Vec<
Box< Box<
dyn FnOnce( dyn FnOnce(
@ -2207,7 +2186,7 @@ pub fn send_draft_async(
#[cfg(feature = "gpgme")] #[cfg(feature = "gpgme")]
if gpg_state.sign_mail.is_true() && !gpg_state.encrypt_mail.is_true() { if gpg_state.sign_mail.is_true() && !gpg_state.encrypt_mail.is_true() {
filters_stack.push(Box::new(crate::components::mail::pgp::sign_filter( filters_stack.push(Box::new(crate::components::mail::pgp::sign_filter(
gpg_state.sign_keys.clone(), gpg_state.sign_keys,
)?)); )?));
} else if gpg_state.encrypt_mail.is_true() { } else if gpg_state.encrypt_mail.is_true() {
filters_stack.push(Box::new(crate::components::mail::pgp::encrypt_filter( filters_stack.push(Box::new(crate::components::mail::pgp::encrypt_filter(
@ -2216,7 +2195,7 @@ pub fn send_draft_async(
} else { } else {
None None
}, },
gpg_state.encrypt_keys.clone(), gpg_state.encrypt_keys,
)?)); )?));
} }
let send_mail = account_settings!(context[account_hash].composing.send_mail).clone(); let send_mail = account_settings!(context[account_hash].composing.send_mail).clone();
@ -2233,11 +2212,11 @@ pub fn send_draft_async(
let mut body: AttachmentBuilder = Attachment::new( let mut body: AttachmentBuilder = Attachment::new(
content_type, content_type,
Default::default(), Default::default(),
std::mem::replace(&mut draft.body, String::new()).into_bytes(), std::mem::take(&mut draft.body).into_bytes(),
) )
.into(); .into();
if !draft.attachments.is_empty() { if !draft.attachments.is_empty() {
let mut parts = std::mem::replace(&mut draft.attachments, Vec::new()); let mut parts = std::mem::take(&mut draft.attachments);
parts.insert(0, body); parts.insert(0, body);
let boundary = ContentType::make_boundary(&parts); let boundary = ContentType::make_boundary(&parts);
body = Attachment::new( body = Attachment::new(
@ -2261,7 +2240,7 @@ pub fn send_draft_async(
let message = Arc::new(draft.finalise()?); let message = Arc::new(draft.finalise()?);
let ret = send_cb(message.clone()).await; let ret = send_cb(message.clone()).await;
let is_ok = ret.is_ok(); let is_ok = ret.is_ok();
if !is_ok || (store_sent_mail && is_ok) { if !is_ok || store_sent_mail {
event_sender event_sender
.send(ThreadEvent::UIEvent(UIEvent::Callback(CallbackFn( .send(ThreadEvent::UIEvent(UIEvent::Callback(CallbackFn(
Box::new(move |context| { Box::new(move |context| {

View File

@ -37,7 +37,7 @@ impl Default for EditAttachmentCursor {
pub enum EditAttachmentMode { pub enum EditAttachmentMode {
Overview, Overview,
Edit { Edit {
inner: FormWidget<FormButtonActions>, inner: Box<FormWidget<FormButtonActions>>,
no: usize, no: usize,
}, },
} }
@ -68,7 +68,7 @@ impl EditAttachments {
} }
impl EditAttachmentsRefMut<'_, '_> { impl EditAttachmentsRefMut<'_, '_> {
fn new_edit_widget(&self, no: usize) -> Option<FormWidget<FormButtonActions>> { fn new_edit_widget(&self, no: usize) -> Option<Box<FormWidget<FormButtonActions>>> {
if no >= self.draft.attachments().len() { if no >= self.draft.attachments().len() {
return None; return None;
} }
@ -80,7 +80,7 @@ impl EditAttachmentsRefMut<'_, '_> {
ret.add_button(("Cancel".into(), FormButtonActions::Cancel)); ret.add_button(("Cancel".into(), FormButtonActions::Cancel));
ret.push(("Filename".into(), filename.unwrap_or_default().to_string())); ret.push(("Filename".into(), filename.unwrap_or_default().to_string()));
ret.push(("Mime type".into(), mime_type.to_string())); ret.push(("Mime type".into(), mime_type.to_string()));
Some(ret) Some(Box::new(ret))
} }
} }

View File

@ -36,7 +36,7 @@ pub enum KeySelection {
err: MeliError, err: MeliError,
}, },
Loaded { Loaded {
widget: UIDialog<melib::gpgme::Key>, widget: Box<UIDialog<melib::gpgme::Key>>,
keys: Vec<melib::gpgme::Key>, keys: Vec<melib::gpgme::Key>,
}, },
} }
@ -129,7 +129,7 @@ impl Component for KeySelection {
match Self::new( match Self::new(
*secret, *secret,
*local, *local,
std::mem::replace(pattern, String::new()), std::mem::take(pattern),
*allow_remote_lookup, *allow_remote_lookup,
context, context,
) { ) {
@ -166,7 +166,7 @@ impl Component for KeySelection {
} }
return false; return false;
} }
let mut widget = UIDialog::new( let mut widget = Box::new(UIDialog::new(
"select key", "select key",
keys.iter() keys.iter()
.map(|k| { .map(|k| {
@ -185,12 +185,12 @@ impl Component for KeySelection {
move |id: ComponentId, results: &[melib::gpgme::Key]| { move |id: ComponentId, results: &[melib::gpgme::Key]| {
Some(UIEvent::FinishedUIDialog( Some(UIEvent::FinishedUIDialog(
id, id,
Box::new(results.get(0).map(|k| k.clone())), Box::new(results.get(0).cloned()),
)) ))
}, },
)), )),
context, context,
); ));
widget.set_dirty(true); widget.set_dirty(true);
*self = KeySelection::Loaded { widget, keys }; *self = KeySelection::Loaded { widget, keys };
} }
@ -275,8 +275,8 @@ pub struct GpgComposeState {
pub sign_keys: Vec<melib::gpgme::Key>, pub sign_keys: Vec<melib::gpgme::Key>,
} }
impl GpgComposeState { impl Default for GpgComposeState {
pub fn new() -> Self { fn default() -> Self {
GpgComposeState { GpgComposeState {
sign_mail: ToggleFlag::Unset, sign_mail: ToggleFlag::Unset,
encrypt_mail: ToggleFlag::Unset, encrypt_mail: ToggleFlag::Unset,

View File

@ -176,7 +176,6 @@ struct AccountMenuEntry {
name: String, name: String,
hash: AccountHash, hash: AccountHash,
index: usize, index: usize,
visible: bool,
entries: SmallVec<[MailboxMenuEntry; 16]>, entries: SmallVec<[MailboxMenuEntry; 16]>,
} }
@ -380,7 +379,7 @@ pub trait MailListingTrait: ListingTrait {
.map(|&env_hash| account.operation(env_hash).and_then(|mut op| op.as_bytes())) .map(|&env_hash| account.operation(env_hash).and_then(|mut op| op.as_bytes()))
.collect::<Result<Vec<_>>>(); .collect::<Result<Vec<_>>>();
let path_ = path.to_path_buf(); let path_ = path.to_path_buf();
let format = format.clone().unwrap_or_default(); let format = (*format).unwrap_or_default();
let collection = account.collection.clone(); let collection = account.collection.clone();
let (sender, mut receiver) = crate::jobs::oneshot::channel(); let (sender, mut receiver) = crate::jobs::oneshot::channel();
let fut: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>> = let fut: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>> =
@ -521,11 +520,11 @@ pub trait ListingTrait: Component {
#[derive(Debug)] #[derive(Debug)]
pub enum ListingComponent { pub enum ListingComponent {
Plain(PlainListing), Plain(Box<PlainListing>),
Threaded(ThreadListing), Threaded(Box<ThreadListing>),
Compact(CompactListing), Compact(Box<CompactListing>),
Conversations(ConversationsListing), Conversations(Box<ConversationsListing>),
Offline(OfflineListing), Offline(Box<OfflineListing>),
} }
use crate::ListingComponent::*; use crate::ListingComponent::*;
@ -534,11 +533,11 @@ impl core::ops::Deref for ListingComponent {
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
match &self { match &self {
Compact(ref l) => l, Compact(ref l) => l.as_ref(),
Plain(ref l) => l, Plain(ref l) => l.as_ref(),
Threaded(ref l) => l, Threaded(ref l) => l.as_ref(),
Conversations(ref l) => l, Conversations(ref l) => l.as_ref(),
Offline(ref l) => l, Offline(ref l) => l.as_ref(),
} }
} }
} }
@ -546,11 +545,11 @@ impl core::ops::Deref for ListingComponent {
impl core::ops::DerefMut for ListingComponent { impl core::ops::DerefMut for ListingComponent {
fn deref_mut(&mut self) -> &mut (dyn MailListingTrait + 'static) { fn deref_mut(&mut self) -> &mut (dyn MailListingTrait + 'static) {
match self { match self {
Compact(l) => l, Compact(l) => l.as_mut(),
Plain(l) => l, Plain(l) => l.as_mut(),
Threaded(l) => l, Threaded(l) => l.as_mut(),
Conversations(l) => l, Conversations(l) => l.as_mut(),
Offline(l) => l, Offline(l) => l.as_mut(),
} }
} }
} }
@ -611,7 +610,6 @@ pub struct Listing {
accounts: Vec<AccountMenuEntry>, accounts: Vec<AccountMenuEntry>,
status: Option<AccountStatus>, status: Option<AccountStatus>,
dirty: bool, dirty: bool,
visible: bool,
cursor_pos: (usize, MenuEntryCursor), cursor_pos: (usize, MenuEntryCursor),
menu_cursor_pos: (usize, MenuEntryCursor), menu_cursor_pos: (usize, MenuEntryCursor),
menu_content: CellBuffer, menu_content: CellBuffer,
@ -755,12 +753,10 @@ impl Component for Listing {
} }
return true; return true;
} }
UIEvent::StartupCheck(ref f) => { UIEvent::StartupCheck(ref f)
if self.component.coordinates().1 == *f { if self.component.coordinates().1 == *f && !self.startup_checks_rate.tick() =>
if !self.startup_checks_rate.tick() { {
return false; return false;
}
}
} }
UIEvent::Timer(n) if *n == self.startup_checks_rate.id() => { UIEvent::Timer(n) if *n == self.startup_checks_rate.id() => {
if self.startup_checks_rate.active { if self.startup_checks_rate.active {
@ -1127,7 +1123,7 @@ impl Component for Listing {
Action::Listing(ListingAction::Import(file_path, mailbox_path)) => { Action::Listing(ListingAction::Import(file_path, mailbox_path)) => {
let account = &mut context.accounts[self.cursor_pos.0]; let account = &mut context.accounts[self.cursor_pos.0];
if let Err(err) = account if let Err(err) = account
.mailbox_by_path(&mailbox_path) .mailbox_by_path(mailbox_path)
.and_then(|mailbox_hash| { .and_then(|mailbox_hash| {
Ok(( Ok((
std::fs::read(&file_path).chain_err_summary(|| { std::fs::read(&file_path).chain_err_summary(|| {
@ -1687,7 +1683,7 @@ impl Component for Listing {
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); .push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
return true; return true;
} }
UIEvent::Input(Key::Char(c)) if c >= '0' && c <= '9' => { UIEvent::Input(Key::Char(c)) if ('0'..='9').contains(&c) => {
self.cmd_buf.push(c); self.cmd_buf.push(c);
self.component.set_modifier_active(true); self.component.set_modifier_active(true);
context context
@ -1808,7 +1804,6 @@ impl Listing {
name: a.name().to_string(), name: a.name().to_string(),
hash: *h, hash: *h,
index: i, index: i,
visible: true,
entries, entries,
} }
}) })
@ -1818,7 +1813,6 @@ impl Listing {
component: Offline(OfflineListing::new((first_account_hash, 0))), component: Offline(OfflineListing::new((first_account_hash, 0))),
accounts: account_entries, accounts: account_entries,
status: None, status: None,
visible: true,
dirty: true, dirty: true,
cursor_pos: (0, MenuEntryCursor::Mailbox(0)), cursor_pos: (0, MenuEntryCursor::Mailbox(0)),
menu_cursor_pos: (0, MenuEntryCursor::Mailbox(0)), menu_cursor_pos: (0, MenuEntryCursor::Mailbox(0)),
@ -1954,7 +1948,6 @@ impl Listing {
#[derive(Copy, Debug, Clone)] #[derive(Copy, Debug, Clone)]
struct Line { struct Line {
visible: bool,
collapsed: bool, collapsed: bool,
depth: usize, depth: usize,
inc: usize, inc: usize,
@ -1990,7 +1983,7 @@ impl Listing {
indentation, indentation,
has_sibling, has_sibling,
mailbox_hash, mailbox_hash,
visible, visible: _,
collapsed, collapsed,
}, },
) in self.accounts[aidx].entries.iter().enumerate() ) in self.accounts[aidx].entries.iter().enumerate()
@ -1999,7 +1992,6 @@ impl Listing {
match context.accounts[self.accounts[aidx].index][&mailbox_hash].status { match context.accounts[self.accounts[aidx].index][&mailbox_hash].status {
crate::conf::accounts::MailboxStatus::Failed(_) => { crate::conf::accounts::MailboxStatus::Failed(_) => {
lines.push(Line { lines.push(Line {
visible,
collapsed, collapsed,
depth, depth,
inc: i, inc: i,
@ -2012,7 +2004,6 @@ impl Listing {
} }
_ => { _ => {
lines.push(Line { lines.push(Line {
visible,
collapsed, collapsed,
depth, depth,
inc: i, inc: i,

View File

@ -172,9 +172,12 @@ pub struct CompactListing {
/// Cache current view. /// Cache current view.
data_columns: DataColumns, data_columns: DataColumns,
rows_drawn: SegmentTree, rows_drawn: SegmentTree,
#[allow(clippy::type_complexity)]
rows: Vec<((usize, (ThreadHash, EnvelopeHash)), EntryStrings)>, rows: Vec<((usize, (ThreadHash, EnvelopeHash)), EntryStrings)>,
#[allow(clippy::type_complexity)]
search_job: Option<(String, JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>)>, search_job: Option<(String, JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>)>,
#[allow(clippy::type_complexity)]
select_job: Option<(String, JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>)>, select_job: Option<(String, JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>)>,
filter_term: String, filter_term: String,
filtered_selection: Vec<ThreadHash>, filtered_selection: Vec<ThreadHash>,
@ -220,7 +223,7 @@ impl MailListingTrait for CompactListing {
.flatten() .flatten()
.chain(cursor_iter.into_iter().flatten()) .chain(cursor_iter.into_iter().flatten())
.cloned(); .cloned();
SmallVec::from_iter(iter.into_iter()) SmallVec::from_iter(iter)
} }
/// Fill the `self.data_columns` `CellBuffers` with the contents of the account mailbox the user has /// Fill the `self.data_columns` `CellBuffers` with the contents of the account mailbox the user has
@ -324,6 +327,7 @@ impl MailListingTrait for CompactListing {
self.length = 0; self.length = 0;
let mut rows = Vec::with_capacity(1024); let mut rows = Vec::with_capacity(1024);
let mut min_width = (0, 0, 0, 0); let mut min_width = (0, 0, 0, 0);
#[allow(clippy::type_complexity)]
let mut row_widths: ( let mut row_widths: (
SmallVec<[u8; 1024]>, SmallVec<[u8; 1024]>,
SmallVec<[u8; 1024]>, SmallVec<[u8; 1024]>,
@ -404,11 +408,7 @@ impl MailListingTrait for CompactListing {
); );
/* subject */ /* subject */
row_widths.3.push( row_widths.3.push(
(entry_strings (entry_strings.flag.grapheme_width()
.flag
.grapheme_width()
.try_into()
.unwrap_or(255)
+ 1 + 1
+ entry_strings.subject.grapheme_width() + entry_strings.subject.grapheme_width()
+ 1 + 1
@ -876,8 +876,8 @@ impl fmt::Display for CompactListing {
impl CompactListing { impl CompactListing {
pub const DESCRIPTION: &'static str = "compact listing"; pub const DESCRIPTION: &'static str = "compact listing";
pub fn new(coordinates: (AccountHash, MailboxHash)) -> Self { pub fn new(coordinates: (AccountHash, MailboxHash)) -> Box<Self> {
CompactListing { Box::new(CompactListing {
cursor_pos: (coordinates.0, 1, 0), cursor_pos: (coordinates.0, 1, 0),
new_cursor_pos: (coordinates.0, coordinates.1, 0), new_cursor_pos: (coordinates.0, coordinates.1, 0),
length: 0, length: 0,
@ -905,8 +905,9 @@ impl CompactListing {
modifier_active: false, modifier_active: false,
modifier_command: None, modifier_command: None,
id: ComponentId::new_v4(), id: ComponentId::new_v4(),
} })
} }
fn make_entry_string( fn make_entry_string(
&self, &self,
e: &Envelope, e: &Envelope,
@ -1423,7 +1424,7 @@ impl CompactListing {
} }
/* Set fg color for flags */ /* Set fg color for flags */
let mut x = 0; let mut x = 0;
if self.selection.get(&thread_hash).cloned().unwrap_or(false) { if self.selection.get(thread_hash).cloned().unwrap_or(false) {
x += selected_flag_len; x += selected_flag_len;
} }
if thread.snoozed() { if thread.snoozed() {
@ -1887,7 +1888,7 @@ impl Component for CompactListing {
UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => { UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => {
let account = &context.accounts[&self.cursor_pos.0]; let account = &context.accounts[&self.cursor_pos.0];
let threads = account.collection.get_threads(self.cursor_pos.1); let threads = account.collection.get_threads(self.cursor_pos.1);
if !account.collection.contains_key(&new_hash) { if !account.collection.contains_key(new_hash) {
return false; return false;
} }
let new_env_thread_node_hash = account.collection.get_env(*new_hash).thread(); let new_env_thread_node_hash = account.collection.get_env(*new_hash).thread();
@ -1917,7 +1918,7 @@ impl Component for CompactListing {
UIEvent::EnvelopeUpdate(ref env_hash) => { UIEvent::EnvelopeUpdate(ref env_hash) => {
let account = &context.accounts[&self.cursor_pos.0]; let account = &context.accounts[&self.cursor_pos.0];
let threads = account.collection.get_threads(self.cursor_pos.1); let threads = account.collection.get_threads(self.cursor_pos.1);
if !account.collection.contains_key(&env_hash) { if !account.collection.contains_key(env_hash) {
return false; return false;
} }
let new_env_thread_node_hash = account.collection.get_env(*env_hash).thread(); let new_env_thread_node_hash = account.collection.get_env(*env_hash).thread();

View File

@ -101,8 +101,10 @@ pub struct ConversationsListing {
subsort: (SortField, SortOrder), subsort: (SortField, SortOrder),
all_threads: HashSet<ThreadHash>, all_threads: HashSet<ThreadHash>,
order: HashMap<ThreadHash, usize>, order: HashMap<ThreadHash, usize>,
#[allow(clippy::type_complexity)]
rows: std::result::Result<Vec<((usize, (ThreadHash, EnvelopeHash)), EntryStrings)>, String>, rows: std::result::Result<Vec<((usize, (ThreadHash, EnvelopeHash)), EntryStrings)>, String>,
#[allow(clippy::type_complexity)]
search_job: Option<(String, JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>)>, search_job: Option<(String, JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>)>,
filter_term: String, filter_term: String,
filtered_selection: Vec<ThreadHash>, filtered_selection: Vec<ThreadHash>,
@ -148,7 +150,7 @@ impl MailListingTrait for ConversationsListing {
.flatten() .flatten()
.chain(cursor_iter.into_iter().flatten()) .chain(cursor_iter.into_iter().flatten())
.cloned(); .cloned();
SmallVec::from_iter(iter.into_iter()) SmallVec::from_iter(iter)
} }
fn refresh_mailbox(&mut self, context: &mut Context, force: bool) { fn refresh_mailbox(&mut self, context: &mut Context, force: bool) {
@ -376,7 +378,7 @@ impl ListingTrait for ConversationsListing {
if let Err(message) = self.rows.as_ref() { if let Err(message) = self.rows.as_ref() {
clear_area(grid, area, self.color_cache.theme_default); clear_area(grid, area, self.color_cache.theme_default);
write_string_to_grid( write_string_to_grid(
&message, message,
grid, grid,
self.color_cache.theme_default.fg, self.color_cache.theme_default.fg,
self.color_cache.theme_default.bg, self.color_cache.theme_default.bg,
@ -584,8 +586,8 @@ impl ConversationsListing {
const DESCRIPTION: &'static str = "conversations listing"; const DESCRIPTION: &'static str = "conversations listing";
//const PADDING_CHAR: char = ' '; //░'; //const PADDING_CHAR: char = ' '; //░';
pub fn new(coordinates: (AccountHash, MailboxHash)) -> Self { pub fn new(coordinates: (AccountHash, MailboxHash)) -> Box<Self> {
ConversationsListing { Box::new(ConversationsListing {
cursor_pos: (coordinates.0, 1, 0), cursor_pos: (coordinates.0, 1, 0),
new_cursor_pos: (coordinates.0, coordinates.1, 0), new_cursor_pos: (coordinates.0, coordinates.1, 0),
length: 0, length: 0,
@ -609,7 +611,7 @@ impl ConversationsListing {
modifier_active: false, modifier_active: false,
modifier_command: None, modifier_command: None,
id: ComponentId::new_v4(), id: ComponentId::new_v4(),
} })
} }
pub(super) fn make_entry_string( pub(super) fn make_entry_string(
@ -714,8 +716,7 @@ impl ConversationsListing {
.settings .settings
.listing .listing
.datetime_fmt .datetime_fmt
.as_ref() .as_deref()
.map(String::as_str)
.or(Some("%Y-%m-%d %T")), .or(Some("%Y-%m-%d %T")),
false, false,
), ),
@ -1226,7 +1227,7 @@ impl Component for ConversationsListing {
UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => { UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => {
let account = &context.accounts[&self.cursor_pos.0]; let account = &context.accounts[&self.cursor_pos.0];
let threads = account.collection.get_threads(self.cursor_pos.1); let threads = account.collection.get_threads(self.cursor_pos.1);
if !account.collection.contains_key(&new_hash) { if !account.collection.contains_key(new_hash) {
return false; return false;
} }
let env_thread_node_hash = account.collection.get_env(*new_hash).thread(); let env_thread_node_hash = account.collection.get_env(*new_hash).thread();
@ -1258,7 +1259,7 @@ impl Component for ConversationsListing {
UIEvent::EnvelopeUpdate(ref env_hash) => { UIEvent::EnvelopeUpdate(ref env_hash) => {
let account = &context.accounts[&self.cursor_pos.0]; let account = &context.accounts[&self.cursor_pos.0];
let threads = account.collection.get_threads(self.cursor_pos.1); let threads = account.collection.get_threads(self.cursor_pos.1);
if !account.collection.contains_key(&env_hash) { if !account.collection.contains_key(env_hash) {
return false; return false;
} }
let env_thread_node_hash = account.collection.get_env(*env_hash).thread(); let env_thread_node_hash = account.collection.get_env(*env_hash).thread();

View File

@ -41,7 +41,7 @@ impl MailListingTrait for OfflineListing {
} }
fn get_focused_items(&self, _context: &Context) -> SmallVec<[ThreadHash; 8]> { fn get_focused_items(&self, _context: &Context) -> SmallVec<[ThreadHash; 8]> {
return SmallVec::new(); SmallVec::new()
} }
fn refresh_mailbox(&mut self, _context: &mut Context, _force: bool) {} fn refresh_mailbox(&mut self, _context: &mut Context, _force: bool) {}
@ -93,14 +93,14 @@ impl fmt::Display for OfflineListing {
} }
impl OfflineListing { impl OfflineListing {
pub fn new(cursor_pos: (AccountHash, MailboxHash)) -> Self { pub fn new(cursor_pos: (AccountHash, MailboxHash)) -> Box<Self> {
OfflineListing { Box::new(OfflineListing {
cursor_pos, cursor_pos,
_row_updates: SmallVec::new(), _row_updates: SmallVec::new(),
_selection: HashMap::default(), _selection: HashMap::default(),
dirty: true, dirty: true,
id: ComponentId::new_v4(), id: ComponentId::new_v4(),
} })
} }
} }

View File

@ -133,6 +133,7 @@ pub struct PlainListing {
/// Cache current view. /// Cache current view.
data_columns: DataColumns, data_columns: DataColumns,
#[allow(clippy::type_complexity)]
search_job: Option<(String, JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>)>, search_job: Option<(String, JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>)>,
filter_term: String, filter_term: String,
filtered_selection: Vec<EnvelopeHash>, filtered_selection: Vec<EnvelopeHash>,
@ -311,7 +312,7 @@ impl MailListingTrait for PlainListing {
let roots = items let roots = items
.filter_map(|r| threads.groups[&r].root().map(|r| r.root)) .filter_map(|r| threads.groups[&r].root().map(|r| r.root))
.collect::<_>(); .collect::<_>();
let thread_nodes: &HashMap<ThreadNodeHash, ThreadNode> = &threads.thread_nodes(); let thread_nodes: &HashMap<ThreadNodeHash, ThreadNode> = threads.thread_nodes();
let env_hash_iter = Box::new( let env_hash_iter = Box::new(
threads threads
.threads_group_iter(roots) .threads_group_iter(roots)
@ -685,8 +686,8 @@ impl fmt::Display for PlainListing {
impl PlainListing { impl PlainListing {
const DESCRIPTION: &'static str = "plain listing"; const DESCRIPTION: &'static str = "plain listing";
pub fn new(coordinates: (AccountHash, MailboxHash)) -> Self { pub fn new(coordinates: (AccountHash, MailboxHash)) -> Box<Self> {
PlainListing { Box::new(PlainListing {
cursor_pos: (0, 1, 0), cursor_pos: (0, 1, 0),
new_cursor_pos: (coordinates.0, coordinates.1, 0), new_cursor_pos: (coordinates.0, coordinates.1, 0),
length: 0, length: 0,
@ -714,7 +715,7 @@ impl PlainListing {
movement: None, movement: None,
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 {
@ -884,7 +885,7 @@ impl PlainListing {
Box::new(self.local_collection.iter().cloned()) Box::new(self.local_collection.iter().cloned())
as Box<dyn Iterator<Item = EnvelopeHash>> as Box<dyn Iterator<Item = EnvelopeHash>>
} else { } else {
Box::new(self.filtered_selection.iter().map(|h| *h)) Box::new(self.filtered_selection.iter().cloned())
as Box<dyn Iterator<Item = EnvelopeHash>> as Box<dyn Iterator<Item = EnvelopeHash>>
}; };
@ -1234,7 +1235,7 @@ impl Component for PlainListing {
.flatten() .flatten()
.chain(cursor_iter.into_iter().flatten()) .chain(cursor_iter.into_iter().flatten())
.cloned(); .cloned();
let stack: SmallVec<[_; 8]> = SmallVec::from_iter(iter.into_iter()); let stack: SmallVec<[_; 8]> = SmallVec::from_iter(iter);
for i in stack { for i in stack {
self.perform_action(context, i, a); self.perform_action(context, i, a);
} }

View File

@ -241,6 +241,7 @@ impl MailListingTrait for ThreadListing {
} }
let mut rows = Vec::with_capacity(1024); let mut rows = Vec::with_capacity(1024);
let mut min_width = (0, 0, 0, 0, 0); let mut min_width = (0, 0, 0, 0, 0);
#[allow(clippy::type_complexity)]
let mut row_widths: ( let mut row_widths: (
SmallVec<[u8; 1024]>, SmallVec<[u8; 1024]>,
SmallVec<[u8; 1024]>, SmallVec<[u8; 1024]>,
@ -260,7 +261,7 @@ impl MailListingTrait for ThreadListing {
.filter_map(|r| threads.groups[&r].root().map(|r| r.root)) .filter_map(|r| threads.groups[&r].root().map(|r| r.root))
.collect::<_>(); .collect::<_>();
let mut iter = threads.threads_group_iter(roots).peekable(); let mut iter = threads.threads_group_iter(roots).peekable();
let thread_nodes: &HashMap<ThreadNodeHash, ThreadNode> = &threads.thread_nodes(); let thread_nodes: &HashMap<ThreadNodeHash, ThreadNode> = threads.thread_nodes();
/* This is just a desugared for loop so that we can use .peek() */ /* This is just a desugared for loop so that we can use .peek() */
let mut idx = 0; let mut idx = 0;
let mut prev_group = ThreadHash::null(); let mut prev_group = ThreadHash::null();
@ -737,8 +738,8 @@ impl fmt::Display for ThreadListing {
} }
impl ThreadListing { impl ThreadListing {
pub fn new(coordinates: (AccountHash, MailboxHash)) -> Self { pub fn new(coordinates: (AccountHash, MailboxHash)) -> Box<Self> {
ThreadListing { Box::new(ThreadListing {
cursor_pos: (coordinates.0, 0, 0), cursor_pos: (coordinates.0, 0, 0),
new_cursor_pos: (coordinates.0, coordinates.1, 0), new_cursor_pos: (coordinates.0, coordinates.1, 0),
length: 0, length: 0,
@ -757,7 +758,7 @@ impl ThreadListing {
initialised: false, initialised: false,
movement: None, movement: None,
id: ComponentId::new_v4(), id: ComponentId::new_v4(),
} })
} }
fn highlight_line_self(&mut self, _idx: usize, _context: &Context) { fn highlight_line_self(&mut self, _idx: usize, _context: &Context) {
@ -879,7 +880,7 @@ impl ThreadListing {
EntryStrings { EntryStrings {
date: DateString(ConversationsListing::format_date(context, e.date())), date: DateString(ConversationsListing::format_date(context, e.date())),
subject: SubjectString(subject), subject: SubjectString(subject),
flag: FlagString(format!("{}", if e.has_attachments() { "📎" } else { "" },)), flag: FlagString((if e.has_attachments() { "📎" } else { "" }).to_string()),
from: FromString(address_list!((e.from()) as comma_sep_list)), from: FromString(address_list!((e.from()) as comma_sep_list)),
tags: TagString(tags, colors), tags: TagString(tags, colors),
} }
@ -1243,7 +1244,7 @@ impl Component for ThreadListing {
} }
UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => { UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => {
let account = &context.accounts[&self.cursor_pos.0]; let account = &context.accounts[&self.cursor_pos.0];
if !account.collection.contains_key(&new_hash) { if !account.collection.contains_key(new_hash) {
return false; return false;
} }
if let Some(row) = self.order.remove(old_hash) { if let Some(row) = self.order.remove(old_hash) {

View File

@ -39,7 +39,7 @@ pub async fn verify(a: Attachment) -> Result<()> {
let (data, sig) = let (data, sig) =
melib_pgp::verify_signature(&a).chain_err_summary(|| "Could not verify signature.")?; melib_pgp::verify_signature(&a).chain_err_summary(|| "Could not verify signature.")?;
let mut ctx = Context::new()?; let mut ctx = Context::new()?;
let sig = ctx.new_data_mem(&sig.body().trim())?; let sig = ctx.new_data_mem(sig.body().trim())?;
let data = ctx.new_data_mem(&data)?; let data = ctx.new_data_mem(&data)?;
ctx.verify(sig, data)?.await ctx.verify(sig, data)?.await
} }

View File

@ -292,7 +292,7 @@ impl Component for AccountStatus {
None, None,
); );
line += 1; line += 1;
for (name, status) in extensions.into_iter() { for (name, status) in extensions.iter() {
width = self.content.size().0; width = self.content.size().0;
write_string_to_grid( write_string_to_grid(
name.trim_at_boundary(30), name.trim_at_boundary(30),
@ -418,7 +418,7 @@ impl Component for AccountStatus {
return true; return true;
} }
UIEvent::Input(ref key) if shortcut!(key == shortcuts["general"]["scroll_right"]) => { UIEvent::Input(ref key) if shortcut!(key == shortcuts["general"]["scroll_right"]) => {
self.cursor.0 = self.cursor.0 + 1; self.cursor.0 += 1;
self.dirty = true; self.dirty = true;
return true; return true;
} }
@ -428,7 +428,7 @@ impl Component for AccountStatus {
return true; return true;
} }
UIEvent::Input(ref key) if shortcut!(key == shortcuts["general"]["scroll_down"]) => { UIEvent::Input(ref key) if shortcut!(key == shortcuts["general"]["scroll_down"]) => {
self.cursor.1 = self.cursor.1 + 1; self.cursor.1 += 1;
self.dirty = true; self.dirty = true;
return true; return true;
} }

View File

@ -58,7 +58,7 @@ enum ViewMode {
Source(Source), Source(Source),
//Ansi(RawBuffer), //Ansi(RawBuffer),
Subview, Subview,
ContactSelector(UIDialog<Card>), ContactSelector(Box<UIDialog<Card>>),
} }
impl Default for ViewMode { impl Default for ViewMode {
@ -76,69 +76,65 @@ impl ViewMode {
} }
} }
*/ */
fn is_attachment(&self) -> bool { fn is_attachment(&self) -> bool {
match self { matches!(self, ViewMode::Attachment(_))
ViewMode::Attachment(_) => true,
_ => false,
}
} }
fn is_contact_selector(&self) -> bool { fn is_contact_selector(&self) -> bool {
match self { matches!(self, ViewMode::ContactSelector(_))
ViewMode::ContactSelector(_) => true,
_ => false,
}
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub enum AttachmentDisplay { pub enum AttachmentDisplay {
Alternative { Alternative {
inner: Attachment, inner: Box<Attachment>,
shown_display: usize, shown_display: usize,
display: Vec<AttachmentDisplay>, display: Vec<AttachmentDisplay>,
}, },
InlineText { InlineText {
inner: Attachment, inner: Box<Attachment>,
comment: Option<String>, comment: Option<String>,
text: String, text: String,
}, },
InlineOther { InlineOther {
inner: Attachment, inner: Box<Attachment>,
}, },
Attachment { Attachment {
inner: Attachment, inner: Box<Attachment>,
}, },
SignedPending { SignedPending {
inner: Attachment, inner: Box<Attachment>,
display: Vec<AttachmentDisplay>, display: Vec<AttachmentDisplay>,
handle: JoinHandle<Result<()>>, handle: JoinHandle<Result<()>>,
job_id: JobId, job_id: JobId,
}, },
SignedFailed { SignedFailed {
inner: Attachment, inner: Box<Attachment>,
display: Vec<AttachmentDisplay>, display: Vec<AttachmentDisplay>,
error: MeliError, error: MeliError,
}, },
SignedUnverified { SignedUnverified {
inner: Attachment, inner: Box<Attachment>,
display: Vec<AttachmentDisplay>, display: Vec<AttachmentDisplay>,
}, },
SignedVerified { SignedVerified {
inner: Attachment, inner: Box<Attachment>,
display: Vec<AttachmentDisplay>, display: Vec<AttachmentDisplay>,
description: String, description: String,
}, },
EncryptedPending { EncryptedPending {
inner: Attachment, inner: Box<Attachment>,
handle: JoinHandle<Result<(melib::pgp::DecryptionMetadata, Vec<u8>)>>, handle: JoinHandle<Result<(melib::pgp::DecryptionMetadata, Vec<u8>)>>,
}, },
EncryptedFailed { EncryptedFailed {
inner: Attachment, inner: Box<Attachment>,
error: MeliError, error: MeliError,
}, },
EncryptedSuccess { EncryptedSuccess {
inner: Attachment, inner: Box<Attachment>,
plaintext: Attachment, plaintext: Box<Attachment>,
plaintext_display: Vec<AttachmentDisplay>, plaintext_display: Vec<AttachmentDisplay>,
description: String, description: String,
}, },
@ -191,8 +187,8 @@ enum MailViewState {
}, },
Loaded { Loaded {
bytes: Vec<u8>, bytes: Vec<u8>,
env: Envelope, env: Box<Envelope>,
body: Attachment, body: Box<Attachment>,
display: Vec<AttachmentDisplay>, display: Vec<AttachmentDisplay>,
body_text: String, body_text: String,
links: Vec<Link>, links: Vec<Link>,
@ -313,9 +309,10 @@ impl MailView {
.get_env_mut(self.coordinates.2) .get_env_mut(self.coordinates.2)
.populate_headers(&bytes); .populate_headers(&bytes);
} }
let env = let env = Box::new(
account.collection.get_env(self.coordinates.2).clone(); account.collection.get_env(self.coordinates.2).clone(),
let body = AttachmentBuilder::new(&bytes).build(); );
let body = Box::new(AttachmentBuilder::new(&bytes).build());
let display = Self::attachment_to( let display = Self::attachment_to(
&body, &body,
context, context,
@ -415,7 +412,7 @@ impl MailView {
.. ..
} => ( } => (
bytes, bytes,
self.attachment_displays_to_text(&display, context, false), self.attachment_displays_to_text(display, context, false),
env, env,
), ),
MailViewState::Error { .. } => { MailViewState::Error { .. } => {
@ -489,13 +486,13 @@ impl MailView {
if !acc.ends_with("\n\n") { if !acc.ends_with("\n\n") {
acc.push_str("\n\n"); acc.push_str("\n\n");
} }
acc.push_str(&text); acc.push_str(text);
} }
InlineText { InlineText {
inner: _, inner: _,
text, text,
comment: _, comment: _,
} => acc.push_str(&text), } => acc.push_str(text),
InlineOther { inner } => { InlineOther { inner } => {
if !acc.ends_with("\n\n") { if !acc.ends_with("\n\n") {
acc.push_str("\n\n"); acc.push_str("\n\n");
@ -550,7 +547,7 @@ impl MailView {
if description.is_empty() { if description.is_empty() {
acc.push_str("Verified signature.\n\n"); acc.push_str("Verified signature.\n\n");
} else { } else {
acc.push_str(&description); acc.push_str(description);
acc.push_str("\n\n"); acc.push_str("\n\n");
} }
} }
@ -574,7 +571,7 @@ impl MailView {
if description.is_empty() { if description.is_empty() {
acc.push_str("Succesfully decrypted.\n\n"); acc.push_str("Succesfully decrypted.\n\n");
} else { } else {
acc.push_str(&description); acc.push_str(description);
acc.push_str("\n\n"); acc.push_str("\n\n");
} }
} }
@ -672,35 +669,32 @@ impl MailView {
s.push(' '); s.push(' ');
} }
s.extend(att.to_string().chars()); s.push_str(&att.to_string());
paths.push(cur_path.clone()); paths.push(cur_path.clone());
match att.content_type { if matches!(att.content_type, ContentType::Multipart { .. }) {
ContentType::Multipart { .. } => { let mut iter = (0..sub_att_display_vec.len()).peekable();
let mut iter = (0..sub_att_display_vec.len()).peekable(); if has_sibling {
if has_sibling { branches.push(true);
branches.push(true); } else {
} else { branches.push(false);
branches.push(false);
}
while let Some(i) = iter.next() {
*idx += 1;
cur_path.push(i);
append_entry(
(idx, (depth + 1, &sub_att_display_vec[i])),
branches,
paths,
cur_path,
iter.peek() != None,
s,
);
if Some(i) == default_alternative {
s.push_str(" (displayed by default)");
}
cur_path.pop();
}
branches.pop();
} }
_ => {} while let Some(i) = iter.next() {
*idx += 1;
cur_path.push(i);
append_entry(
(idx, (depth + 1, &sub_att_display_vec[i])),
branches,
paths,
cur_path,
iter.peek() != None,
s,
);
if Some(i) == default_alternative {
s.push_str(" (displayed by default)");
}
cur_path.pop();
}
branches.pop();
} }
} }
@ -735,7 +729,9 @@ impl MailView {
active_jobs: &mut HashSet<JobId>, active_jobs: &mut HashSet<JobId>,
) { ) {
if a.content_disposition.kind.is_attachment() || a.content_type == "message/rfc822" { if a.content_disposition.kind.is_attachment() || a.content_type == "message/rfc822" {
acc.push(AttachmentDisplay::Attachment { inner: a.clone() }); acc.push(AttachmentDisplay::Attachment {
inner: Box::new(a.clone()),
});
} else if a.content_type().is_text_html() { } else if a.content_type().is_text_html() {
let bytes = decode(a, None); let bytes = decode(a, None);
let filter_invocation = let filter_invocation =
@ -764,7 +760,7 @@ impl MailView {
)); ));
let text = String::from_utf8_lossy(&bytes).to_string(); let text = String::from_utf8_lossy(&bytes).to_string();
acc.push(AttachmentDisplay::InlineText { acc.push(AttachmentDisplay::InlineText {
inner: a.clone(), inner: Box::new(a.clone()),
comment, comment,
text, text,
}); });
@ -785,7 +781,7 @@ impl MailView {
) )
.to_string(); .to_string();
acc.push(AttachmentDisplay::InlineText { acc.push(AttachmentDisplay::InlineText {
inner: a.clone(), inner: Box::new(a.clone()),
comment, comment,
text, text,
}); });
@ -794,7 +790,7 @@ impl MailView {
} else if a.is_text() { } else if a.is_text() {
let bytes = decode(a, None); let bytes = decode(a, None);
acc.push(AttachmentDisplay::InlineText { acc.push(AttachmentDisplay::InlineText {
inner: a.clone(), inner: Box::new(a.clone()),
comment: None, comment: None,
text: String::from_utf8_lossy(&bytes).to_string(), text: String::from_utf8_lossy(&bytes).to_string(),
}); });
@ -838,7 +834,7 @@ impl MailView {
rec(a, context, coordinates, &mut display, active_jobs); rec(a, context, coordinates, &mut display, active_jobs);
} }
acc.push(AttachmentDisplay::Alternative { acc.push(AttachmentDisplay::Alternative {
inner: a.clone(), inner: Box::new(a.clone()),
shown_display: chosen_attachment_idx, shown_display: chosen_attachment_idx,
display, display,
}); });
@ -847,7 +843,7 @@ impl MailView {
#[cfg(not(feature = "gpgme"))] #[cfg(not(feature = "gpgme"))]
{ {
acc.push(AttachmentDisplay::SignedUnverified { acc.push(AttachmentDisplay::SignedUnverified {
inner: a.clone(), inner: Box::new(a.clone()),
display: { display: {
let mut v = vec![]; let mut v = vec![];
rec(&parts[0], context, coordinates, &mut v, active_jobs); rec(&parts[0], context, coordinates, &mut v, active_jobs);
@ -869,7 +865,7 @@ impl MailView {
StatusEvent::NewJob(handle.job_id), StatusEvent::NewJob(handle.job_id),
)); ));
acc.push(AttachmentDisplay::SignedPending { acc.push(AttachmentDisplay::SignedPending {
inner: a.clone(), inner: Box::new(a.clone()),
job_id: handle.job_id, job_id: handle.job_id,
display: { display: {
let mut v = vec![]; let mut v = vec![];
@ -880,7 +876,7 @@ impl MailView {
}); });
} else { } else {
acc.push(AttachmentDisplay::SignedUnverified { acc.push(AttachmentDisplay::SignedUnverified {
inner: a.clone(), inner: Box::new(a.clone()),
display: { display: {
let mut v = vec![]; let mut v = vec![];
rec(&parts[0], context, coordinates, &mut v, active_jobs); rec(&parts[0], context, coordinates, &mut v, active_jobs);
@ -896,7 +892,7 @@ impl MailView {
#[cfg(not(feature = "gpgme"))] #[cfg(not(feature = "gpgme"))]
{ {
acc.push(AttachmentDisplay::EncryptedFailed { acc.push(AttachmentDisplay::EncryptedFailed {
inner: a.clone(), inner: Box::new(a.clone()),
error: MeliError::new("Cannot decrypt: meli must be compiled with libgpgme support."), error: MeliError::new("Cannot decrypt: meli must be compiled with libgpgme support."),
}); });
} }
@ -914,12 +910,12 @@ impl MailView {
StatusEvent::NewJob(handle.job_id), StatusEvent::NewJob(handle.job_id),
)); ));
acc.push(AttachmentDisplay::EncryptedPending { acc.push(AttachmentDisplay::EncryptedPending {
inner: a.clone(), inner: Box::new(a.clone()),
handle, handle,
}); });
} else { } else {
acc.push(AttachmentDisplay::EncryptedFailed { acc.push(AttachmentDisplay::EncryptedFailed {
inner: a.clone(), inner: Box::new(a.clone()),
error: MeliError::new("Undecrypted."), error: MeliError::new("Undecrypted."),
}); });
} }
@ -963,17 +959,13 @@ impl MailView {
} else { } else {
return None; return None;
}; };
if let Some(path) = if let Some(path) = self.attachment_paths.get(lidx).and_then(|path| {
self.attachment_paths.get(lidx).and_then( if !path.is_empty() {
|path| { Some(path)
if path.len() > 0 { } else {
Some(path) None
} else { }
None }) {
}
},
)
{
let first = path[0]; let first = path[0];
use AttachmentDisplay::*; use AttachmentDisplay::*;
let root_attachment = match &display[first] { let root_attachment = match &display[first] {
@ -1022,14 +1014,11 @@ impl MailView {
if path.is_empty() { if path.is_empty() {
return Some(a); return Some(a);
} }
match a.content_type { if let ContentType::Multipart { ref parts, .. } = a.content_type {
ContentType::Multipart { ref parts, .. } => { let first = path[0];
let first = path[0]; if first < parts.len() {
if first < parts.len() { return find_attachment(&parts[first], &path[1..]);
return find_attachment(&parts[first], &path[1..]);
}
} }
_ => {}
} }
None None
} }
@ -1080,7 +1069,7 @@ impl MailView {
entries.push((new_card, format!("{}", addr))); entries.push((new_card, format!("{}", addr)));
} }
drop(envelope); drop(envelope);
self.mode = ViewMode::ContactSelector(Selector::new( self.mode = ViewMode::ContactSelector(Box::new(Selector::new(
"select contacts to add", "select contacts to add",
entries, entries,
false, false,
@ -1088,7 +1077,7 @@ impl MailView {
Some(UIEvent::FinishedUIDialog(id, Box::new(results.to_vec()))) Some(UIEvent::FinishedUIDialog(id, Box::new(results.to_vec())))
})), })),
context, context,
)); )));
self.dirty = true; self.dirty = true;
self.initialised = false; self.initialised = false;
} }
@ -1420,10 +1409,7 @@ impl Component for MailView {
err.to_string(), err.to_string(),
Some(NotificationType::Error(err.kind)), Some(NotificationType::Error(err.kind)),
)); ));
log( log(format!("Failed to open envelope: {}", err), ERROR);
format!("Failed to open envelope: {}", err.to_string()),
ERROR,
);
self.init_futures(context); self.init_futures(context);
return; return;
} else { } else {
@ -1444,7 +1430,7 @@ impl Component for MailView {
let mut text = "Viewing attachment. Press `r` to return \n".to_string(); let mut text = "Viewing attachment. Press `r` to return \n".to_string();
if let Some(attachment) = self.open_attachment(aidx, context) { if let Some(attachment) = self.open_attachment(aidx, context) {
if attachment.is_html() { if attachment.is_html() {
self.subview = Some(Box::new(HtmlView::new(&attachment, context))); self.subview = Some(Box::new(HtmlView::new(attachment, context)));
self.mode = ViewMode::Subview; self.mode = ViewMode::Subview;
} else { } else {
text.push_str(&attachment.text()); text.push_str(&attachment.text());
@ -1475,7 +1461,7 @@ impl Component for MailView {
} }
} }
ViewMode::Normal if body.is_html() => { ViewMode::Normal if body.is_html() => {
self.subview = Some(Box::new(HtmlView::new(&body, context))); self.subview = Some(Box::new(HtmlView::new(body, context)));
self.mode = ViewMode::Subview; self.mode = ViewMode::Subview;
} }
ViewMode::Normal ViewMode::Normal
@ -1497,13 +1483,12 @@ impl Component for MailView {
} => } =>
{ {
self.subview = Some(Box::new(HtmlView::new( self.subview = Some(Box::new(HtmlView::new(
&body body.content_type
.content_type
.parts() .parts()
.unwrap() .unwrap()
.into_iter() .iter()
.find(|a| a.is_html()) .find(|a| a.is_html())
.unwrap_or(&body), .unwrap_or(body),
context, context,
))); )));
self.mode = ViewMode::Subview; self.mode = ViewMode::Subview;
@ -1541,7 +1526,7 @@ impl Component for MailView {
if !ret.ends_with("\n\n") { if !ret.ends_with("\n\n") {
ret.push_str("\n\n"); ret.push_str("\n\n");
} }
ret.extend(body_text.chars()); ret.push_str(body_text);
if !ret.ends_with("\n\n") { if !ret.ends_with("\n\n") {
ret.push_str("\n\n"); ret.push_str("\n\n");
} }
@ -1721,10 +1706,8 @@ impl Component for MailView {
self.force_draw_headers = true; self.force_draw_headers = true;
if self.pager.cursor_pos() == 0 { if self.pager.cursor_pos() == 0 {
self.headers_cursor = self.headers_cursor.saturating_sub(1); self.headers_cursor = self.headers_cursor.saturating_sub(1);
} else { } else if self.pager.process_event(event, context) {
if self.pager.process_event(event, context) { return true;
return true;
}
} }
self.pager.set_dirty(true); self.pager.set_dirty(true);
return true; return true;
@ -1767,11 +1750,13 @@ impl Component for MailView {
.get_env_mut(self.coordinates.2) .get_env_mut(self.coordinates.2)
.populate_headers(&bytes); .populate_headers(&bytes);
} }
let env = context.accounts[&self.coordinates.0] let env = Box::new(
.collection context.accounts[&self.coordinates.0]
.get_env(self.coordinates.2) .collection
.clone(); .get_env(self.coordinates.2)
let body = AttachmentBuilder::new(&bytes).build(); .clone(),
);
let body = Box::new(AttachmentBuilder::new(&bytes).build());
let display = Self::attachment_to( let display = Self::attachment_to(
&body, &body,
context, context,
@ -1823,9 +1808,11 @@ impl Component for MailView {
*d = AttachmentDisplay::SignedVerified { *d = AttachmentDisplay::SignedVerified {
inner: std::mem::replace( inner: std::mem::replace(
inner, inner,
AttachmentBuilder::new(&[]).build(), Box::new(
AttachmentBuilder::new(&[]).build(),
),
), ),
display: std::mem::replace(display, vec![]), display: std::mem::take(display),
description: String::new(), description: String::new(),
}; };
} }
@ -1833,9 +1820,11 @@ impl Component for MailView {
*d = AttachmentDisplay::SignedFailed { *d = AttachmentDisplay::SignedFailed {
inner: std::mem::replace( inner: std::mem::replace(
inner, inner,
AttachmentBuilder::new(&[]).build(), Box::new(
AttachmentBuilder::new(&[]).build(),
),
), ),
display: std::mem::replace(display, vec![]), display: std::mem::take(display),
error, error,
}; };
} }
@ -1851,9 +1840,10 @@ impl Component for MailView {
Ok(None) => { /* something happened, perhaps a worker thread panicked */ Ok(None) => { /* something happened, perhaps a worker thread panicked */
} }
Ok(Some(Ok((metadata, decrypted_bytes)))) => { Ok(Some(Ok((metadata, decrypted_bytes)))) => {
let plaintext = let plaintext = Box::new(
AttachmentBuilder::new(&decrypted_bytes) AttachmentBuilder::new(&decrypted_bytes)
.build(); .build(),
);
let plaintext_display = Self::attachment_to( let plaintext_display = Self::attachment_to(
&plaintext, &plaintext,
context, context,
@ -1863,7 +1853,9 @@ impl Component for MailView {
*d = AttachmentDisplay::EncryptedSuccess { *d = AttachmentDisplay::EncryptedSuccess {
inner: std::mem::replace( inner: std::mem::replace(
inner, inner,
AttachmentBuilder::new(&[]).build(), Box::new(
AttachmentBuilder::new(&[]).build(),
),
), ),
plaintext, plaintext,
plaintext_display, plaintext_display,
@ -1874,7 +1866,9 @@ impl Component for MailView {
*d = AttachmentDisplay::EncryptedFailed { *d = AttachmentDisplay::EncryptedFailed {
inner: std::mem::replace( inner: std::mem::replace(
inner, inner,
AttachmentBuilder::new(&[]).build(), Box::new(
AttachmentBuilder::new(&[]).build(),
),
), ),
error, error,
}; };
@ -1888,9 +1882,9 @@ impl Component for MailView {
let mut new_body_text = String::new(); let mut new_body_text = String::new();
if let MailViewState::Loaded { ref display, .. } = self.state { if let MailViewState::Loaded { ref display, .. } = self.state {
new_body_text = new_body_text =
self.attachment_displays_to_text(&display, context, true); self.attachment_displays_to_text(display, context, true);
let (paths, attachment_tree_s) = let (paths, attachment_tree_s) =
self.attachment_displays_to_tree(&display); self.attachment_displays_to_tree(display);
self.attachment_tree = attachment_tree_s; self.attachment_tree = attachment_tree_s;
self.attachment_paths = paths; self.attachment_paths = paths;
} }
@ -2042,7 +2036,7 @@ impl Component for MailView {
.get(&env_hash) .get(&env_hash)
.map(|env| env.message_id_display()) .map(|env| env.message_id_display())
.unwrap_or_else(|| "Not found".into()), .unwrap_or_else(|| "Not found".into()),
err.to_string() err
); );
log(&err_string, ERROR); log(&err_string, ERROR);
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification(
@ -2088,7 +2082,7 @@ impl Component for MailView {
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); .push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
return true; return true;
} }
UIEvent::Input(Key::Char(c)) if c >= '0' && c <= '9' => { UIEvent::Input(Key::Char(c)) if ('0'..='9').contains(&c) => {
self.cmd_buf.push(c); self.cmd_buf.push(c);
context context
.replies .replies
@ -2218,7 +2212,7 @@ impl Component for MailView {
if let Ok(command) = query_default_app(&attachment_type) { if let Ok(command) = query_default_app(&attachment_type) {
let p = create_temp_file( let p = create_temp_file(
&decode(attachment, None), &decode(attachment, None),
filename.as_ref().map(|s| s.as_str()), filename.as_deref(),
None, None,
true, true,
); );
@ -2400,10 +2394,7 @@ impl Component for MailView {
err.to_string(), err.to_string(),
Some(NotificationType::Error(err.kind)), Some(NotificationType::Error(err.kind)),
)); ));
log( log(format!("Failed to open envelope: {}", err), ERROR);
format!("Failed to open envelope: {}", err.to_string()),
ERROR,
);
self.init_futures(context); self.init_futures(context);
return true; return true;
} else { } else {
@ -2424,11 +2415,7 @@ impl Component for MailView {
Some(NotificationType::Error(melib::ErrorKind::External)), Some(NotificationType::Error(melib::ErrorKind::External)),
)); ));
log( log(
format!( format!("Failed to create file at {}: {}", path.display(), err),
"Failed to create file at {}: {}",
path.display(),
err.to_string()
),
ERROR, ERROR,
); );
return true; return true;
@ -2461,10 +2448,7 @@ impl Component for MailView {
err.to_string(), err.to_string(),
Some(NotificationType::Error(err.kind)), Some(NotificationType::Error(err.kind)),
)); ));
log( log(format!("Failed to open envelope: {}", err), ERROR);
format!("Failed to open envelope: {}", err.to_string()),
ERROR,
);
self.init_futures(context); self.init_futures(context);
return true; return true;
} else { } else {
@ -2490,11 +2474,7 @@ impl Component for MailView {
Some(NotificationType::Error(melib::ErrorKind::External)), Some(NotificationType::Error(melib::ErrorKind::External)),
)); ));
log( log(
format!( format!("Failed to create file at {}: {}", path.display(), err),
"Failed to create file at {}: {}",
path.display(),
err.to_string()
),
ERROR, ERROR,
); );
} }
@ -2521,11 +2501,7 @@ impl Component for MailView {
Some(NotificationType::Error(melib::ErrorKind::External)), Some(NotificationType::Error(melib::ErrorKind::External)),
)); ));
log( log(
format!( format!("Failed to create file at {}: {}", path.display(), err),
"Failed to create file at {}: {}",
path.display(),
err.to_string()
),
ERROR, ERROR,
); );
return true; return true;

View File

@ -36,10 +36,7 @@ enum ViewMode {
impl ViewMode { impl ViewMode {
fn is_attachment(&self) -> bool { fn is_attachment(&self) -> bool {
match self { matches!(self, ViewMode::Attachment(_))
ViewMode::Attachment(_) => true,
_ => false,
}
} }
} }
@ -53,7 +50,7 @@ pub struct EnvelopeView {
mode: ViewMode, mode: ViewMode,
mail: Mail, mail: Mail,
account_hash: AccountHash, _account_hash: AccountHash,
cmd_buf: String, cmd_buf: String,
id: ComponentId, id: ComponentId,
} }
@ -69,7 +66,7 @@ impl EnvelopeView {
mail: Mail, mail: Mail,
pager: Option<Pager>, pager: Option<Pager>,
subview: Option<Box<dyn Component>>, subview: Option<Box<dyn Component>>,
account_hash: AccountHash, _account_hash: AccountHash,
) -> Self { ) -> Self {
EnvelopeView { EnvelopeView {
pager, pager,
@ -77,7 +74,7 @@ impl EnvelopeView {
dirty: true, dirty: true,
mode: ViewMode::Normal, mode: ViewMode::Normal,
mail, mail,
account_hash, _account_hash,
cmd_buf: String::with_capacity(4), cmd_buf: String::with_capacity(4),
id: ComponentId::new_v4(), id: ComponentId::new_v4(),
} }
@ -87,7 +84,7 @@ impl EnvelopeView {
fn attachment_to_text(&self, body: &Attachment, context: &mut Context) -> String { fn attachment_to_text(&self, body: &Attachment, context: &mut Context) -> String {
let finder = LinkFinder::new(); let finder = LinkFinder::new();
let body_text = String::from_utf8_lossy(&decode_rec( let body_text = String::from_utf8_lossy(&decode_rec(
&body, body,
Some(Box::new(|a: &Attachment, v: &mut Vec<u8>| { Some(Box::new(|a: &Attachment, v: &mut Vec<u8>| {
if a.content_type().is_text_html() { if a.content_type().is_text_html() {
let settings = &context.settings; let settings = &context.settings;
@ -107,14 +104,13 @@ impl EnvelopeView {
err.to_string(), err.to_string(),
Some(NotificationType::Error(melib::ErrorKind::External)), Some(NotificationType::Error(melib::ErrorKind::External)),
)); ));
return;
} }
Ok(mut html_filter) => { Ok(mut html_filter) => {
html_filter html_filter
.stdin .stdin
.as_mut() .as_mut()
.unwrap() .unwrap()
.write_all(&v) .write_all(v)
.expect("Failed to write to stdin"); .expect("Failed to write to stdin");
*v = format!( *v = format!(
"Text piped through `{}`. Press `v` to open in web browser. \n\n", "Text piped through `{}`. Press `v` to open in web browser. \n\n",
@ -286,7 +282,7 @@ impl Component for EnvelopeView {
match self.mode { match self.mode {
ViewMode::Attachment(aidx) if body.attachments()[aidx].is_html() => { ViewMode::Attachment(aidx) if body.attachments()[aidx].is_html() => {
let attachment = &body.attachments()[aidx]; let attachment = &body.attachments()[aidx];
self.subview = Some(Box::new(HtmlView::new(&attachment, context))); self.subview = Some(Box::new(HtmlView::new(attachment, context)));
} }
ViewMode::Normal if body.is_html() => { ViewMode::Normal if body.is_html() => {
self.subview = Some(Box::new(HtmlView::new(&body, context))); self.subview = Some(Box::new(HtmlView::new(&body, context)));
@ -336,7 +332,7 @@ impl Component for EnvelopeView {
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); .push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
return true; return true;
} }
UIEvent::Input(Key::Char(c)) if c >= '0' && c <= '9' => { UIEvent::Input(Key::Char(c)) if ('0'..='9').contains(&c) => {
self.cmd_buf.push(c); self.cmd_buf.push(c);
return true; return true;
} }
@ -368,71 +364,70 @@ impl Component for EnvelopeView {
.replies .replies
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); .push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
{ if let Some(u) = self.mail.body().attachments().get(lidx) {
if let Some(u) = self.mail.body().attachments().get(lidx) { match u.content_type() {
match u.content_type() { ContentType::MessageRfc822 => {
ContentType::MessageRfc822 => { self.mode = ViewMode::Subview;
self.mode = ViewMode::Subview; let colors = crate::conf::value(context, "mail.view.body");
let colors = crate::conf::value(context, "mail.view.body"); self.subview = Some(Box::new(Pager::from_string(
self.subview = Some(Box::new(Pager::from_string( String::from_utf8_lossy(&decode_rec(u, None)).to_string(),
String::from_utf8_lossy(&decode_rec(u, None)).to_string(), Some(context),
Some(context), None,
None, None,
None, colors,
colors, )));
))); }
}
ContentType::Text { .. } ContentType::Text { .. }
| ContentType::PGPSignature | ContentType::PGPSignature
| ContentType::CMSSignature => { | ContentType::CMSSignature => {
self.mode = ViewMode::Attachment(lidx); self.mode = ViewMode::Attachment(lidx);
self.dirty = true; self.dirty = true;
} }
ContentType::Multipart { .. } => { ContentType::Multipart { .. } => {
context.replies.push_back(UIEvent::StatusEvent( context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage( StatusEvent::DisplayMessage(
"Multipart attachments are not supported yet.".to_string(), "Multipart attachments are not supported yet.".to_string(),
), ),
)); ));
return true; return true;
} }
ContentType::Other { .. } => { ContentType::Other { .. } => {
let attachment_type = u.mime_type(); let attachment_type = u.mime_type();
let filename = u.filename(); let filename = u.filename();
if let Ok(command) = query_default_app(&attachment_type) { if let Ok(command) = query_default_app(&attachment_type) {
let p = create_temp_file( let p = create_temp_file(
&decode(u, None), &decode(u, None),
filename.as_ref().map(|s| s.as_str()), filename.as_deref(),
None, None,
true, true,
); );
let (exec_cmd, argument) = super::desktop_exec_to_command( let (exec_cmd, argument) = super::desktop_exec_to_command(
&command, &command,
p.path.display().to_string(), p.path.display().to_string(),
false, false,
); );
match Command::new(&exec_cmd) match Command::new(&exec_cmd)
.arg(&argument) .arg(&argument)
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn() .spawn()
{ {
Ok(child) => { Ok(child) => {
context.temp_files.push(p); context.temp_files.push(p);
context.children.push(child); context.children.push(child);
}
Err(err) => {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!(
"Failed to start `{} {}`: {}",
&exec_cmd, &argument, err
)),
));
}
} }
} else { Err(err) => {
context.replies.push_back(UIEvent::StatusEvent( context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!(
"Failed to start `{} {}`: {}",
&exec_cmd, &argument, err
)),
));
}
}
} else {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(if let Some(filename) = filename.as_ref() { StatusEvent::DisplayMessage(if let Some(filename) = filename.as_ref() {
format!( format!(
"Couldn't find a default application for file {} (type {})", "Couldn't find a default application for file {} (type {})",
@ -445,29 +440,28 @@ impl Component for EnvelopeView {
attachment_type attachment_type
) )
}), }),
));
return true;
}
}
ContentType::OctetStream { .. } => {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(
"application/octet-stream isn't supported yet".to_string(),
),
)); ));
return true; return true;
} }
} }
} else { ContentType::OctetStream { .. } => {
context.replies.push_back(UIEvent::StatusEvent( context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!( StatusEvent::DisplayMessage(
"Attachment `{}` not found.", "application/octet-stream isn't supported yet".to_string(),
lidx ),
)), ));
)); return true;
return true; }
} }
}; } else {
context
.replies
.push_back(UIEvent::StatusEvent(StatusEvent::DisplayMessage(format!(
"Attachment `{}` not found.",
lidx
))));
return true;
}
return true; return true;
} }
UIEvent::Input(Key::Char('g')) UIEvent::Input(Key::Char('g'))
@ -492,22 +486,16 @@ impl Component for EnvelopeView {
} }
}; };
let url_launcher = context let url_launcher = context.settings.pager.url_launcher.as_deref().unwrap_or(
.settings #[cfg(target_os = "macos")]
.pager {
.url_launcher "open"
.as_ref() },
.map(|s| s.as_str()) #[cfg(not(target_os = "macos"))]
.unwrap_or( {
#[cfg(target_os = "macos")] "xdg-open"
{ },
"open" );
},
#[cfg(not(target_os = "macos"))]
{
"xdg-open"
},
);
match Command::new(url_launcher) match Command::new(url_launcher)
.arg(url) .arg(url)
.stdin(Stdio::piped()) .stdin(Stdio::piped())

View File

@ -415,11 +415,7 @@ impl ThreadView {
idx: usize, idx: usize,
context: &Context, context: &Context,
) { ) {
let visibles: Vec<&usize> = self let visibles: Vec<&usize> = self.visible_entries.iter().flat_map(|v| v.iter()).collect();
.visible_entries
.iter()
.flat_map(|ref v| v.iter())
.collect();
if idx == *visibles[self.cursor_pos] { if idx == *visibles[self.cursor_pos] {
let theme_default = crate::conf::value(context, "theme_default"); let theme_default = crate::conf::value(context, "theme_default");
let bg_color = crate::conf::value(context, "highlight").bg; let bg_color = crate::conf::value(context, "highlight").bg;
@ -514,11 +510,8 @@ impl ThreadView {
if page_no != prev_page_no { if page_no != prev_page_no {
clear_area(grid, area, crate::conf::value(context, "theme_default")); clear_area(grid, area, crate::conf::value(context, "theme_default"));
} }
let visibles: Vec<&usize> = self let visibles: Vec<&usize> =
.visible_entries self.visible_entries.iter().flat_map(|v| v.iter()).collect();
.iter()
.flat_map(|ref v| v.iter())
.collect();
for (visible_entry_counter, v) in visibles.iter().skip(top_idx).take(rows).enumerate() { for (visible_entry_counter, v) in visibles.iter().skip(top_idx).take(rows).enumerate() {
if visible_entry_counter >= rows { if visible_entry_counter >= rows {
@ -595,11 +588,8 @@ impl ThreadView {
self.cursor_pos = self.new_cursor_pos; self.cursor_pos = self.new_cursor_pos;
/* If cursor position has changed, remove the highlight from the previous position and /* If cursor position has changed, remove the highlight from the previous position and
* apply it in the new one. */ * apply it in the new one. */
let visibles: Vec<&usize> = self let visibles: Vec<&usize> =
.visible_entries self.visible_entries.iter().flat_map(|v| v.iter()).collect();
.iter()
.flat_map(|ref v| v.iter())
.collect();
for &idx in &[old_cursor_pos, self.cursor_pos] { for &idx in &[old_cursor_pos, self.cursor_pos] {
let entry_idx = *visibles[idx]; let entry_idx = *visibles[idx];
let src_area = { get_entry_area(entry_idx, &self.entries) }; let src_area = { get_entry_area(entry_idx, &self.entries) };
@ -948,11 +938,7 @@ impl ThreadView {
/// Current position in self.entries (not in drawn entries which might exclude nonvisible ones) /// Current position in self.entries (not in drawn entries which might exclude nonvisible ones)
fn current_pos(&self) -> usize { fn current_pos(&self) -> usize {
let visibles: Vec<&usize> = self let visibles: Vec<&usize> = self.visible_entries.iter().flat_map(|v| v.iter()).collect();
.visible_entries
.iter()
.flat_map(|ref v| v.iter())
.collect();
*visibles[self.new_cursor_pos] *visibles[self.new_cursor_pos]
} }
} }
@ -999,15 +985,12 @@ impl Component for ThreadView {
self.dirty = false; self.dirty = false;
} }
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool { fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
match event { if let UIEvent::Action(Listing(OpenInNewTab)) = event {
UIEvent::Action(Listing(OpenInNewTab)) => { /* Handle this before self.mailview does */
/* Handle this before self.mailview does */ context
context .replies
.replies .push_back(UIEvent::Action(Tab(New(Some(Box::new(self.clone()))))));
.push_back(UIEvent::Action(Tab(New(Some(Box::new(self.clone())))))); return true;
return true;
}
_ => {}
} }
if self.show_mailview && self.mailview.process_event(event, context) { if self.show_mailview && self.mailview.process_event(event, context) {

View File

@ -169,7 +169,7 @@ mod dbus {
} }
/// Passes notifications to a user defined shell command /// Passes notifications to a user defined shell command
#[derive(Debug)] #[derive(Default, Debug)]
pub struct NotificationCommand {} pub struct NotificationCommand {}
impl NotificationCommand { impl NotificationCommand {
@ -204,7 +204,7 @@ impl Component for NotificationCommand {
} }
Err(err) => { Err(err) => {
log( log(
format!("Could not run notification script: {}.", err.to_string()), format!("Could not run notification script: {}.", err),
ERROR, ERROR,
); );
debug!("Could not run notification script: {:?}", err); debug!("Could not run notification script: {:?}", err);
@ -226,10 +226,7 @@ impl Component for NotificationCommand {
} }
Err(err) => { Err(err) => {
log( log(
format!( format!("Could not run notification script: {}.", err),
"Could not run notification script: {}.",
err.to_string()
),
ERROR, ERROR,
); );
debug!("Could not run notification script: {:?}", err); debug!("Could not run notification script: {:?}", err);

View File

@ -67,7 +67,7 @@ pub struct StatusBar {
done_jobs: HashSet<JobId>, done_jobs: HashSet<JobId>,
scroll_contexts: IndexMap<ComponentId, ScrollContext>, scroll_contexts: IndexMap<ComponentId, ScrollContext>,
auto_complete: AutoComplete, auto_complete: Box<AutoComplete>,
cmd_history: Vec<String>, cmd_history: Vec<String>,
} }
@ -136,7 +136,7 @@ impl StatusBar {
.set_bg(attribute.bg) .set_bg(attribute.bg)
.set_attrs(attribute.attrs); .set_attrs(attribute.attrs);
} }
let offset = self.status.find('|').unwrap_or_else(|| self.status.len()); let offset = self.status.find('|').unwrap_or(self.status.len());
if y < get_y(bottom_right!(area)) + 1 { if y < get_y(bottom_right!(area)) + 1 {
for x in get_x(upper_left!(area)) for x in get_x(upper_left!(area))
..std::cmp::min( ..std::cmp::min(
@ -209,8 +209,7 @@ impl StatusBar {
.settings .settings
.terminal .terminal
.mouse_flag .mouse_flag
.as_ref() .as_deref()
.map(|s| s.as_str())
.unwrap_or("🖱️ ") .unwrap_or("🖱️ ")
} else { } else {
"" ""
@ -362,7 +361,7 @@ impl Component for StatusBar {
} }
suggestions.sort_by(|a, b| a.entry.cmp(&b.entry)); suggestions.sort_by(|a, b| a.entry.cmp(&b.entry));
suggestions.dedup_by(|a, b| &a.entry == &b.entry); suggestions.dedup_by(|a, b| a.entry == b.entry);
if self.auto_complete.set_suggestions(suggestions) { if self.auto_complete.set_suggestions(suggestions) {
let len = self.auto_complete.suggestions().len() - 1; let len = self.auto_complete.suggestions().len() - 1;
self.auto_complete.set_cursor(len); self.auto_complete.set_cursor(len);
@ -529,7 +528,7 @@ impl Component for StatusBar {
self.container.set_dirty(true); self.container.set_dirty(true);
} }
UIEvent::ChangeMode(m) => { UIEvent::ChangeMode(m) => {
let offset = self.status.find('|').unwrap_or_else(|| self.status.len()); let offset = self.status.find('|').unwrap_or(self.status.len());
self.status.replace_range( self.status.replace_range(
..offset, ..offset,
&format!( &format!(
@ -540,8 +539,7 @@ impl Component for StatusBar {
.settings .settings
.terminal .terminal
.mouse_flag .mouse_flag
.as_ref() .as_deref()
.map(|s| s.as_str())
.unwrap_or("🖱️ ") .unwrap_or("🖱️ ")
} else { } else {
"" ""
@ -559,7 +557,7 @@ impl Component for StatusBar {
.replies .replies
.push_back(UIEvent::Command(self.ex_buffer.as_str().to_string())); .push_back(UIEvent::Command(self.ex_buffer.as_str().to_string()));
} }
if parse_command(&self.ex_buffer.as_str().as_bytes()).is_ok() if parse_command(self.ex_buffer.as_str().as_bytes()).is_ok()
&& self.cmd_history.last().map(String::as_str) && self.cmd_history.last().map(String::as_str)
!= Some(self.ex_buffer.as_str()) != Some(self.ex_buffer.as_str())
{ {
@ -1138,7 +1136,7 @@ impl Component for Tabbed {
None, None,
); );
write_string_to_grid( write_string_to_grid(
&k, k,
&mut self.help_content, &mut self.help_content,
self.theme_default.fg, self.theme_default.fg,
self.theme_default.bg, self.theme_default.bg,
@ -1485,13 +1483,13 @@ impl Component for Tabbed {
self.help_screen_cursor.1 = self.help_screen_cursor.1.saturating_sub(1); self.help_screen_cursor.1 = self.help_screen_cursor.1.saturating_sub(1);
} }
_ if shortcut!(key == shortcuts["general"]["scroll_down"]) => { _ if shortcut!(key == shortcuts["general"]["scroll_down"]) => {
self.help_screen_cursor.1 = self.help_screen_cursor.1 + 1; self.help_screen_cursor.1 += 1;
} }
_ if shortcut!(key == shortcuts["general"]["scroll_left"]) => { _ if shortcut!(key == shortcuts["general"]["scroll_left"]) => {
self.help_screen_cursor.0 = self.help_screen_cursor.0.saturating_sub(1); self.help_screen_cursor.0 = self.help_screen_cursor.0.saturating_sub(1);
} }
_ if shortcut!(key == shortcuts["general"]["scroll_right"]) => { _ if shortcut!(key == shortcuts["general"]["scroll_right"]) => {
self.help_screen_cursor.0 = self.help_screen_cursor.0 + 1; self.help_screen_cursor.0 += 1;
} }
_ => { _ => {
/* ignore, don't pass to components below the shortcut panel */ /* ignore, don't pass to components below the shortcut panel */
@ -1600,7 +1598,7 @@ impl Component for RawBuffer {
true true
} }
UIEvent::Input(Key::Right) => { UIEvent::Input(Key::Right) => {
self.cursor.0 = self.cursor.0 + 1; self.cursor.0 += 1;
self.dirty = true; self.dirty = true;
true true
} }
@ -1610,7 +1608,7 @@ impl Component for RawBuffer {
true true
} }
UIEvent::Input(Key::Down) => { UIEvent::Input(Key::Down) => {
self.cursor.1 = self.cursor.1 + 1; self.cursor.1 += 1;
self.dirty = true; self.dirty = true;
true true
} }
@ -1641,9 +1639,6 @@ impl RawBuffer {
} }
} }
pub fn title(&self) -> &str { pub fn title(&self) -> &str {
self.title self.title.as_deref().unwrap_or("untitled")
.as_ref()
.map(String::as_str)
.unwrap_or("untitled")
} }
} }

View File

@ -760,7 +760,7 @@ impl<T: PartialEq + Debug + Clone + Sync + Send, F: 'static + Sync + Send> Selec
) -> Selector<T, F> { ) -> Selector<T, F> {
let entry_titles = entries let entry_titles = entries
.iter_mut() .iter_mut()
.map(|(_id, ref mut title)| std::mem::replace(title, String::new())) .map(|(_id, ref mut title)| std::mem::take(title))
.collect::<Vec<String>>(); .collect::<Vec<String>>();
let mut identifiers: Vec<(T, bool)> = let mut identifiers: Vec<(T, bool)> =
entries.into_iter().map(|(id, _)| (id, false)).collect(); entries.into_iter().map(|(id, _)| (id, false)).collect();
@ -812,7 +812,7 @@ impl<T: PartialEq + Debug + Clone + Sync + Send, F: 'static + Sync + Send> Selec
if self.single_only { if self.single_only {
for (i, e) in self.entry_titles.iter().enumerate() { for (i, e) in self.entry_titles.iter().enumerate() {
write_string_to_grid( write_string_to_grid(
&e, e,
&mut content, &mut content,
self.theme_default.fg, self.theme_default.fg,
self.theme_default.bg, self.theme_default.bg,
@ -824,7 +824,7 @@ impl<T: PartialEq + Debug + Clone + Sync + Send, F: 'static + Sync + Send> Selec
} else { } else {
for (i, e) in self.entry_titles.iter().enumerate() { for (i, e) in self.entry_titles.iter().enumerate() {
write_string_to_grid( write_string_to_grid(
&format!("[ ] {}", &e), &format!("[ ] {}", e),
&mut content, &mut content,
self.theme_default.fg, self.theme_default.fg,
self.theme_default.bg, self.theme_default.bg,

View File

@ -39,7 +39,6 @@ pub struct Pager {
colors: ThemeAttribute, colors: ThemeAttribute,
initialised: bool, initialised: bool,
show_scrollbar: bool, show_scrollbar: bool,
content: CellBuffer,
filtered_content: Option<(String, Result<CellBuffer>)>, filtered_content: Option<(String, Result<CellBuffer>)>,
text_lines: Vec<String>, text_lines: Vec<String>,
line_breaker: LineBreakText, line_breaker: LineBreakText,
@ -57,8 +56,10 @@ impl Pager {
pub const DESCRIPTION: &'static str = "pager"; pub const DESCRIPTION: &'static str = "pager";
const PAGES_AHEAD_TO_RENDER_NO: usize = 16; const PAGES_AHEAD_TO_RENDER_NO: usize = 16;
pub fn new(context: &Context) -> Self { pub fn new(context: &Context) -> Self {
let mut ret = Pager::default(); let mut ret = Pager {
ret.minimum_width = context.settings.pager.minimum_width; minimum_width: context.settings.pager.minimum_width,
..Pager::default()
};
ret.set_colors(crate::conf::value(context, "theme_default")) ret.set_colors(crate::conf::value(context, "theme_default"))
.set_reflow(if context.settings.pager.split_long_lines { .set_reflow(if context.settings.pager.split_long_lines {
Reflow::All Reflow::All
@ -178,10 +179,7 @@ impl Pager {
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn() .spawn()
.chain_err_summary(|| "Failed to start pager filter process")?; .chain_err_summary(|| "Failed to start pager filter process")?;
let stdin = filter_child let stdin = filter_child.stdin.as_mut().ok_or("failed to open stdin")?;
.stdin
.as_mut()
.ok_or_else(|| "failed to open stdin")?;
stdin stdin
.write_all(text.as_bytes()) .write_all(text.as_bytes())
.chain_err_summary(|| "Failed to write to stdin")?; .chain_err_summary(|| "Failed to write to stdin")?;
@ -196,7 +194,7 @@ impl Pager {
for b in out { for b in out {
embedded.process_byte(&mut dev_null, b); embedded.process_byte(&mut dev_null, b);
} }
Ok(std::mem::replace(embedded.buffer_mut(), Default::default())) Ok(std::mem::take(embedded.buffer_mut()))
}; };
let buf = _f(cmd, &self.text); let buf = _f(cmd, &self.text);
if let Some((width, height)) = buf.as_ref().ok().map(CellBuffer::size) { if let Some((width, height)) = buf.as_ref().ok().map(CellBuffer::size) {
@ -301,7 +299,7 @@ impl Pager {
Ok(ref content) => { Ok(ref content) => {
copy_area( copy_area(
grid, grid,
&content, content,
area, area,
( (
( (
@ -353,7 +351,7 @@ impl Pager {
(upper_left, bottom_right), (upper_left, bottom_right),
None, None,
); );
if l.starts_with("") { if l.starts_with('⤷') {
grid[upper_left] grid[upper_left]
.set_fg(crate::conf::value(context, "highlight").fg) .set_fg(crate::conf::value(context, "highlight").fg)
.set_attrs(crate::conf::value(context, "highlight").attrs); .set_attrs(crate::conf::value(context, "highlight").attrs);

View File

@ -42,7 +42,7 @@ impl Default for FormFocus {
} }
pub enum Field { pub enum Field {
Text(UText, Option<(AutoCompleteFn, AutoComplete)>), Text(UText, Option<(AutoCompleteFn, Box<AutoComplete>)>),
Choice(Vec<Cow<'static, str>>, Cursor), Choice(Vec<Cow<'static, str>>, Cursor),
} }
@ -456,6 +456,10 @@ impl<T: 'static + std::fmt::Debug + Copy + Default + Send + Sync> FormWidget<T>
self.layout.len() self.layout.len()
} }
pub fn is_empty(&self) -> bool {
self.layout.len() == 0
}
pub fn add_button(&mut self, val: (Cow<'static, str>, T)) { pub fn add_button(&mut self, val: (Cow<'static, str>, T)) {
self.buttons.push(val); self.buttons.push(val);
} }
@ -1003,7 +1007,7 @@ impl Component for AutoComplete {
} }
impl AutoComplete { impl AutoComplete {
pub fn new(entries: Vec<AutoCompleteEntry>) -> Self { pub fn new(entries: Vec<AutoCompleteEntry>) -> Box<Self> {
let mut ret = AutoComplete { let mut ret = AutoComplete {
entries: Vec::new(), entries: Vec::new(),
content: CellBuffer::default(), content: CellBuffer::default(),
@ -1012,7 +1016,7 @@ impl AutoComplete {
id: ComponentId::new_v4(), id: ComponentId::new_v4(),
}; };
ret.set_suggestions(entries); ret.set_suggestions(entries);
ret Box::new(ret)
} }
pub fn set_suggestions(&mut self, entries: Vec<AutoCompleteEntry>) -> bool { pub fn set_suggestions(&mut self, entries: Vec<AutoCompleteEntry>) -> bool {
@ -1401,7 +1405,7 @@ impl Component for ProgressSpinner {
if self.active { if self.active {
write_string_to_grid( write_string_to_grid(
match self.kind.as_ref() { match self.kind.as_ref() {
Ok(kind) => (Self::KINDS[*kind].1)[self.stage].as_ref(), Ok(kind) => (Self::KINDS[*kind].1)[self.stage],
Err(custom) => custom[self.stage].as_ref(), Err(custom) => custom[self.stage].as_ref(),
}, },
grid, grid,

View File

@ -250,7 +250,7 @@ impl From<FileAccount> for AccountConf {
let root_mailbox = x.root_mailbox.clone(); let root_mailbox = x.root_mailbox.clone();
let identity = x.identity.clone(); let identity = x.identity.clone();
let display_name = x.display_name.clone(); let display_name = x.display_name.clone();
let order = x.order.clone(); let order = x.order;
let mailboxes = x let mailboxes = x
.mailboxes .mailboxes
.iter() .iter()
@ -365,13 +365,14 @@ impl FileSettings {
pub fn validate(path: PathBuf, interactive: bool, clear_extras: bool) -> Result<Self> { pub fn validate(path: PathBuf, interactive: bool, clear_extras: bool) -> Result<Self> {
let s = pp::pp(&path)?; let s = pp::pp(&path)?;
let map: toml::map::Map<String, toml::value::Value> = toml::from_str(&s).map_err(|e| { let map: toml::map::Map<String, toml::value::Value> =
MeliError::new(format!( toml::from_str(&s).map_err(|err| {
"{}:\nConfig file is invalid TOML: {}", MeliError::new(format!(
path.display(), "{}:\nConfig file is invalid TOML: {}",
e.to_string() path.display(),
)) err
})?; ))
})?;
/* /*
* Check that a global composing option is set and return a user-friendly error message because the * Check that a global composing option is set and return a user-friendly error message because the
* default serde one is confusing. * default serde one is confusing.
@ -410,11 +411,11 @@ This is required so that you don't accidentally start meli and find out later th
path.display() path.display()
))); )));
} }
let mut s: FileSettings = toml::from_str(&s).map_err(|e| { let mut s: FileSettings = toml::from_str(&s).map_err(|err| {
MeliError::new(format!( MeliError::new(format!(
"{}:\nConfig file contains errors: {}", "{}:\nConfig file contains errors: {}",
path.display(), path.display(),
e.to_string() err
)) ))
})?; })?;
let backends = melib::backends::Backends::new(); let backends = melib::backends::Backends::new();
@ -831,7 +832,7 @@ mod pp {
} }
i += 1; i += 1;
} }
return Ok(("", None)); Ok(("", None))
} }
} }

View File

@ -89,18 +89,11 @@ impl Default for MailboxStatus {
impl MailboxStatus { impl MailboxStatus {
pub fn is_available(&self) -> bool { pub fn is_available(&self) -> bool {
if let MailboxStatus::Available = self { matches!(self, MailboxStatus::Available)
true
} else {
false
}
} }
pub fn is_parsing(&self) -> bool { pub fn is_parsing(&self) -> bool {
if let MailboxStatus::Parsing(_, _) = self { matches!(self, MailboxStatus::Parsing(_, _))
true
} else {
false
}
} }
} }
@ -132,7 +125,7 @@ impl MailboxEntry {
if let Some(name) = self.conf.mailbox_conf.alias.as_ref() { if let Some(name) = self.conf.mailbox_conf.alias.as_ref() {
name name
} else { } else {
&self.ref_mailbox.name() self.ref_mailbox.name()
} }
} }
} }
@ -165,6 +158,7 @@ pub enum JobRequest {
}, },
Fetch { Fetch {
mailbox_hash: MailboxHash, mailbox_hash: MailboxHash,
#[allow(clippy::type_complexity)]
handle: JoinHandle<( handle: JoinHandle<(
Option<Result<Vec<Envelope>>>, Option<Result<Vec<Envelope>>>,
Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>, Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>,
@ -345,26 +339,17 @@ impl core::fmt::Display for JobRequest {
impl JobRequest { impl JobRequest {
pub fn is_watch(&self) -> bool { pub fn is_watch(&self) -> bool {
match self { matches!(self, JobRequest::Watch { .. })
JobRequest::Watch { .. } => true,
_ => false,
}
} }
pub fn is_fetch(&self, mailbox_hash: MailboxHash) -> bool { pub fn is_fetch(&self, mailbox_hash: MailboxHash) -> bool {
match self { matches!(self, JobRequest::Fetch {
JobRequest::Fetch { mailbox_hash: h, ..
mailbox_hash: h, .. } if *h == mailbox_hash)
} if *h == mailbox_hash => true,
_ => false,
}
} }
pub fn is_online(&self) -> bool { pub fn is_online(&self) -> bool {
match self { matches!(self, JobRequest::IsOnline { .. })
JobRequest::IsOnline { .. } => true,
_ => false,
}
} }
} }
@ -388,7 +373,6 @@ impl Drop for Account {
let writer = io::BufWriter::new(f); let writer = io::BufWriter::new(f);
if let Err(err) = serde_json::to_writer(writer, &self.address_book) { if let Err(err) = serde_json::to_writer(writer, &self.address_book) {
eprintln!("{}", err); eprintln!("{}", err);
return;
}; };
}; };
/* /*
@ -455,7 +439,7 @@ impl Account {
)?; )?;
let data_dir = xdg::BaseDirectories::with_profile("meli", &name).unwrap(); let data_dir = xdg::BaseDirectories::with_profile("meli", &name).unwrap();
let mut address_book = AddressBook::with_account(&settings.account()); let mut address_book = AddressBook::with_account(settings.account());
if let Ok(data) = data_dir.place_data_file("addressbook") { if let Ok(data) = data_dir.place_data_file("addressbook") {
if data.exists() { if data.exists() {
@ -730,7 +714,7 @@ impl Account {
format!( format!(
"Failed to update envelope {} in cache: {}", "Failed to update envelope {} in cache: {}",
envelope.message_id_display(), envelope.message_id_display(),
err.to_string() err
), ),
melib::ERROR, melib::ERROR,
); );
@ -804,7 +788,7 @@ impl Account {
"Failed to update envelope {} in cache: {}", "Failed to update envelope {} in cache: {}",
self.collection.envelopes.read().unwrap()[&env_hash] self.collection.envelopes.read().unwrap()[&env_hash]
.message_id_display(), .message_id_display(),
err.to_string() err
), ),
melib::ERROR, melib::ERROR,
); );
@ -834,7 +818,7 @@ impl Account {
"Failed to update envelope {} in cache: {}", "Failed to update envelope {} in cache: {}",
&self.collection.envelopes.read().unwrap()[&new_hash] &self.collection.envelopes.read().unwrap()[&new_hash]
.message_id_display(), .message_id_display(),
err.to_string() err
), ),
melib::ERROR, melib::ERROR,
); );
@ -961,7 +945,7 @@ impl Account {
"Failed to remove envelope {} [{}] in cache: {}", "Failed to remove envelope {} [{}] in cache: {}",
&envelopes[&env_hash].message_id_display(), &envelopes[&env_hash].message_id_display(),
env_hash, env_hash,
err.to_string() err
), ),
melib::ERROR, melib::ERROR,
); );
@ -1193,14 +1177,10 @@ impl Account {
self.special_use_mailbox(SpecialUsageMailbox::Normal), self.special_use_mailbox(SpecialUsageMailbox::Normal),
] { ] {
if let Some(mailbox_hash) = mailbox { if let Some(mailbox_hash) = mailbox {
if let Err(e) = self.save(bytes, *mailbox_hash, Some(flags)) { if let Err(err) = self.save(bytes, *mailbox_hash, Some(flags)) {
debug!("{:?} could not save msg", e); debug!("{:?} could not save msg", err);
melib::log( melib::log(
format!( format!("Could not save in '{}' mailbox: {}.", *mailbox_hash, err),
"Could not save in '{}' mailbox: {}.",
*mailbox_hash,
e.to_string()
),
melib::ERROR, melib::ERROR,
); );
} else { } else {
@ -1311,9 +1291,7 @@ impl Account {
) )
}; };
melib::log(&error_message, melib::LoggingLevel::ERROR); melib::log(&error_message, melib::LoggingLevel::ERROR);
return Err( return Err(MeliError::new(error_message).set_summary("Message not sent."));
MeliError::new(error_message.clone()).set_summary("Message not sent.")
);
} }
Ok(None) Ok(None)
} }
@ -1334,11 +1312,11 @@ impl Account {
} }
SendMail::ServerSubmission => { SendMail::ServerSubmission => {
if self.backend_capabilities.supports_submission { if self.backend_capabilities.supports_submission {
let job = self.backend.write().unwrap().submit( let job =
message.clone().into_bytes(), self.backend
None, .write()
None, .unwrap()
)?; .submit(message.into_bytes(), None, None)?;
let handle = if self.backend_capabilities.is_async { let handle = if self.backend_capabilities.is_async {
self.job_executor.spawn_specialized(job) self.job_executor.spawn_specialized(job)
@ -1348,8 +1326,8 @@ impl Account {
self.insert_job(handle.job_id, JobRequest::SendMessageBackground { handle }); self.insert_job(handle.job_id, JobRequest::SendMessageBackground { handle });
return Ok(None); return Ok(None);
} }
return Err(MeliError::new("Server does not support submission.") Err(MeliError::new("Server does not support submission.")
.set_summary("Message not sent.")); .set_summary("Message not sent."))
} }
} }
} }
@ -1400,8 +1378,9 @@ impl Account {
) )
}; };
melib::log(&error_message, melib::LoggingLevel::ERROR); melib::log(&error_message, melib::LoggingLevel::ERROR);
return Err(MeliError::new(error_message.clone()) return Err(
.set_summary("Message not sent.")); MeliError::new(error_message).set_summary("Message not sent.")
);
} }
Ok(()) Ok(())
} }
@ -1423,8 +1402,8 @@ impl Account {
fut.await?; fut.await?;
return Ok(()); return Ok(());
} }
return Err(MeliError::new("Server does not support submission.") Err(MeliError::new("Server does not support submission.")
.set_summary("Message not sent.")); .set_summary("Message not sent."))
} }
} }
}) })
@ -1537,11 +1516,7 @@ impl Account {
.mailbox_entries .mailbox_entries
.iter() .iter()
.find(|(_, f)| f.conf.mailbox_conf().usage == Some(special_use)); .find(|(_, f)| f.conf.mailbox_conf().usage == Some(special_use));
if let Some(ret) = ret.as_ref() { ret.as_ref().map(|ret| ret.1.ref_mailbox.hash())
Some(ret.1.ref_mailbox.hash())
} else {
None
}
} }
/* Call only in Context::is_online, since only Context can launch the watcher threads if an /* Call only in Context::is_online, since only Context can launch the watcher threads if an
@ -1575,7 +1550,7 @@ impl Account {
self.insert_job(handle.job_id, JobRequest::IsOnline { handle }); self.insert_job(handle.job_id, JobRequest::IsOnline { handle });
} }
} }
return self.is_online.clone(); self.is_online.clone()
} }
pub fn search( pub fn search(
@ -2272,7 +2247,7 @@ fn build_mailboxes_order(
node node
} }
tree.push(rec(*h, &mailbox_entries, 0)); tree.push(rec(*h, mailbox_entries, 0));
} }
} }
@ -2356,7 +2331,6 @@ fn build_mailboxes_order(
fn rec( fn rec(
node: &mut MailboxNode, node: &mut MailboxNode,
mailbox_entries: &IndexMap<MailboxHash, MailboxEntry>, mailbox_entries: &IndexMap<MailboxHash, MailboxEntry>,
depth: usize,
mut indentation: u32, mut indentation: u32,
has_sibling: bool, has_sibling: bool,
) { ) {
@ -2379,16 +2353,10 @@ fn build_mailboxes_order(
} }
while let Some(i) = iter.next() { while let Some(i) = iter.next() {
let c = &mut node.children[i]; let c = &mut node.children[i];
rec( rec(c, mailbox_entries, indentation, iter.peek() != None);
c,
mailbox_entries,
depth + 1,
indentation,
iter.peek() != None,
);
} }
} }
rec(node, &mailbox_entries, 1, 0, false); rec(node, mailbox_entries, 0, false);
} }
} }

View File

@ -19,7 +19,9 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
//! This module is automatically generated by build.rs. #![allow(clippy::derivable_impls)]
//! This module is automatically generated by config_macros.rs.
use super::*; use super::*;
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]

View File

@ -34,7 +34,7 @@ macro_rules! shortcut {
}; };
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Default, Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Shortcuts { pub struct Shortcuts {
#[serde(default)] #[serde(default)]
@ -55,21 +55,6 @@ pub struct Shortcuts {
pub pager: PagerShortcuts, pub 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(),
}
}
}
impl DotAddressable for Shortcuts { impl DotAddressable for Shortcuts {
fn lookup(&self, parent_field: &str, path: &[&str]) -> Result<String> { fn lookup(&self, parent_field: &str, path: &[&str]) -> Result<String> {
match path.first() { match path.first() {

View File

@ -28,7 +28,7 @@ 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)] #[derive(Default, Debug, Deserialize, Clone, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct TagsSettings { pub struct TagsSettings {
#[serde(default, deserialize_with = "tag_color_de")] #[serde(default, deserialize_with = "tag_color_de")]
@ -37,15 +37,6 @@ pub struct TagsSettings {
pub ignore_tags: HashSet<u64>, pub ignore_tags: HashSet<u64>,
} }
impl Default for TagsSettings {
fn default() -> Self {
TagsSettings {
colors: Default::default(),
ignore_tags: Default::default(),
}
}
}
pub fn tag_set_de<'de, D, T: std::convert::From<HashSet<u64>>>( pub fn tag_set_de<'de, D, T: std::convert::From<HashSet<u64>>>(
deserializer: D, deserializer: D,
) -> std::result::Result<T, D::Error> ) -> std::result::Result<T, D::Error>

View File

@ -102,7 +102,7 @@ pub fn attrs(context: &Context, key: &'static str) -> Attr {
} }
#[inline(always)] #[inline(always)]
fn unlink<'k, 't: 'k>(theme: &'t Theme, key: &'k Cow<'static, str>) -> ThemeAttribute { fn unlink<'k, 't: 'k>(theme: &'t Theme, key: &'k str) -> ThemeAttribute {
ThemeAttribute { ThemeAttribute {
fg: unlink_fg(theme, &ColorField::Fg, key), fg: unlink_fg(theme, &ColorField::Fg, key),
bg: unlink_bg(theme, &ColorField::Bg, key), bg: unlink_bg(theme, &ColorField::Bg, key),
@ -111,11 +111,7 @@ fn unlink<'k, 't: 'k>(theme: &'t Theme, key: &'k Cow<'static, str>) -> ThemeAttr
} }
#[inline(always)] #[inline(always)]
fn unlink_fg<'k, 't: 'k>( fn unlink_fg<'k, 't: 'k>(theme: &'t Theme, mut field: &'k ColorField, mut key: &'k str) -> Color {
theme: &'t Theme,
mut field: &'k ColorField,
mut key: &'k Cow<'static, str>,
) -> Color {
loop { loop {
match field { match field {
ColorField::LikeSelf | ColorField::Fg => match &theme[key].fg { ColorField::LikeSelf | ColorField::Fg => match &theme[key].fg {
@ -166,11 +162,7 @@ fn unlink_fg<'k, 't: 'k>(
} }
#[inline(always)] #[inline(always)]
fn unlink_bg<'k, 't: 'k>( fn unlink_bg<'k, 't: 'k>(theme: &'t Theme, mut field: &'k ColorField, mut key: &'k str) -> Color {
theme: &'t Theme,
mut field: &'k ColorField,
mut key: &'k Cow<'static, str>,
) -> Color {
loop { loop {
match field { match field {
ColorField::LikeSelf | ColorField::Bg => match &theme[key].bg { ColorField::LikeSelf | ColorField::Bg => match &theme[key].bg {
@ -220,7 +212,7 @@ fn unlink_bg<'k, 't: 'k>(
} }
#[inline(always)] #[inline(always)]
fn unlink_attrs<'k, 't: 'k>(theme: &'t Theme, mut key: &'k Cow<'static, str>) -> Attr { fn unlink_attrs<'k, 't: 'k>(theme: &'t Theme, mut key: &'k str) -> Attr {
loop { loop {
match &theme[key].attrs { match &theme[key].attrs {
ThemeValue::Link(ref new_key, ()) => key = new_key, ThemeValue::Link(ref new_key, ()) => key = new_key,
@ -416,8 +408,8 @@ impl<'de> Deserialize<'de> for ThemeValue<Attr> {
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
if let Ok(s) = <String>::deserialize(deserializer) { if let Ok(s) = <String>::deserialize(deserializer) {
if s.starts_with("$") { if let Some(stripped) = s.strip_prefix('$') {
Ok(ThemeValue::Alias(s[1..].to_string().into())) Ok(ThemeValue::Alias(stripped.to_string().into()))
} else if let Ok(c) = Attr::from_string_de::<'de, D, String>(s.clone()) { } else if let Ok(c) = Attr::from_string_de::<'de, D, String>(s.clone()) {
Ok(ThemeValue::Value(c)) Ok(ThemeValue::Value(c))
} else { } else {
@ -467,8 +459,8 @@ impl<'de> Deserialize<'de> for ThemeValue<Color> {
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
if let Ok(s) = <String>::deserialize(deserializer) { if let Ok(s) = <String>::deserialize(deserializer) {
if s.starts_with("$") { if let Some(stripped) = s.strip_prefix('$') {
Ok(ThemeValue::Alias(s[1..].to_string().into())) Ok(ThemeValue::Alias(stripped.to_string().into()))
} else if let Ok(c) = Color::from_string_de::<'de, D>(s.clone()) { } else if let Ok(c) = Color::from_string_de::<'de, D>(s.clone()) {
Ok(ThemeValue::Value(c)) Ok(ThemeValue::Value(c))
} else if s.ends_with(".fg") { } else if s.ends_with(".fg") {
@ -606,37 +598,26 @@ mod regexp {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
loop { loop {
let next_byte_offset = self.pcre_iter.next(); let next_byte_offset = self.pcre_iter.next()?;
if next_byte_offset.is_none() {
return None;
}
let next_byte_offset = next_byte_offset.unwrap();
if next_byte_offset.is_err() { if next_byte_offset.is_err() {
continue; continue;
} }
let next_byte_offset = next_byte_offset.unwrap(); let next_byte_offset = next_byte_offset.unwrap();
let mut next_char_index = self.char_indices.next(); let mut next_char_index = self.char_indices.next()?;
if next_char_index.is_none() {
return None;
}
while next_byte_offset.start() < next_char_index.unwrap().0 { while next_byte_offset.start() < next_char_index.0 {
self.char_offset += 1; self.char_offset += 1;
next_char_index = self.char_indices.next(); next_char_index = self.char_indices.next()?;
if next_char_index.is_none() {
return None;
}
} }
let start = self.char_offset; let start = self.char_offset;
while next_byte_offset.end() while next_byte_offset.end()
>= self > self
.char_indices .char_indices
.next() .next()
.map(|(v, _)| v) .map(|(v, _)| v)
.unwrap_or(next_byte_offset.end()) .unwrap_or_else(|| next_byte_offset.end())
+ 1
{ {
self.char_offset += 1; self.char_offset += 1;
} }
@ -1219,7 +1200,7 @@ impl Themes {
Ok(()) Ok(())
} }
pub fn validate(&self) -> Result<()> { pub fn validate(&self) -> Result<()> {
let hash_set: HashSet<&'static str> = DEFAULT_KEYS.into_iter().map(|k| *k).collect(); let hash_set: HashSet<&'static str> = DEFAULT_KEYS.iter().copied().collect();
Themes::validate_keys("light", &self.light, &hash_set)?; Themes::validate_keys("light", &self.light, &hash_set)?;
Themes::validate_keys("dark", &self.dark, &hash_set)?; Themes::validate_keys("dark", &self.dark, &hash_set)?;
for (name, t) in self.other_themes.iter() { for (name, t) in self.other_themes.iter() {
@ -1255,51 +1236,49 @@ impl Themes {
t => self.other_themes.get(t).unwrap_or(&self.dark), t => self.other_themes.get(t).unwrap_or(&self.dark),
}; };
let mut ret = String::new(); let mut ret = String::new();
ret.extend(format!("[terminal.themes.{}]\n", key).chars()); ret.push_str(&format!("[terminal.themes.{}]\n", key));
if unlink { if unlink {
for k in theme.keys() { for k in theme.keys() {
ret.extend( ret.push_str(&format!(
format!( "\"{}\" = {{ fg = {}, bg = {}, attrs = {} }}\n",
"\"{}\" = {{ fg = {}, bg = {}, attrs = {} }}\n", k,
k, toml::to_string(&unlink_fg(theme, &ColorField::Fg, k)).unwrap(),
toml::to_string(&unlink_fg(&theme, &ColorField::Fg, k)).unwrap(), toml::to_string(&unlink_bg(theme, &ColorField::Bg, k)).unwrap(),
toml::to_string(&unlink_bg(&theme, &ColorField::Bg, k)).unwrap(), toml::to_string(&unlink_attrs(theme, k)).unwrap(),
toml::to_string(&unlink_attrs(&theme, k)).unwrap(), ));
)
.chars(),
);
} }
} else { } else {
for k in theme.keys() { for k in theme.keys() {
ret.extend( ret.push_str(&format!(
format!( "\"{}\" = {{ fg = {}, bg = {}, attrs = {} }}\n",
"\"{}\" = {{ fg = {}, bg = {}, attrs = {} }}\n", k,
k, toml::to_string(&theme[k].fg).unwrap(),
toml::to_string(&theme[k].fg).unwrap(), toml::to_string(&theme[k].bg).unwrap(),
toml::to_string(&theme[k].bg).unwrap(), toml::to_string(&theme[k].attrs).unwrap(),
toml::to_string(&theme[k].attrs).unwrap(), ));
)
.chars(),
);
} }
} }
ret ret
} }
pub fn to_string(&self) -> String { }
let mut ret = String::new();
ret.extend(self.key_to_string("dark", true).chars()); impl std::fmt::Display for Themes {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
ret.push_str("\n\n"); let mut ret = String::new();
ret.extend(self.key_to_string("light", true).chars()); ret.push_str(&self.key_to_string("dark", true));
for name in self.other_themes.keys() {
ret.push_str("\n\n"); ret.push_str("\n\n");
ret.extend(self.key_to_string(name, true).chars()); ret.push_str(&self.key_to_string("light", true));
} for name in self.other_themes.keys() {
ret ret.push_str("\n\n");
} ret.push_str(&self.key_to_string(name, true));
}
write!(fmt, "{}", ret)
}
} }
impl Default for Themes { impl Default for Themes {
#[allow(clippy::needless_update)]
fn default() -> Themes { fn default() -> Themes {
let mut light = IndexMap::default(); let mut light = IndexMap::default();
let mut dark = IndexMap::default(); let mut dark = IndexMap::default();
@ -1762,9 +1741,9 @@ impl Serialize for Themes {
new_map.insert( new_map.insert(
k.clone(), k.clone(),
ThemeAttribute { ThemeAttribute {
fg: unlink_fg(&t, &ColorField::Fg, k), fg: unlink_fg(t, &ColorField::Fg, k),
bg: unlink_bg(&t, &ColorField::Bg, k), bg: unlink_bg(t, &ColorField::Bg, k),
attrs: unlink_attrs(&t, k), attrs: unlink_attrs(t, k),
}, },
); );
} }
@ -1790,10 +1769,10 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
} }
fn is_cyclic_util<'a>( fn is_cyclic_util<'a>(
course: Course, course: Course,
k: &'a Cow<'static, str>, k: &'a str,
visited: &mut IndexMap<(&'a Cow<'static, str>, Course), bool>, visited: &mut IndexMap<(&'a str, Course), bool>,
stack: &mut IndexMap<(&'a Cow<'static, str>, Course), bool>, stack: &mut IndexMap<(&'a str, Course), bool>,
path: &mut SmallVec<[(&'a Cow<'static, str>, Course); 16]>, path: &mut SmallVec<[(&'a str, Course); 16]>,
theme: &'a Theme, theme: &'a Theme,
) -> bool { ) -> bool {
if !visited[&(k, course)] { if !visited[&(k, course)] {
@ -1804,6 +1783,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
Course::Fg => match theme[k].fg { Course::Fg => match theme[k].fg {
ThemeValue::Link(ref l, ColorField::LikeSelf) ThemeValue::Link(ref l, ColorField::LikeSelf)
| ThemeValue::Link(ref l, ColorField::Fg) => { | ThemeValue::Link(ref l, ColorField::Fg) => {
let l = l.as_ref();
path.push((l, Course::Fg)); path.push((l, Course::Fg));
if (!visited[&(l, Course::Fg)] if (!visited[&(l, Course::Fg)]
&& is_cyclic_util(course, l, visited, stack, path, theme)) && is_cyclic_util(course, l, visited, stack, path, theme))
@ -1814,6 +1794,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
path.pop(); path.pop();
} }
ThemeValue::Link(ref l, ColorField::Bg) => { ThemeValue::Link(ref l, ColorField::Bg) => {
let l = l.as_ref();
path.push((l, Course::Bg)); path.push((l, Course::Bg));
if (!visited[&(l, Course::Bg)] if (!visited[&(l, Course::Bg)]
&& is_cyclic_util(Course::Bg, l, visited, stack, path, theme)) && is_cyclic_util(Course::Bg, l, visited, stack, path, theme))
@ -1824,6 +1805,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
path.pop(); path.pop();
} }
ThemeValue::Alias(ref ident) => { ThemeValue::Alias(ref ident) => {
let ident = ident.as_ref();
path.push((ident, Course::ColorAliasFg)); path.push((ident, Course::ColorAliasFg));
if (!visited[&(ident, Course::ColorAliasFg)] if (!visited[&(ident, Course::ColorAliasFg)]
&& is_cyclic_util( && is_cyclic_util(
@ -1845,6 +1827,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
Course::Bg => match theme[k].bg { Course::Bg => match theme[k].bg {
ThemeValue::Link(ref l, ColorField::LikeSelf) ThemeValue::Link(ref l, ColorField::LikeSelf)
| ThemeValue::Link(ref l, ColorField::Bg) => { | ThemeValue::Link(ref l, ColorField::Bg) => {
let l = l.as_ref();
path.push((l, Course::Bg)); path.push((l, Course::Bg));
if (!visited[&(l, Course::Bg)] if (!visited[&(l, Course::Bg)]
&& is_cyclic_util(Course::Bg, l, visited, stack, path, theme)) && is_cyclic_util(Course::Bg, l, visited, stack, path, theme))
@ -1855,6 +1838,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
path.pop(); path.pop();
} }
ThemeValue::Link(ref l, ColorField::Fg) => { ThemeValue::Link(ref l, ColorField::Fg) => {
let l = l.as_ref();
path.push((l, Course::Fg)); path.push((l, Course::Fg));
if (!visited[&(l, Course::Fg)] if (!visited[&(l, Course::Fg)]
&& is_cyclic_util(Course::Fg, l, visited, stack, path, theme)) && is_cyclic_util(Course::Fg, l, visited, stack, path, theme))
@ -1865,6 +1849,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
path.pop(); path.pop();
} }
ThemeValue::Alias(ref ident) => { ThemeValue::Alias(ref ident) => {
let ident = ident.as_ref();
path.push((ident, Course::ColorAliasBg)); path.push((ident, Course::ColorAliasBg));
if (!visited[&(ident, Course::ColorAliasBg)] if (!visited[&(ident, Course::ColorAliasBg)]
&& is_cyclic_util( && is_cyclic_util(
@ -1885,6 +1870,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
}, },
Course::Attrs => match theme[k].attrs { Course::Attrs => match theme[k].attrs {
ThemeValue::Link(ref l, _) => { ThemeValue::Link(ref l, _) => {
let l = l.as_ref();
path.push((l, course)); path.push((l, course));
if (!visited[&(l, course)] if (!visited[&(l, course)]
&& is_cyclic_util(course, l, visited, stack, path, theme)) && is_cyclic_util(course, l, visited, stack, path, theme))
@ -1895,6 +1881,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
path.pop(); path.pop();
} }
ThemeValue::Alias(ref ident) => { ThemeValue::Alias(ref ident) => {
let ident = ident.as_ref();
path.push((ident, Course::AttrAlias)); path.push((ident, Course::AttrAlias));
if (!visited[&(ident, Course::AttrAlias)] if (!visited[&(ident, Course::AttrAlias)]
&& is_cyclic_util( && is_cyclic_util(
@ -1915,6 +1902,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
}, },
Course::ColorAliasFg | Course::ColorAliasBg => match &theme.color_aliases[k] { Course::ColorAliasFg | Course::ColorAliasBg => match &theme.color_aliases[k] {
ThemeValue::Link(ref l, ref field) => { ThemeValue::Link(ref l, ref field) => {
let l = l.as_ref();
let course = match (course, field) { let course = match (course, field) {
(Course::ColorAliasFg, ColorField::LikeSelf) => Course::Fg, (Course::ColorAliasFg, ColorField::LikeSelf) => Course::Fg,
(Course::ColorAliasBg, ColorField::LikeSelf) => Course::Bg, (Course::ColorAliasBg, ColorField::LikeSelf) => Course::Bg,
@ -1934,6 +1922,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
path.pop(); path.pop();
} }
ThemeValue::Alias(ref ident) => { ThemeValue::Alias(ref ident) => {
let ident = ident.as_ref();
path.push((ident, course)); path.push((ident, course));
if (!visited[&(ident, course)] if (!visited[&(ident, course)]
&& is_cyclic_util(course, ident, visited, stack, path, theme)) && is_cyclic_util(course, ident, visited, stack, path, theme))
@ -1947,6 +1936,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
}, },
Course::AttrAlias => match &theme.attr_aliases[k] { Course::AttrAlias => match &theme.attr_aliases[k] {
ThemeValue::Link(ref l, ()) => { ThemeValue::Link(ref l, ()) => {
let l = l.as_ref();
path.push((l, Course::Attrs)); path.push((l, Course::Attrs));
if (!visited[&(l, Course::Attrs)] if (!visited[&(l, Course::Attrs)]
&& is_cyclic_util(Course::Attrs, l, visited, stack, path, theme)) && is_cyclic_util(Course::Attrs, l, visited, stack, path, theme))
@ -1957,6 +1947,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
path.pop(); path.pop();
} }
ThemeValue::Alias(ref ident) => { ThemeValue::Alias(ref ident) => {
let ident = ident.as_ref();
path.push((ident, course)); path.push((ident, course));
if (!visited[&(ident, course)] if (!visited[&(ident, course)]
&& is_cyclic_util(course, ident, visited, stack, path, theme)) && is_cyclic_util(course, ident, visited, stack, path, theme))
@ -1977,35 +1968,28 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
let mut path = SmallVec::new(); let mut path = SmallVec::new();
let mut visited = theme let mut visited = theme
.keys() .keys()
.map(|k| { .flat_map(|k| {
std::iter::once(((k, Course::Fg), false)) std::iter::once(((k.as_ref(), Course::Fg), false))
.chain(std::iter::once(((k, Course::Bg), false))) .chain(std::iter::once(((k.as_ref(), Course::Bg), false)))
.chain(std::iter::once(((k, Course::Attrs), false))) .chain(std::iter::once(((k.as_ref(), Course::Attrs), false)))
}) })
.flatten() .chain(theme.color_aliases.keys().flat_map(|k| {
.chain( std::iter::once(((k.as_ref(), Course::ColorAliasFg), false))
theme .chain(std::iter::once(((k.as_ref(), Course::ColorAliasBg), false)))
.color_aliases }))
.keys()
.map(|k| {
std::iter::once(((k, Course::ColorAliasFg), false))
.chain(std::iter::once(((k, Course::ColorAliasBg), false)))
})
.flatten(),
)
.chain( .chain(
theme theme
.attr_aliases .attr_aliases
.keys() .keys()
.map(|k| ((k, Course::AttrAlias), false)), .map(|k| ((k.as_ref(), Course::AttrAlias), false)),
) )
.collect::<IndexMap<(&Cow<'static, str>, Course), bool>>(); .collect::<IndexMap<(&str, Course), bool>>();
let mut stack = visited.clone(); let mut stack = visited.clone();
for k in theme.keys() { for k in theme.keys() {
for &course in [Course::Fg, Course::Bg, Course::Attrs].iter() { for &course in [Course::Fg, Course::Bg, Course::Attrs].iter() {
path.push((k, course)); path.push((k.as_ref(), course));
if is_cyclic_util(course, k, &mut visited, &mut stack, &mut path, &theme) { if is_cyclic_util(course, k, &mut visited, &mut stack, &mut path, theme) {
let path = path let path = path
.into_iter() .into_iter()
.map(|(k, c)| match c { .map(|(k, c)| match c {
@ -2037,7 +2021,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
} }
} }
return Ok(()); Ok(())
} }
#[test] #[test]

View File

@ -96,6 +96,7 @@ impl MailcapEntry {
//let flags = parts_iter.next().unwrap(); //let flags = parts_iter.next().unwrap();
if key.starts_with(&content_type) || key.matches_glob(&content_type) { if key.starts_with(&content_type) || key.matches_glob(&content_type) {
let mut copiousoutput = false; let mut copiousoutput = false;
#[allow(clippy::while_let_on_iterator)]
while let Some(flag) = parts_iter.next() { while let Some(flag) = parts_iter.next() {
if flag.trim() == "copiousoutput" { if flag.trim() == "copiousoutput" {
copiousoutput = true; copiousoutput = true;
@ -118,6 +119,7 @@ impl MailcapEntry {
//let flags = parts_iter.next().unwrap(); //let flags = parts_iter.next().unwrap();
if key.starts_with(&content_type) || key.matches_glob(&content_type) { if key.starts_with(&content_type) || key.matches_glob(&content_type) {
let mut copiousoutput = false; let mut copiousoutput = false;
#[allow(clippy::while_let_on_iterator)]
while let Some(flag) = parts_iter.next() { while let Some(flag) = parts_iter.next() {
if flag.trim() == "copiousoutput" { if flag.trim() == "copiousoutput" {
copiousoutput = true; copiousoutput = true;
@ -178,7 +180,7 @@ impl MailcapEntry {
.collect::<Vec<String>>(); .collect::<Vec<String>>();
let cmd_string = format!("{} {}", cmd, args.join(" ")); let cmd_string = format!("{} {}", cmd, args.join(" "));
melib::log( melib::log(
format!("Executing: sh -c \"{}\"", cmd_string.replace("\"", "\\\"")), format!("Executing: sh -c \"{}\"", cmd_string.replace('"', "\\\"")),
melib::DEBUG, melib::DEBUG,
); );
if copiousoutput { if copiousoutput {
@ -212,25 +214,23 @@ impl MailcapEntry {
.spawn()?; .spawn()?;
pager.stdin.as_mut().unwrap().write_all(&out)?; pager.stdin.as_mut().unwrap().write_all(&out)?;
debug!(pager.wait_with_output()?.stdout); debug!(pager.wait_with_output()?.stdout);
} else if needs_stdin {
let mut child = Command::new("sh")
.args(&["-c", &cmd_string])
.stdin(Stdio::piped())
.stdout(Stdio::inherit())
.spawn()?;
child.stdin.as_mut().unwrap().write_all(&decode(a, None))?;
debug!(child.wait_with_output()?.stdout);
} else { } else {
if needs_stdin { let child = Command::new("sh")
let mut child = Command::new("sh") .args(&["-c", &cmd_string])
.args(&["-c", &cmd_string]) .stdin(Stdio::inherit())
.stdin(Stdio::piped()) .stdout(Stdio::inherit())
.stdout(Stdio::inherit()) .spawn()?;
.spawn()?;
child.stdin.as_mut().unwrap().write_all(&decode(a, None))?; debug!(child.wait_with_output()?.stdout);
debug!(child.wait_with_output()?.stdout);
} else {
let child = Command::new("sh")
.args(&["-c", &cmd_string])
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.spawn()?;
debug!(child.wait_with_output()?.stdout);
}
} }
context.replies.push_back(UIEvent::Fork(ForkType::Finished)); context.replies.push_back(UIEvent::Fork(ForkType::Finished));
Ok(()) Ok(())

View File

@ -168,14 +168,14 @@ pub async fn insert(
format!( format!(
"Failed to open envelope {}: {}", "Failed to open envelope {}: {}",
envelope.message_id_display(), envelope.message_id_display(),
err.to_string() err
) )
); );
log( log(
format!( format!(
"Failed to open envelope {}: {}", "Failed to open envelope {}: {}",
envelope.message_id_display(), envelope.message_id_display(),
err.to_string() err
), ),
ERROR, ERROR,
); );
@ -190,13 +190,13 @@ pub async fn insert(
debug!( debug!(
"Failed to insert envelope {}: {}", "Failed to insert envelope {}: {}",
envelope.message_id_display(), envelope.message_id_display(),
err.to_string() err
); );
log( log(
format!( format!(
"Failed to insert envelope {}: {}", "Failed to insert envelope {}: {}",
envelope.message_id_display(), envelope.message_id_display(),
err.to_string() err
), ),
ERROR, ERROR,
); );
@ -217,19 +217,19 @@ pub async fn insert(
if let Err(err) = conn.execute( if let Err(err) = conn.execute(
"INSERT OR REPLACE INTO envelopes (account_id, hash, date, _from, _to, cc, bcc, subject, message_id, in_reply_to, _references, flags, has_attachments, body_text, timestamp) "INSERT OR REPLACE INTO envelopes (account_id, hash, date, _from, _to, cc, bcc, subject, message_id, in_reply_to, _references, flags, has_attachments, body_text, timestamp)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15)", VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15)",
params![account_id, envelope.hash().to_be_bytes().to_vec(), envelope.date_as_str(), envelope.field_from_to_string(), envelope.field_to_to_string(), envelope.field_cc_to_string(), envelope.field_bcc_to_string(), envelope.subject().into_owned().trim_end_matches('\u{0}'), envelope.message_id_display().to_string(), envelope.in_reply_to_display().map(|f| f.to_string()).unwrap_or(String::new()), envelope.field_references_to_string(), i64::from(envelope.flags().bits()), if envelope.has_attachments() { 1 } else { 0 }, body, envelope.date().to_be_bytes().to_vec()], params![account_id, envelope.hash().to_be_bytes().to_vec(), envelope.date_as_str(), envelope.field_from_to_string(), envelope.field_to_to_string(), envelope.field_cc_to_string(), envelope.field_bcc_to_string(), envelope.subject().into_owned().trim_end_matches('\u{0}'), envelope.message_id_display().to_string(), envelope.in_reply_to_display().map(|f| f.to_string()).unwrap_or_default(), envelope.field_references_to_string(), i64::from(envelope.flags().bits()), if envelope.has_attachments() { 1 } else { 0 }, body, envelope.date().to_be_bytes().to_vec()],
) )
.map_err(|e| MeliError::new(e.to_string())) { .map_err(|e| MeliError::new(e.to_string())) {
debug!( debug!(
"Failed to insert envelope {}: {}", "Failed to insert envelope {}: {}",
envelope.message_id_display(), envelope.message_id_display(),
err.to_string() err
); );
log( log(
format!( format!(
"Failed to insert envelope {}: {}", "Failed to insert envelope {}: {}",
envelope.message_id_display(), envelope.message_id_display(),
err.to_string() err
), ),
ERROR, ERROR,
); );
@ -253,17 +253,9 @@ pub fn remove(env_hash: EnvelopeHash) -> Result<()> {
) )
.map_err(|e| MeliError::new(e.to_string())) .map_err(|e| MeliError::new(e.to_string()))
{ {
debug!( debug!("Failed to remove envelope {}: {}", env_hash, err);
"Failed to remove envelope {}: {}",
env_hash,
err.to_string()
);
log( log(
format!( format!("Failed to remove envelope {}: {}", env_hash, err),
"Failed to remove envelope {}: {}",
env_hash,
err.to_string()
),
ERROR, ERROR,
); );
return Err(err); return Err(err);
@ -324,11 +316,11 @@ pub fn index(context: &mut crate::state::Context, account_index: usize) -> Resul
.await .await
.chain_err_summary(|| format!("Failed to open envelope {}", env_hash))?; .chain_err_summary(|| format!("Failed to open envelope {}", env_hash))?;
let envelopes_lck = acc_mutex.read().unwrap(); let envelopes_lck = acc_mutex.read().unwrap();
if let Some(e) = envelopes_lck.get(&env_hash) { if let Some(e) = envelopes_lck.get(env_hash) {
let body = e.body_bytes(&bytes).text().replace('\0', ""); let body = e.body_bytes(&bytes).text().replace('\0', "");
conn.execute("INSERT OR REPLACE INTO envelopes (account_id, hash, date, _from, _to, cc, bcc, subject, message_id, in_reply_to, _references, flags, has_attachments, body_text, timestamp) conn.execute("INSERT OR REPLACE INTO envelopes (account_id, hash, date, _from, _to, cc, bcc, subject, message_id, in_reply_to, _references, flags, has_attachments, body_text, timestamp)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15)", VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15)",
params![account_id, e.hash().to_be_bytes().to_vec(), e.date_as_str(), e.field_from_to_string(), e.field_to_to_string(), e.field_cc_to_string(), e.field_bcc_to_string(), e.subject().into_owned().trim_end_matches('\u{0}'), e.message_id_display().to_string(), e.in_reply_to_display().map(|f| f.to_string()).unwrap_or(String::new()), e.field_references_to_string(), i64::from(e.flags().bits()), if e.has_attachments() { 1 } else { 0 }, body, e.date().to_be_bytes().to_vec()], params![account_id, e.hash().to_be_bytes().to_vec(), e.date_as_str(), e.field_from_to_string(), e.field_to_to_string(), e.field_cc_to_string(), e.field_bcc_to_string(), e.subject().into_owned().trim_end_matches('\u{0}'), e.message_id_display().to_string(), e.in_reply_to_display().map(|f| f.to_string()).unwrap_or_default(), e.field_references_to_string(), i64::from(e.flags().bits()), if e.has_attachments() { 1 } else { 0 }, body, e.date().to_be_bytes().to_vec()],
).chain_err_summary(|| format!( "Failed to insert envelope {}", e.message_id_display()))?; ).chain_err_summary(|| format!( "Failed to insert envelope {}", e.message_id_display()))?;
} }
} }
@ -366,7 +358,7 @@ pub fn search(
.prepare( .prepare(
debug!(format!( debug!(format!(
"SELECT hash FROM envelopes WHERE {} ORDER BY {} {};", "SELECT hash FROM envelopes WHERE {} ORDER BY {} {};",
query_to_sql(&query), query_to_sql(query),
sort_field, sort_field,
sort_order sort_order
)) ))
@ -375,7 +367,7 @@ pub fn search(
.map_err(|e| MeliError::new(e.to_string()))?; .map_err(|e| MeliError::new(e.to_string()))?;
let results = stmt let results = stmt
.query_map([], |row| Ok(row.get(0)?)) .query_map([], |row| row.get(0))
.map_err(|e| MeliError::new(e.to_string()))? .map_err(|e| MeliError::new(e.to_string()))?
.map(|r: std::result::Result<Vec<u8>, rusqlite::Error>| { .map(|r: std::result::Result<Vec<u8>, rusqlite::Error>| {
Ok(u64::from_be_bytes( Ok(u64::from_be_bytes(
@ -424,14 +416,14 @@ pub fn query_to_sql(q: &Query) -> String {
s.push_str("%\" "); s.push_str("%\" ");
} }
And(q1, q2) => { And(q1, q2) => {
s.push_str("("); s.push('(');
rec(q1, s); rec(q1, s);
s.push_str(") AND ("); s.push_str(") AND (");
rec(q2, s); rec(q2, s);
s.push_str(") "); s.push_str(") ");
} }
Or(q1, q2) => { Or(q1, q2) => {
s.push_str("("); s.push('(');
rec(q1, s); rec(q1, s);
s.push_str(") OR ("); s.push_str(") OR (");
rec(q2, s); rec(q2, s);
@ -445,7 +437,7 @@ pub fn query_to_sql(q: &Query) -> String {
Flags(v) => { Flags(v) => {
let total = v.len(); let total = v.len();
if total > 1 { if total > 1 {
s.push_str("("); s.push('(');
} }
for (i, f) in v.iter().enumerate() { for (i, f) in v.iter().enumerate() {
match f.as_str() { match f.as_str() {

View File

@ -134,20 +134,18 @@ impl Context {
} = self; } = self;
let was_online = accounts[account_pos].is_online.is_ok(); let was_online = accounts[account_pos].is_online.is_ok();
let ret = accounts[account_pos].is_online(); let ret = accounts[account_pos].is_online();
if ret.is_ok() { if ret.is_ok() && !was_online {
if !was_online { debug!("inserting mailbox hashes:");
debug!("inserting mailbox hashes:"); for mailbox_node in accounts[account_pos].list_mailboxes() {
for mailbox_node in accounts[account_pos].list_mailboxes() { debug!(
debug!( "hash & mailbox: {:?} {}",
"hash & mailbox: {:?} {}", mailbox_node.hash,
mailbox_node.hash, accounts[account_pos][&mailbox_node.hash].name()
accounts[account_pos][&mailbox_node.hash].name() );
);
}
accounts[account_pos].watch();
replies.push_back(UIEvent::AccountStatusChange(accounts[account_pos].hash()));
} }
accounts[account_pos].watch();
replies.push_back(UIEvent::AccountStatusChange(accounts[account_pos].hash()));
} }
if ret.is_ok() != was_online { if ret.is_ok() != was_online {
replies.push_back(UIEvent::AccountStatusChange(accounts[account_pos].hash())); replies.push_back(UIEvent::AccountStatusChange(accounts[account_pos].hash()));
@ -331,7 +329,7 @@ impl State {
display_messages_area: ((0, 0), (0, 0)), display_messages_area: ((0, 0), (0, 0)),
context: Context { context: Context {
accounts, accounts,
settings: settings, settings,
dirty_areas: VecDeque::with_capacity(5), dirty_areas: VecDeque::with_capacity(5),
replies: VecDeque::with_capacity(5), replies: VecDeque::with_capacity(5),
temp_files: Vec::new(), temp_files: Vec::new(),
@ -399,10 +397,8 @@ impl State {
} }
self.rcv_event(notification); self.rcv_event(notification);
} }
} else { } else if let melib::backends::RefreshEventKind::Failure(err) = event.kind {
if let melib::backends::RefreshEventKind::Failure(err) = event.kind { debug!(err);
debug!(err);
}
} }
} }
@ -850,7 +846,7 @@ impl State {
)); ));
} }
AccountAction(ref account_name, PrintAccountSetting(ref setting)) => { AccountAction(ref account_name, PrintAccountSetting(ref setting)) => {
let path = setting.split(".").collect::<SmallVec<[&str; 16]>>(); let path = setting.split('.').collect::<SmallVec<[&str; 16]>>();
if let Some(pos) = self if let Some(pos) = self
.context .context
.accounts .accounts
@ -858,13 +854,12 @@ impl State {
.position(|(_h, a)| a.name() == account_name) .position(|(_h, a)| a.name() == account_name)
{ {
self.context.replies.push_back(UIEvent::StatusEvent( self.context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::UpdateStatus(format!( StatusEvent::UpdateStatus(
"{}",
self.context.accounts[pos] self.context.accounts[pos]
.settings .settings
.lookup("settings", &path) .lookup("settings", &path)
.unwrap_or_else(|err| err.to_string()) .unwrap_or_else(|err| err.to_string()),
)), ),
)); ));
} else { } else {
self.context.replies.push_back(UIEvent::Notification( self.context.replies.push_back(UIEvent::Notification(
@ -872,20 +867,18 @@ impl State {
format!("Account {} was not found.", account_name), format!("Account {} was not found.", account_name),
Some(NotificationType::Error(ErrorKind::None)), Some(NotificationType::Error(ErrorKind::None)),
)); ));
return;
} }
} }
PrintSetting(ref setting) => { PrintSetting(ref setting) => {
let path = setting.split(".").collect::<SmallVec<[&str; 16]>>(); let path = setting.split('.').collect::<SmallVec<[&str; 16]>>();
self.context self.context
.replies .replies
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(format!( .push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
"{}",
self.context self.context
.settings .settings
.lookup("settings", &path) .lookup("settings", &path)
.unwrap_or_else(|err| err.to_string()) .unwrap_or_else(|err| err.to_string()),
)))); )));
} }
ToggleMouse => { ToggleMouse => {
self.screen.mouse = !self.screen.mouse; self.screen.mouse = !self.screen.mouse;
@ -920,7 +913,7 @@ impl State {
match event { match event {
// Command type is handled only by State. // Command type is handled only by State.
UIEvent::Command(cmd) => { UIEvent::Command(cmd) => {
if let Ok(action) = parse_command(&cmd.as_bytes()) { if let Ok(action) = parse_command(cmd.as_bytes()) {
if action.needs_confirmation() { if action.needs_confirmation() {
self.overlay.push(Box::new(UIConfirmationDialog::new( self.overlay.push(Box::new(UIConfirmationDialog::new(
"You sure?", "You sure?",
@ -932,7 +925,7 @@ impl State {
Box::new(if result { Some(action) } else { None }), Box::new(if result { Some(action) } else { None }),
)) ))
})), })),
&mut self.context, &self.context,
))); )));
} else if let Action::ReloadConfiguration = action { } else if let Action::ReloadConfiguration = action {
match Settings::new().and_then(|new_settings| { match Settings::new().and_then(|new_settings| {
@ -952,7 +945,7 @@ impl State {
Ok(new_settings) Ok(new_settings)
}) { }) {
Ok(new_settings) => { Ok(new_settings) => {
let old_settings = std::mem::replace(&mut self.context.settings, new_settings); let old_settings = Box::new(std::mem::replace(&mut self.context.settings, new_settings));
self.context.replies.push_back(UIEvent::ConfigReload { self.context.replies.push_back(UIEvent::ConfigReload {
old_settings old_settings
}); });
@ -1130,11 +1123,8 @@ impl State {
match w { match w {
Ok(Some(_)) => true, Ok(Some(_)) => true,
Ok(None) => false, Ok(None) => false,
Err(e) => { Err(err) => {
log( log(format!("Failed to wait on editor process: {}", err), ERROR);
format!("Failed to wait on editor process: {}", e.to_string()),
ERROR,
);
return None; return None;
} }
} }
@ -1144,11 +1134,8 @@ impl State {
match w { match w {
Ok(Some(_)) => true, Ok(Some(_)) => true,
Ok(None) => false, Ok(None) => false,
Err(e) => { Err(err) => {
log( log(format!("Failed to wait on child process: {}", err), ERROR);
format!("Failed to wait on child process: {}", e.to_string()),
ERROR,
);
return None; return None;
} }
} }
@ -1174,7 +1161,7 @@ impl State {
} }
pub fn switch_to_alternate_screen(&mut self) { pub fn switch_to_alternate_screen(&mut self) {
self.screen.switch_to_alternate_screen(&mut self.context); self.screen.switch_to_alternate_screen(&self.context);
} }
fn flush(&mut self) { fn flush(&mut self) {

View File

@ -151,7 +151,7 @@ impl CellBuffer {
pub fn resize(&mut self, newcols: usize, newrows: usize, blank: Option<Cell>) -> bool { pub fn resize(&mut self, newcols: usize, newrows: usize, blank: Option<Cell>) -> bool {
let newlen = newcols * newrows; let newlen = newcols * newrows;
if (self.cols, self.rows) == (newcols, newrows) || newlen >= Self::MAX_SIZE { if (self.cols, self.rows) == (newcols, newrows) || newlen >= Self::MAX_SIZE {
return !(newlen >= Self::MAX_SIZE); return newlen < Self::MAX_SIZE;
} }
let blank = blank.unwrap_or(self.default_cell); let blank = blank.unwrap_or(self.default_cell);
@ -432,10 +432,10 @@ impl CellBuffer {
pub fn set_tag(&mut self, tag: u64, start: (usize, usize), end: (usize, usize)) { pub fn set_tag(&mut self, tag: u64, start: (usize, usize), end: (usize, usize)) {
let start = self let start = self
.pos_to_index(start.0, start.1) .pos_to_index(start.0, start.1)
.unwrap_or(self.buf.len().saturating_sub(1)); .unwrap_or_else(|| self.buf.len().saturating_sub(1));
let end = self let end = self
.pos_to_index(end.0, end.1) .pos_to_index(end.0, end.1)
.unwrap_or(self.buf.len().saturating_sub(1)); .unwrap_or_else(|| self.buf.len().saturating_sub(1));
if start != end { if start != end {
self.tag_associations.push((tag, (start, end))); self.tag_associations.push((tag, (start, end)));
} }
@ -856,9 +856,9 @@ impl Attr {
"Blink" => Ok(Attr::BLINK), "Blink" => Ok(Attr::BLINK),
"Reverse" => Ok(Attr::REVERSE), "Reverse" => Ok(Attr::REVERSE),
"Hidden" => Ok(Attr::HIDDEN), "Hidden" => Ok(Attr::HIDDEN),
combination if combination.contains("|") => { combination if combination.contains('|') => {
let mut ret = Attr::DEFAULT; let mut ret = Attr::DEFAULT;
for c in combination.trim().split("|") { for c in combination.trim().split('|') {
ret |= Self::from_string_de::<'de, D, &str>(c)?; ret |= Self::from_string_de::<'de, D, &str>(c)?;
} }
Ok(ret) Ok(ret)
@ -1725,7 +1725,7 @@ impl core::cmp::Ord for FormatTag {
impl core::cmp::PartialOrd for FormatTag { impl core::cmp::PartialOrd for FormatTag {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(&other)) Some(self.cmp(other))
} }
} }

View File

@ -391,10 +391,12 @@ impl Color {
"Yellow5" => 106, "Yellow5" => 106,
"Yellow6" => 148, "Yellow6" => 148,
"Default" => return Ok(Color::Default), "Default" => return Ok(Color::Default),
s if s.starts_with("#") s if s.starts_with('#')
&& s.len() == 7 && s.len() == 7
&& s[1..].as_bytes().iter().all(|&b| { && s[1..].as_bytes().iter().all(|&b| {
(b >= b'0' && b <= b'9') || (b >= b'a' && b <= b'f') || (b >= b'A' && b <= b'F') (b'0'..=b'9').contains(&b)
|| (b'a'..=b'f').contains(&b)
|| (b'A'..=b'F').contains(&b)
}) => }) =>
{ {
return Ok(Color::Rgb( return Ok(Color::Rgb(
@ -406,10 +408,12 @@ impl Color {
.map_err(|_| de::Error::custom("invalid `color` value"))?, .map_err(|_| de::Error::custom("invalid `color` value"))?,
)) ))
} }
s if s.starts_with("#") s if s.starts_with('#')
&& s.len() == 4 && s.len() == 4
&& s[1..].as_bytes().iter().all(|&b| { && s[1..].as_bytes().iter().all(|&b| {
(b >= b'0' && b <= b'9') || (b >= b'a' && b <= b'f') || (b >= b'A' && b <= b'F') (b'0'..=b'9').contains(&b)
|| (b'a'..=b'f').contains(&b)
|| (b'A'..=b'F').contains(&b)
}) => }) =>
{ {
return Ok(Color::Rgb( return Ok(Color::Rgb(
@ -421,7 +425,8 @@ impl Color {
.map_err(|_| de::Error::custom("invalid `color` value"))?, .map_err(|_| de::Error::custom("invalid `color` value"))?,
)) ))
} }
_ => u8::from_str_radix(&s, 10) _ => s
.parse::<u8>()
.map_err(|_| de::Error::custom("invalid `color` value"))?, .map_err(|_| de::Error::custom("invalid `color` value"))?,
}; };
Ok(Color::Byte(byte)) Ok(Color::Byte(byte))

View File

@ -346,7 +346,7 @@ impl EmbedGrid {
*state = State::CsiQ(buf1); *state = State::CsiQ(buf1);
} }
/* OSC stuff */ /* OSC stuff */
(c, State::Osc1(ref mut buf)) if (c >= b'0' && c <= b'9') || c == b'?' => { (c, State::Osc1(ref mut buf)) if (b'0'..=b'9').contains(&c) || c == b'?' => {
buf.push(c); buf.push(c);
} }
(b';', State::Osc1(ref mut buf1_p)) => { (b';', State::Osc1(ref mut buf1_p)) => {
@ -354,7 +354,7 @@ impl EmbedGrid {
let buf2 = SmallVec::new(); let buf2 = SmallVec::new();
*state = State::Osc2(buf1, buf2); *state = State::Osc2(buf1, buf2);
} }
(c, State::Osc2(_, ref mut buf)) if (c >= b'0' && c <= b'9') || c == b'?' => { (c, State::Osc2(_, ref mut buf)) if (b'0'..=b'9').contains(&c) || c == b'?' => {
buf.push(c); buf.push(c);
} }
/* Normal */ /* Normal */
@ -512,7 +512,7 @@ impl EmbedGrid {
*state = State::Normal; *state = State::Normal;
} }
/* CSI ? stuff */ /* CSI ? stuff */
(c, State::CsiQ(ref mut buf)) if c >= b'0' && c <= b'9' => { (c, State::CsiQ(ref mut buf)) if (b'0'..=b'9').contains(&c) => {
buf.push(c); buf.push(c);
} }
(b'h', State::CsiQ(ref buf)) => { (b'h', State::CsiQ(ref buf)) => {
@ -569,7 +569,7 @@ impl EmbedGrid {
*state = State::Normal; *state = State::Normal;
} }
/* END OF CSI ? stuff */ /* END OF CSI ? stuff */
(c, State::Csi) if c >= b'0' && c <= b'9' => { (c, State::Csi) if (b'0'..=b'9').contains(&c) => {
let mut buf1 = SmallVec::new(); let mut buf1 = SmallVec::new();
buf1.push(c); buf1.push(c);
*state = State::Csi1(buf1); *state = State::Csi1(buf1);
@ -973,17 +973,9 @@ impl EmbedGrid {
*bg_color = Color::Default; *bg_color = Color::Default;
} }
b"1" => { /* bold */ } b"1" => { /* bold */ }
b"7" => { b"7" | b"27" => {
/* Inverse */ /* Inverse on/off */
let temp = *fg_color; std::mem::swap(&mut (*fg_color), &mut (*bg_color))
*fg_color = *bg_color;
*bg_color = temp;
}
b"27" => {
/* Inverse off */
let temp = *fg_color;
*fg_color = *bg_color;
*bg_color = temp;
} }
b"30" => *fg_color = Color::Black, b"30" => *fg_color = Color::Black,
b"31" => *fg_color = Color::Red, b"31" => *fg_color = Color::Red,
@ -1037,17 +1029,9 @@ impl EmbedGrid {
*bg_color = Color::Default; *bg_color = Color::Default;
} }
b"1" => { /* bold */ } b"1" => { /* bold */ }
b"7" => { b"7" | b"27" => {
/* Inverse */ /* Inverse on/off */
let temp = *fg_color; std::mem::swap(&mut (*fg_color), &mut (*bg_color))
*fg_color = *bg_color;
*bg_color = temp;
}
b"27" => {
/* Inverse off */
let temp = *fg_color;
*fg_color = *bg_color;
*bg_color = temp;
} }
b"30" => *fg_color = Color::Black, b"30" => *fg_color = Color::Black,
b"31" => *fg_color = Color::Red, b"31" => *fg_color = Color::Red,
@ -1094,7 +1078,7 @@ impl EmbedGrid {
grid[cursor_val!()].set_bg(*bg_color); grid[cursor_val!()].set_bg(*bg_color);
*state = State::Normal; *state = State::Normal;
} }
(c, State::Csi1(ref mut buf)) if (c >= b'0' && c <= b'9') || c == b' ' => { (c, State::Csi1(ref mut buf)) if (b'0'..=b'9').contains(&c) || c == b' ' => {
buf.push(c); buf.push(c);
} }
(b';', State::Csi2(ref mut buf1_p, ref mut buf2_p)) => { (b';', State::Csi2(ref mut buf1_p, ref mut buf2_p)) => {
@ -1144,7 +1128,7 @@ impl EmbedGrid {
//debug!("cursor became: {:?}", cursor); //debug!("cursor became: {:?}", cursor);
*state = State::Normal; *state = State::Normal;
} }
(c, State::Csi2(_, ref mut buf)) if c >= b'0' && c <= b'9' => { (c, State::Csi2(_, ref mut buf)) if (b'0'..=b'9').contains(&c) => {
buf.push(c); buf.push(c);
} }
(b'r', State::Csi2(_, _)) | (b'r', State::Csi) => { (b'r', State::Csi2(_, _)) | (b'r', State::Csi) => {
@ -1177,7 +1161,7 @@ impl EmbedGrid {
*state = State::Normal; *state = State::Normal;
} }
(c, State::Csi3(_, _, ref mut buf)) if c >= b'0' && c <= b'9' => { (c, State::Csi3(_, _, ref mut buf)) if (b'0'..=b'9').contains(&c) => {
buf.push(c); buf.push(c);
} }
(b'm', State::Csi3(ref buf1, ref buf2, ref buf3)) (b'm', State::Csi3(ref buf1, ref buf2, ref buf3))
@ -1185,7 +1169,7 @@ impl EmbedGrid {
{ {
/* Set character attributes | foreground color */ /* Set character attributes | foreground color */
*fg_color = if let Ok(byte) = *fg_color = if let Ok(byte) =
u8::from_str_radix(unsafe { std::str::from_utf8_unchecked(buf3) }, 10) unsafe { std::str::from_utf8_unchecked(buf3) }.parse::<u8>()
{ {
//debug!("parsed buf as {}", byte); //debug!("parsed buf as {}", byte);
Color::Byte(byte) Color::Byte(byte)
@ -1200,7 +1184,7 @@ impl EmbedGrid {
{ {
/* Set character attributes | background color */ /* Set character attributes | background color */
*bg_color = if let Ok(byte) = *bg_color = if let Ok(byte) =
u8::from_str_radix(unsafe { std::str::from_utf8_unchecked(buf3) }, 10) unsafe { std::str::from_utf8_unchecked(buf3) }.parse::<u8>()
{ {
//debug!("parsed buf as {}", byte); //debug!("parsed buf as {}", byte);
Color::Byte(byte) Color::Byte(byte)
@ -1246,3 +1230,9 @@ impl EmbedGrid {
} }
} }
} }
impl Default for EmbedGrid {
fn default() -> Self {
Self::new()
}
}

View File

@ -182,7 +182,7 @@ pub fn get_events(
select! { select! {
default => { default => {
if stdin_fd.revents().is_some() { if stdin_fd.revents().is_some() {
'stdin_while: while let Some(c) = stdin_iter.next(){ 'stdin_while: for c in stdin_iter.by_ref() {
match (c, &mut input_mode) { match (c, &mut input_mode) {
(Ok((TermionEvent::Key(k), bytes)), InputMode::Normal) => { (Ok((TermionEvent::Key(k), bytes)), InputMode::Normal) => {
closure((Key::from(k), bytes)); closure((Key::from(k), bytes));
@ -192,7 +192,7 @@ pub fn get_events(
Ok((TermionEvent::Key(TermionKey::Char(k)), ref mut bytes)), InputMode::Paste(ref mut buf), Ok((TermionEvent::Key(TermionKey::Char(k)), ref mut bytes)), InputMode::Paste(ref mut buf),
) => { ) => {
paste_buf.push(k); paste_buf.push(k);
let bytes = std::mem::replace(bytes, Vec::new()); let bytes = std::mem::take(bytes);
buf.extend(bytes.into_iter()); buf.extend(bytes.into_iter());
continue 'stdin_while; continue 'stdin_while;
} }
@ -203,7 +203,7 @@ pub fn get_events(
(Ok((TermionEvent::Unsupported(ref k), _)), InputMode::Paste(ref mut buf)) (Ok((TermionEvent::Unsupported(ref k), _)), InputMode::Paste(ref mut buf))
if k.as_slice() == BRACKET_PASTE_END => if k.as_slice() == BRACKET_PASTE_END =>
{ {
let buf = std::mem::replace(buf, Vec::new()); let buf = std::mem::take(buf);
input_mode = InputMode::Normal; input_mode = InputMode::Normal;
let ret = Key::from(&paste_buf); let ret = Key::from(&paste_buf);
paste_buf.clear(); paste_buf.clear();
@ -277,12 +277,12 @@ impl<'de> Deserialize<'de> for Key {
"Enter" | "enter" => Ok(Key::Char('\n')), "Enter" | "enter" => Ok(Key::Char('\n')),
"Tab" | "tab" => Ok(Key::Char('\t')), "Tab" | "tab" => Ok(Key::Char('\t')),
"Esc" | "esc" => Ok(Key::Esc), "Esc" | "esc" => Ok(Key::Esc),
ref s if s.len() == 1 => Ok(Key::Char(s.chars().nth(0).unwrap())), s if s.len() == 1 => Ok(Key::Char(s.chars().next().unwrap())),
ref s if s.starts_with("F") && (s.len() == 2 || s.len() == 3) => { s if s.starts_with('F') && (s.len() == 2 || s.len() == 3) => {
use std::str::FromStr; use std::str::FromStr;
if let Ok(n) = u8::from_str(&s[1..]) { if let Ok(n) = u8::from_str(&s[1..]) {
if n >= 1 && n <= 12 { if (1..=12).contains(&n) {
return Ok(Key::F(n)); return Ok(Key::F(n));
} }
} }
@ -291,7 +291,7 @@ impl<'de> Deserialize<'de> for Key {
&s[1..] &s[1..]
))) )))
} }
ref s if s.starts_with("M-") && s.len() == 3 => { s if s.starts_with("M-") && s.len() == 3 => {
let c = s.as_bytes()[2] as char; let c = s.as_bytes()[2] as char;
if c.is_lowercase() || c.is_numeric() { if c.is_lowercase() || c.is_numeric() {
@ -303,7 +303,7 @@ impl<'de> Deserialize<'de> for Key {
&s[2..] &s[2..]
))) )))
} }
ref s if s.starts_with("C-") && s.len() == 3 => { s if s.starts_with("C-") && s.len() == 3 => {
let c = s.as_bytes()[2] as char; let c = s.as_bytes()[2] as char;
if c.is_lowercase() || c.is_numeric() { if c.is_lowercase() || c.is_numeric() {

View File

@ -149,12 +149,12 @@ pub enum UIEvent {
GlobalUIDialog(Box<dyn Component>), GlobalUIDialog(Box<dyn Component>),
Timer(Uuid), Timer(Uuid),
ConfigReload { ConfigReload {
old_settings: crate::conf::Settings, old_settings: Box<crate::conf::Settings>,
}, },
VisibilityChange(bool), VisibilityChange(bool),
} }
pub struct CallbackFn(pub Box<dyn FnOnce(&mut crate::Context) -> () + Send + 'static>); pub struct CallbackFn(pub Box<dyn FnOnce(&mut crate::Context) + Send + 'static>);
impl core::fmt::Debug for CallbackFn { impl core::fmt::Debug for CallbackFn {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
@ -322,8 +322,6 @@ pub struct RateLimit {
last_tick: std::time::Instant, last_tick: std::time::Instant,
pub timer: crate::jobs::Timer, pub timer: crate::jobs::Timer,
rate: std::time::Duration, rate: std::time::Duration,
reqs: u64,
millis: std::time::Duration,
pub active: bool, pub active: bool,
} }
@ -336,8 +334,6 @@ impl RateLimit {
std::time::Duration::from_millis(millis), std::time::Duration::from_millis(millis),
), ),
rate: std::time::Duration::from_millis(millis / reqs), rate: std::time::Duration::from_millis(millis / reqs),
reqs,
millis: std::time::Duration::from_millis(millis),
active: false, active: false,
} }
} }

View File

@ -36,7 +36,7 @@ pub struct File {
impl Drop for File { impl Drop for File {
fn drop(&mut self) { fn drop(&mut self) {
if self.delete_on_drop { if self.delete_on_drop {
std::fs::remove_file(self.path()).unwrap_or_else(|_| {}); let _ = std::fs::remove_file(self.path());
} }
} }
} }
@ -54,6 +54,7 @@ impl File {
pub fn path(&self) -> &PathBuf { pub fn path(&self) -> &PathBuf {
&self.path &self.path
} }
pub fn read_to_string(&self) -> String { pub fn read_to_string(&self) -> String {
let mut buf = Vec::new(); let mut buf = Vec::new();
let mut f = fs::File::open(&self.path) let mut f = fs::File::open(&self.path)