ui: Add possible mailing list actions from List-* headers

embed
Manos Pitsidianakis 2019-06-18 22:01:02 +03:00
parent ba1d0c42e0
commit 07700ca00f
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
4 changed files with 141 additions and 1 deletions

View File

@ -24,6 +24,8 @@ use linkify::{Link, LinkFinder};
use std::process::{Command, Stdio};
mod list_management;
mod html;
pub use self::html::*;
mod thread;

View File

@ -0,0 +1,116 @@
/*
* meli - ui crate.
*
* Copyright 2017-2019 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use melib::parser;
use melib::Envelope;
use melib::StackVec;
use std::convert::From;
#[derive(Debug, Copy)]
pub enum UnsubscribeOption<'a> {
Url(&'a [u8]),
Email(&'a [u8]),
}
impl<'a> From<&'a [u8]> for UnsubscribeOption<'a> {
fn from(value: &'a [u8]) -> Self {
if value.starts_with(b"mailto:") {
/* if branch looks if value looks like a mailto url but doesn't validate it.
* parser::mailto() will handle this if user tries to unsubscribe.
*/
UnsubscribeOption::Email(value)
} else {
/* Otherwise treat it as url. There's no foolproof way to check if this is valid, so
* postpone it until we try an HTTP request.
*/
UnsubscribeOption::Url(value)
}
}
}
/* Required for StackVec's place holder elements, never actually used */
impl<'a> Default for UnsubscribeOption<'a> {
fn default() -> Self {
UnsubscribeOption::Email(b"")
}
}
impl<'a> Clone for UnsubscribeOption<'a> {
fn clone(&self) -> Self {
match self {
UnsubscribeOption::Url(a) => UnsubscribeOption::Url(a.clone()),
UnsubscribeOption::Email(a) => UnsubscribeOption::Email(a.clone()),
}
}
}
#[derive(Default)]
pub struct ListActions<'a> {
pub id: Option<&'a str>,
pub archive: Option<&'a str>,
pub post: Option<&'a str>,
pub unsubscribe: Option<StackVec<UnsubscribeOption<'a>>>,
}
pub fn detect<'a>(envelope: &'a Envelope) -> Option<ListActions<'a>> {
let mut ret = ListActions::default();
if let Some(id) = envelope.other_headers().get("List-ID") {
ret.id = Some(id);
} else if let Some(id) = envelope.other_headers().get("List-Id") {
ret.id = Some(id);
}
if let Some(archive) = envelope.other_headers().get("List-Archive") {
ret.archive = Some(archive);
}
if let Some(post) = envelope.other_headers().get("List-Post") {
ret.post = Some(post);
}
if let Some(unsubscribe) = envelope.other_headers().get("List-Unsubscribe") {
ret.unsubscribe = parser::angle_bracket_delimeted_list(unsubscribe.as_bytes())
.map(|mut vec| {
/* Prefer email options first, since this _is_ a mail client after all and it's
* more automated */
vec.sort_unstable_by(|a, b| {
match (a.starts_with(b"mailto:"), b.starts_with(b"mailto:")) {
(true, false) => std::cmp::Ordering::Less,
(false, true) => std::cmp::Ordering::Greater,
_ => std::cmp::Ordering::Equal,
}
});
vec.into_iter()
.map(|elem| UnsubscribeOption::from(elem))
.collect::<StackVec<UnsubscribeOption<'a>>>()
})
.to_full_result()
.ok();
}
if ret.id.is_none() && ret.archive.is_none() && ret.post.is_none() && ret.unsubscribe.is_none()
{
None
} else {
Some(ret)
}
}

View File

@ -27,6 +27,7 @@ use std;
pub mod actions;
pub use crate::actions::Action::{self, *};
pub use crate::actions::ListingAction::{self, *};
pub use crate::actions::MailingListAction::{self, *};
pub use crate::actions::TabAction::{self, *};
named!(
@ -95,6 +96,19 @@ named!(
map!(ws!(tag!("toggle_thread_snooze")), |_| ToggleThreadSnooze)
);
named!(
mailinglist<Action>,
alt_complete!(
map!(ws!(tag!("list-post")), |_| MailingListAction(ListPost))
| map!(ws!(tag!("list-unsubscribe")), |_| MailingListAction(
ListUnsubscribe
))
| map!(ws!(tag!("list-archive")), |_| MailingListAction(
ListArchive
))
)
);
named!(pub parse_command<Action>,
alt_complete!( goto | toggle | sort | subsort | close | toggle_thread_snooze)
alt_complete!( goto | toggle | sort | subsort | close | toggle_thread_snooze | mailinglist)
);

View File

@ -48,6 +48,13 @@ pub enum TabAction {
Kill(Uuid),
}
#[derive(Debug)]
pub enum MailingListAction {
ListPost,
ListArchive,
ListUnsubscribe,
}
#[derive(Debug)]
pub enum Action {
Listing(ListingAction),
@ -56,4 +63,5 @@ pub enum Action {
SubSort(SortField, SortOrder),
Tab(TabAction),
ToggleThreadSnooze,
MailingListAction(MailingListAction),
}