Remove ui crate

Merge ui crate with root crate.

In preparation for uploading `meli` as a separate crate on crates.io.

Workspace crates will need to be published as well and having a separate
`ui` crate and binary perhaps doesn't make sense anymore.
async
Manos Pitsidianakis 2020-02-04 15:52:12 +02:00
parent 6fcc792b83
commit 8b6ea8de9a
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
69 changed files with 320 additions and 892 deletions

62
Cargo.lock generated
View File

@ -236,15 +236,6 @@ dependencies = [
"libdbus-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "libdbus-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "debug_printer"
version = "0.0.1"
dependencies = [
"libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)",
"melib 0.4.1",
"ui 0.4.1",
]
[[package]] [[package]]
name = "dirs" name = "dirs"
version = "1.0.5" version = "1.0.5"
@ -743,13 +734,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "meli" name = "meli"
version = "0.4.1" version = "0.4.1"
dependencies = [ dependencies = [
"bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)",
"linkify 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"melib 0.4.1", "melib 0.4.1",
"nix 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
"nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"notify 4.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
"notify-rust 3.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rmp 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)",
"rmp-serde 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rmpv 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
"signal-hook 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "signal-hook 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ui 0.4.1", "smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"text_processing 0.4.1",
"toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
"xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"xdg-utils 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -1839,37 +1850,6 @@ name = "try-lock"
version = "0.2.2" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ui"
version = "0.4.1"
dependencies = [
"bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)",
"linkify 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"melib 0.4.1",
"nix 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
"nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"notify 4.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
"notify-rust 3.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rmp 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)",
"rmp-serde 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rmpv 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"text_processing 0.4.1",
"toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
"xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"xdg-utils 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "unicase" name = "unicase"
version = "2.6.0" version = "2.6.0"

View File

@ -14,21 +14,44 @@ crossbeam = "0.7.2"
signal-hook = "0.1.12" signal-hook = "0.1.12"
signal-hook-registry = "1.2.0" signal-hook-registry = "1.2.0"
nix = "0.16.1" nix = "0.16.1"
melib = { path = "melib", version = "*" } melib = { path = "melib", version = "0.4.1" }
ui = { path = "ui", version = "*" }
serde = "1.0.71"
serde_derive = "1.0.71"
serde_json = "1.0"
toml = "0.5.3"
fnv = "1.0.3" # >:c
linkify = "0.3.1" # >:c
xdg-utils = "0.3.0"
nom = "3.2.0"
notify = "4.0.1" # >:c
notify-rust = "^3" # >:c
termion = "1.5.1"
bincode = "1.2.0"
uuid = { version = "0.7.4", features = ["serde", "v4"] }
unicode-segmentation = "1.2.1" # >:c
text_processing = { path = "text_processing", version = "0.4.1" }
libc = {version = "0.2.59", features = ["extra_traits",]}
rusqlite = {version = "0.20.0", optional =true }
rmp = "^0.8"
rmpv = { version = "^0.4.2", features=["with-serde",] }
rmp-serde = "^0.14.0"
smallvec = { version = "1.1.0", features = ["serde", ] }
[profile.release] [profile.release]
lto = true lto = true
debug = false debug = false
[workspace] [workspace]
members = ["melib", "ui", "debug_printer", "testing", "text_processing"] members = ["melib", "testing", "text_processing"]
[features] [features]
default = [] default = ["sqlite3"]
notmuch = ["melib/notmuch_backend", "ui/notmuch"] notmuch = ["melib/notmuch_backend", ]
jmap = ["melib/jmap_backend",] jmap = ["melib/jmap_backend",]
sqlite3 = ["rusqlite"]
# Print tracing logs as meli runs in stderr # Print tracing logs as meli runs in stderr
# enable for debug tracing logs: build with --features=debug-tracing # enable for debug tracing logs: build with --features=debug-tracing
debug-tracing = ["melib/debug-tracing", "ui/debug-tracing"] debug-tracing = ["melib/debug-tracing", ]

View File

@ -46,17 +46,18 @@ GREEN ?= `[ -z $${NO_COLOR+x} ] && tput setaf 2 || echo ""`
help: help:
@echo "For a quick start, build and install locally:\n ${BOLD}${GREEN}PREFIX=~/.local make install${ANSI_RESET}\n" @echo "For a quick start, build and install locally:\n ${BOLD}${GREEN}PREFIX=~/.local make install${ANSI_RESET}\n"
@echo "Available subcommands:" @echo "Available subcommands:"
@echo " - ${BOLD}install${ANSI_RESET} (installs binary and documentation)" @echo " - ${BOLD}meli${ANSI_RESET} (builds meli with optimizations in \$$CARGO_TARGET_DIR)"
@echo " - ${BOLD}install${ANSI_RESET} (installs binary in \$$BINDIR and documentation to \$$MANDIR)"
@echo " - ${BOLD}uninstall${ANSI_RESET}" @echo " - ${BOLD}uninstall${ANSI_RESET}"
@echo "Secondary subcommands:" @echo "Secondary subcommands:"
@echo " - ${BOLD}clean${ANSI_RESET} (cleans build artifacts)" @echo " - ${BOLD}clean${ANSI_RESET} (cleans build artifacts)"
@echo " - ${BOLD}check-deps${ANSI_RESET} (checks dependencies)" @echo " - ${BOLD}check-deps${ANSI_RESET} (checks dependencies)"
@echo " - ${BOLD}install-bin${ANSI_RESET} (installs binary to BINDIR)" @echo " - ${BOLD}install-bin${ANSI_RESET} (installs binary to \$$BINDIR)"
@echo " - ${BOLD}install-doc${ANSI_RESET} (installs manpages to MANDIR)" @echo " - ${BOLD}install-doc${ANSI_RESET} (installs manpages to \$$MANDIR)"
@echo " - ${BOLD}help${ANSI_RESET} (prints this information)" @echo " - ${BOLD}help${ANSI_RESET} (prints this information)"
@echo " - ${BOLD}dist${ANSI_RESET} (creates release tarball named meli-"${VERSION}".tar.gz)" @echo " - ${BOLD}dist${ANSI_RESET} (creates release tarball named meli-"${VERSION}".tar.gz in this directory)"
@echo " - ${BOLD}deb-dist${ANSI_RESET} (builds debian package)" @echo " - ${BOLD}deb-dist${ANSI_RESET} (builds debian package in the parent directory)"
@echo " - ${BOLD}distclean${ANSI_RESET} (cleans distribution build artifacts)" @echo " - ${BOLD}distclean${ANSI_RESET} (cleans distribution build artifacts)"
@echo "\nENVIRONMENT variables of interest:" @echo "\nENVIRONMENT variables of interest:"
@echo "* PREFIX = ${UNDERLINE}${PREFIX}${ANSI_RESET}" @echo "* PREFIX = ${UNDERLINE}${PREFIX}${ANSI_RESET}"

View File

@ -1,17 +0,0 @@
[package]
name = "debug_printer"
version = "0.0.1" #:version
authors = ["Manos Pitsidianakis <el13635@mail.ntua.gr>"]
workspace = ".."
edition = "2018"
[lib]
name = "debugprinter"
crate-type = ["dylib"]
path = "src/lib.rs"
[dependencies]
libc = {version = "0.2.55", features = ["extra_traits",] }
melib = { path = "../melib", version = "*" }
ui = { path = "../ui", version = "*" }

View File

@ -1,44 +0,0 @@
extern crate libc;
extern crate melib;
use melib::Envelope;
use std::ffi::CString;
use std::os::raw::c_char;
#[no_mangle]
pub extern "C" fn print_envelope(ptr: *const Envelope) -> *const c_char {
unsafe {
assert!(!ptr.is_null(), "Null pointer in print_envelope");
//println!("got addr {}", p as u64);
//unsafe { CString::new("blah".to_string()).unwrap().as_ptr() }
let s = CString::new(format!("{:?}", *ptr)).unwrap();
drop(ptr);
let p = s.as_ptr();
std::mem::forget(s);
p
}
}
#[no_mangle]
pub extern "C" fn get_empty_envelope() -> *mut Envelope {
let mut ret = Envelope::default();
let ptr = std::ptr::NonNull::new(&mut ret as *mut Envelope)
.expect("Envelope::default() has a NULL pointer?");
let ptr = ptr.as_ptr();
std::mem::forget(ret);
ptr
}
#[no_mangle]
pub extern "C" fn destroy_cstring(ptr: *mut c_char) {
unsafe {
let slice = CString::from_raw(ptr);
drop(slice);
}
}
#[no_mangle]
pub extern "C" fn envelope_size() -> libc::size_t {
std::mem::size_of::<Envelope>()
}

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2019 Manos Pitsidianakis * Copyright 2017-2019 Manos Pitsidianakis
* *

View File

@ -21,24 +21,74 @@
//! //!
//! This crate contains the frontend stuff of the application. The application entry way on //! This crate contains the frontend stuff of the application. The application entry way on
//! `src/bin.rs` creates an event loop and passes input to the `ui` module. //! `src/bin.rs` creates an event loop and passes input to a thread.
//! //!
//! The mail handling stuff is done in the `melib` crate which includes all backend needs. The //! The mail handling stuff is done in the `melib` crate which includes all backend needs. The
//! split is done to theoretically be able to create different frontends with the same innards. //! split is done to theoretically be able to create different frontends with the same innards.
//! //!
use std::alloc::System; use std::alloc::System;
use std::collections::VecDeque;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
extern crate notify_rust;
extern crate text_processing;
use text_processing::*;
extern crate xdg_utils;
#[macro_use]
extern crate serde_derive;
extern crate linkify;
extern crate uuid;
extern crate fnv;
extern crate termion;
#[macro_use]
extern crate nom;
extern crate serde_json;
extern crate smallvec;
#[global_allocator] #[global_allocator]
static GLOBAL: System = System; static GLOBAL: System = System;
// Re export to put crates in the documentation's start page. #[macro_use]
pub use melib; extern crate melib;
pub use ui;
use melib::*; use melib::*;
use ui::*;
mod unix;
use unix::*;
#[macro_use]
pub mod types;
use crate::types::*;
#[macro_use]
pub mod terminal;
use crate::terminal::*;
#[macro_use]
pub mod execute;
use crate::execute::*;
pub mod state;
use crate::state::*;
pub mod components;
use crate::components::*;
#[macro_use]
pub mod conf;
use crate::conf::*;
pub mod workers;
use crate::workers::*;
#[cfg(feature = "sqlite3")]
pub mod sqlite3;
pub mod cache;
pub mod mailcap;
pub mod plugins;
use nix; use nix;
use std::os::raw::c_int; use std::os::raw::c_int;
@ -152,15 +202,12 @@ fn run_app() -> Result<()> {
args.version = true; args.version = true;
} }
"--print-loaded-themes" => { "--print-loaded-themes" => {
let s = ui::conf::FileSettings::new()?; let s = conf::FileSettings::new()?;
print!("{}", s.terminal.themes.to_string()); print!("{}", s.terminal.themes.to_string());
return Ok(()); return Ok(());
} }
"--print-default-theme" => { "--print-default-theme" => {
print!( print!("{}", conf::Theme::default().key_to_string("dark", false));
"{}",
ui::conf::Theme::default().key_to_string("dark", false)
);
return Ok(()); return Ok(());
} }
e => match prev { e => match prev {
@ -215,7 +262,7 @@ fn run_app() -> Result<()> {
}; };
if let Some(config_path) = args.test_config.as_ref() { if let Some(config_path) = args.test_config.as_ref() {
ui::conf::FileSettings::validate(config_path)?; conf::FileSettings::validate(config_path)?;
return Ok(()); return Ok(());
} }
@ -235,7 +282,7 @@ fn run_app() -> Result<()> {
if config_path.exists() { if config_path.exists() {
return Err(MeliError::new(format!("File `{}` already exists.\nMaybe you meant to specify another path with --create-config=PATH", config_path.display()))); return Err(MeliError::new(format!("File `{}` already exists.\nMaybe you meant to specify another path with --create-config=PATH", config_path.display())));
} }
ui::conf::create_config_file(&config_path)?; conf::create_config_file(&config_path)?;
return Ok(()); return Ok(());
} }
@ -268,19 +315,17 @@ fn run_app() -> Result<()> {
let status_bar = Box::new(StatusBar::new(window)); let status_bar = Box::new(StatusBar::new(window));
state.register_component(status_bar); state.register_component(status_bar);
let xdg_notifications = Box::new(ui::components::notifications::XDGNotifications::new()); let xdg_notifications = Box::new(components::notifications::XDGNotifications::new());
state.register_component(xdg_notifications); state.register_component(xdg_notifications);
state.register_component(Box::new( state.register_component(Box::new(components::notifications::NotificationFilter {}));
ui::components::notifications::NotificationFilter {},
));
/* Keep track of the input mode. See ui::UIMode for details */ /* Keep track of the input mode. See UIMode for details */
'main: loop { 'main: loop {
state.render(); state.render();
'inner: loop { 'inner: loop {
/* Check if any components have sent reply events to State. */ /* Check if any components have sent reply events to State. */
let events: ui::smallvec::SmallVec<[UIEvent; 8]> = state.context.replies(); let events: smallvec::SmallVec<[UIEvent; 8]> = state.context.replies();
for e in events { for e in events {
state.rcv_event(e); state.rcv_event(e);
} }

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *
@ -19,6 +19,8 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
/*! Search queries.
*/
use melib::parsec::*; use melib::parsec::*;
use melib::UnixTimestamp; use melib::UnixTimestamp;
use melib::{ use melib::{
@ -27,6 +29,7 @@ use melib::{
thread::{SortField, SortOrder}, thread::{SortField, SortOrder},
Result, Result,
}; };
use std::borrow::Cow;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
pub use query_parser::query; pub use query_parser::query;
@ -334,7 +337,6 @@ pub mod query_parser {
pub fn query_to_imap(q: &Query) -> String { pub fn query_to_imap(q: &Query) -> String {
fn rec(q: &Query, s: &mut String) { fn rec(q: &Query, s: &mut String) {
use crate::sqlite3::escape_double_quote;
match q { match q {
Subject(t) => { Subject(t) => {
s.push_str(" SUBJECT \""); s.push_str(" SUBJECT \"");
@ -440,3 +442,12 @@ pub fn imap_search(
panic!("Could not downcast ImapType backend. BUG"); panic!("Could not downcast ImapType backend. BUG");
} }
} }
#[inline(always)]
pub fn escape_double_quote(w: &str) -> Cow<str> {
if w.contains('"') {
Cow::from(w.replace('"', "\"\""))
} else {
Cow::from(w)
}
}

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *
@ -19,11 +19,11 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
/*! /*! Components visual and logical separations of application interfaces.
Components are ways to handle application data. They can draw on the terminal and receive events, but also do other stuff as well. (For example, see the `notifications` module.) *
* They can draw on the terminal and receive events, but also do other stuff as well. (For example, see the `notifications` module.)
See the `Component` Trait for more details. * See the `Component` Trait for more details.
*/ */
use super::*; use super::*;
@ -32,9 +32,6 @@ pub use crate::mail::*;
pub mod notifications; pub mod notifications;
pub mod indexer;
pub use self::indexer::*;
pub mod utilities; pub mod utilities;
pub use self::utilities::*; pub use self::utilities::*;

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *
@ -74,6 +74,7 @@ pub(super) struct EntryStrings {
} }
#[macro_export] #[macro_export]
/// Creates a comma separated list `String` out of an `Address` iterable.
macro_rules! address_list { macro_rules! address_list {
(($name:expr) as comma_sep_list) => {{ (($name:expr) as comma_sep_list) => {{
let mut ret: String = let mut ret: String =

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2019 Manos Pitsidianakis * Copyright 2019 Manos Pitsidianakis
* *

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2020 Manos Pitsidianakis * Copyright 2017-2020 Manos Pitsidianakis
* *

View File

@ -19,6 +19,8 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
/*! Configuration logic and `config.toml` interfaces. */
extern crate bincode; extern crate bincode;
extern crate serde; extern crate serde;
extern crate toml; extern crate toml;
@ -637,7 +639,7 @@ pub fn create_config_file(p: &Path) -> Result<()> {
.create_new(true) .create_new(true)
.open(p) .open(p)
.expect("Could not create config file."); .expect("Could not create config file.");
file.write_all(include_bytes!("../../sample-config")) file.write_all(include_bytes!("../sample-config"))
.expect("Could not write to config file."); .expect("Could not write to config file.");
println!("Written example configuration to {}", p.display()); println!("Written example configuration to {}", p.display());
let metadata = file.metadata()?; let metadata = file.metadata()?;
@ -649,6 +651,7 @@ pub fn create_config_file(p: &Path) -> Result<()> {
} }
mod pp { mod pp {
//! Preprocess configuration files by unfolding `include` macros.
use melib::{ use melib::{
error::{MeliError, Result}, error::{MeliError, Result},
parsec::*, parsec::*,
@ -656,6 +659,7 @@ mod pp {
use std::io::Read; use std::io::Read;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
/// Try to parse line into a path to be included.
fn include_directive<'a>() -> impl Parser<'a, Option<&'a str>> { fn include_directive<'a>() -> impl Parser<'a, Option<&'a str>> {
move |input: &'a str| { move |input: &'a str| {
enum State { enum State {
@ -712,6 +716,7 @@ mod pp {
} }
} }
/// Expands `include` macros in path.
fn pp_helper(path: &Path, level: u8) -> Result<String> { fn pp_helper(path: &Path, level: u8) -> Result<String> {
if level > 7 { if level > 7 {
return Err(MeliError::new(format!("Maximum recursion limit reached while unfolding include directives in {}. Have you included a config file within itself?", path.display()))); return Err(MeliError::new(format!("Maximum recursion limit reached while unfolding include directives in {}. Have you included a config file within itself?", path.display())));
@ -752,6 +757,8 @@ mod pp {
Ok(ret) Ok(ret)
} }
/// Expands `include` macros in configuration file and other configuration files (eg. themes)
/// in the filesystem.
pub fn pp<P: AsRef<Path>>(path: P) -> Result<String> { pub fn pp<P: AsRef<Path>>(path: P) -> Result<String> {
let p_buf: PathBuf = if path.as_ref().is_relative() { let p_buf: PathBuf = if path.as_ref().is_relative() {
path.as_ref().canonicalize()? path.as_ref().canonicalize()?

View File

@ -1106,10 +1106,9 @@ impl Account {
#[cfg(not(feature = "sqlite3"))] #[cfg(not(feature = "sqlite3"))]
{ {
let mut ret = SmallVec::new(); let mut ret = SmallVec::new();
let envelopes = self.collection.envelopes.clone().read(); let envelopes = self.collection.envelopes.read().unwrap();
let envelopes = envelopes.unwrap();
for env_hash in self.folders[folder_hash].as_result()?.envelopes { for &env_hash in &self.folders[&folder_hash].as_result()?.envelopes {
let envelope = &envelopes[&env_hash]; let envelope = &envelopes[&env_hash];
if envelope.subject().contains(&search_term) { if envelope.subject().contains(&search_term) {
ret.push(env_hash); ret.push(env_hash);
@ -1127,7 +1126,7 @@ impl Account {
ret.push(env_hash); ret.push(env_hash);
} }
} }
ret Ok(ret)
} }
} }
} }

View File

@ -18,6 +18,8 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
//! Configuration for composing email.
use super::default_vals::{false_val, none, true_val}; use super::default_vals::{false_val, none, true_val};
/// Settings for writing and sending new e-mail /// Settings for writing and sending new e-mail

View File

@ -19,6 +19,8 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
//! Settings for the pager function.
use super::default_vals::*; use super::default_vals::*;
use super::deserializers::*; use super::deserializers::*;
use melib::ToggleFlag; use melib::ToggleFlag;

View File

@ -19,6 +19,8 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
//! E-mail tag configuration and {de,}serializing.
use crate::terminal::Color; use crate::terminal::Color;
use serde::{Deserialize, Deserializer}; use serde::{Deserialize, Deserializer};
use std::collections::{hash_map::DefaultHasher, HashMap, HashSet}; use std::collections::{hash_map::DefaultHasher, HashMap, HashSet};

View File

@ -19,6 +19,8 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
//! Settings for terminal display
use super::deserializers::non_empty_string; use super::deserializers::non_empty_string;
use super::Theme; use super::Theme;
use super::ToggleFlag; use super::ToggleFlag;

View File

@ -19,6 +19,15 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
//! Application themes.
//!
//! * An attribute is a triple of foreground color, background color and terminal attribute `ThemeValue`s.
//! * A `ThemeValue<T>` is either an actual value or the key name of another value to which it depends. The value is either `Color` or `Attr`.
//! * `ThemeAttributeInner` is an attribute triplet.
//! * `ThemeAttribute` is an attribute triplet with the links resolved.
//!
//! On startup a [DFS](https://en.wikipedia.org/wiki/Depth-first_search) is performed to see if there are any cycles in the link graph.
use crate::terminal::{Attr, Color}; use crate::terminal::{Attr, Color};
use crate::Context; use crate::Context;
use melib::{MeliError, Result}; use melib::{MeliError, Result};
@ -187,6 +196,7 @@ const DEFAULT_KEYS: &'static [&'static str] = &[
"mail.listing.thread_snooze_flag", "mail.listing.thread_snooze_flag",
]; ];
/// `ThemeAttributeInner` but with the links resolved.
#[derive(Debug, PartialEq, Eq, Clone, Default, Copy, Serialize, Deserialize)] #[derive(Debug, PartialEq, Eq, Clone, Default, Copy, Serialize, Deserialize)]
pub struct ThemeAttribute { pub struct ThemeAttribute {
pub fg: Color, pub fg: Color,
@ -194,6 +204,7 @@ pub struct ThemeAttribute {
pub attrs: Attr, pub attrs: Attr,
} }
/// Holds {fore,back}ground color and terminal attribute values.
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ThemeAttributeInner { pub struct ThemeAttributeInner {
#[serde(default)] #[serde(default)]
@ -205,6 +216,7 @@ pub struct ThemeAttributeInner {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
/// Holds either an actual value or refers to the key name of the attribute that holds the value.
pub enum ThemeValue<T> { pub enum ThemeValue<T> {
Value(T), Value(T),
Link(Cow<'static, str>), Link(Cow<'static, str>),

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *
@ -19,7 +19,7 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
/*! A parser module for user commands passed through the Ex mode. /*! A parser module for user commands passed through the Execute mode.
*/ */
use melib::backends::FolderOperation; use melib::backends::FolderOperation;
pub use melib::thread::{SortField, SortOrder}; pub use melib::thread::{SortField, SortOrder};

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2019 Manos Pitsidianakis * Copyright 2019 Manos Pitsidianakis
* *

View File

@ -18,6 +18,9 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
/*! Find mailcap entries to execute attachments.
*/
use crate::split_command; use crate::split_command;
use crate::state::Context; use crate::state::Context;
use crate::types::{create_temp_file, ForkType, UIEvent}; use crate::types::{create_temp_file, ForkType, UIEvent};

View File

@ -19,6 +19,8 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
/*! Plugins are executed by meli and communication is done by `messagepack` IPC.
*/
use melib::error::{MeliError, Result}; use melib::error::{MeliError, Result};
use std::collections::HashMap; use std::collections::HashMap;
use std::io::Write; use std::io::Write;

View File

@ -19,9 +19,10 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
/*! Use an sqlite3 database for fast searching.
*/
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::cache::query; use crate::cache::{escape_double_quote, query, Query::{self, *}};
use crate::cache::Query::{self, *};
use crate::melib::parsec::Parser; use crate::melib::parsec::Parser;
use melib::{ use melib::{
backends::MailBackend, backends::MailBackend,
@ -31,20 +32,10 @@ use melib::{
MeliError, Result, ERROR, MeliError, Result, ERROR,
}; };
use rusqlite::{params, Connection}; use rusqlite::{params, Connection};
use std::borrow::Cow;
use std::path::PathBuf; use std::path::PathBuf;
use std::convert::TryInto; use std::convert::TryInto;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
#[inline(always)]
pub fn escape_double_quote(w: &str) -> Cow<str> {
if w.contains('"') {
Cow::from(w.replace('"', "\"\""))
} else {
Cow::from(w)
}
}
pub fn db_path() -> Result<PathBuf> { pub fn db_path() -> Result<PathBuf> {
let data_dir = let data_dir =
xdg::BaseDirectories::with_prefix("meli").map_err(|e| MeliError::new(e.to_string()))?; xdg::BaseDirectories::with_prefix("meli").map_err(|e| MeliError::new(e.to_string()))?;

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *
@ -641,6 +641,7 @@ impl State {
} }
} }
AccountAction(ref account_name, ReIndex) => { AccountAction(ref account_name, ReIndex) => {
#[cfg(feature = "sqlite3")]
match crate::sqlite3::index(&mut self.context, account_name) { match crate::sqlite3::index(&mut self.context, account_name) {
Ok(()) => { Ok(()) => {
self.context.replies.push_back(UIEvent::Notification( self.context.replies.push_back(UIEvent::Notification(
@ -657,6 +658,15 @@ impl State {
)); ));
} }
} }
#[cfg(not(feature = "sqlite3"))]
{
self.context.replies.push_back(UIEvent::Notification(
None,
"Message index rebuild failed: meli is not built with sqlite3 support."
.to_string(),
Some(NotificationType::ERROR),
));
}
} }
v => { v => {
self.rcv_event(UIEvent::Action(v)); self.rcv_event(UIEvent::Action(v));

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *
@ -18,6 +18,9 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
/*! Terminal grid cells, keys, colors, etc.
*/
extern crate serde; extern crate serde;
use self::serde::de::Visitor; use self::serde::de::Visitor;
use self::serde::{de, Deserialize, Deserializer}; use self::serde::{de, Deserialize, Deserializer};

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *
@ -36,6 +36,9 @@ use termion::color::{AnsiValue, Rgb as TermionRgb};
/// In a scroll region up and down cursor movements shift the region vertically. The new lines are /// In a scroll region up and down cursor movements shift the region vertically. The new lines are
/// empty. /// empty.
///
/// See `CellBuffer::scroll_up` and `CellBuffer::scroll_down` for an explanation of how `xterm`
/// scrolling works.
#[derive(Debug, Clone, PartialEq, Eq, Default)] #[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ScrollRegion { pub struct ScrollRegion {
pub top: usize, pub top: usize,
@ -56,7 +59,9 @@ pub struct CellBuffer {
cols: usize, cols: usize,
rows: usize, rows: usize,
buf: Vec<Cell>, buf: Vec<Cell>,
/// ASCII-only flag.
pub ascii_drawing: bool, pub ascii_drawing: bool,
/// If printing to this buffer and we run out of space, expand it.
growable: bool, growable: bool,
} }
@ -170,7 +175,7 @@ impl CellBuffer {
/// ///
/// # Examples /// # Examples
/// ///
/// ```norun /// ```no_run
/// ///
/// let mut term = Terminal::new().unwrap(); /// let mut term = Terminal::new().unwrap();
/// ///
@ -188,7 +193,7 @@ impl CellBuffer {
/// ///
/// # Examples /// # Examples
/// ///
/// ```norun /// ```no_run
/// ///
/// let mut term = Terminal::new().unwrap(); /// let mut term = Terminal::new().unwrap();
/// ///
@ -427,6 +432,8 @@ impl fmt::Display for CellBuffer {
pub struct Cell { pub struct Cell {
ch: char, ch: char,
/// Set a `Cell` as empty when a previous cell spans multiple columns and it would
/// "overflow" to this cell.
empty: bool, empty: bool,
fg: Color, fg: Color,
bg: Color, bg: Color,
@ -440,8 +447,7 @@ impl Cell {
/// ///
/// # Examples /// # Examples
/// ///
/// ```norun /// ```no_run
///
/// let cell = Cell::new('x', Color::Default, Color::Green, Attr::Default); /// let cell = Cell::new('x', Color::Default, Color::Green, Attr::Default);
/// assert_eq!(cell.ch(), 'x'); /// assert_eq!(cell.ch(), 'x');
/// assert_eq!(cell.fg(), Color::Default); /// assert_eq!(cell.fg(), Color::Default);
@ -464,8 +470,7 @@ impl Cell {
/// ///
/// # Examples /// # Examples
/// ///
/// ```norun /// ```no_run
///
/// let mut cell = Cell::with_char('x'); /// let mut cell = Cell::with_char('x');
/// assert_eq!(cell.ch(), 'x'); /// assert_eq!(cell.ch(), 'x');
/// assert_eq!(cell.fg(), Color::Default); /// assert_eq!(cell.fg(), Color::Default);
@ -480,8 +485,7 @@ impl Cell {
/// ///
/// # Examples /// # Examples
/// ///
/// ```norun /// ```no_run
///
/// let mut cell = Cell::with_style(Color::Default, Color::Red, Attr::Bold); /// let mut cell = Cell::with_style(Color::Default, Color::Red, Attr::Bold);
/// assert_eq!(cell.fg(), Color::Default); /// assert_eq!(cell.fg(), Color::Default);
/// assert_eq!(cell.bg(), Color::Red); /// assert_eq!(cell.bg(), Color::Red);
@ -496,8 +500,7 @@ impl Cell {
/// ///
/// # Examples /// # Examples
/// ///
/// ```norun /// ```no_run
///
/// let mut cell = Cell::with_char('x'); /// let mut cell = Cell::with_char('x');
/// assert_eq!(cell.ch(), 'x'); /// assert_eq!(cell.ch(), 'x');
/// ``` /// ```
@ -509,8 +512,7 @@ impl Cell {
/// ///
/// # Examples /// # Examples
/// ///
/// ```norun /// ```no_run
///
/// let mut cell = Cell::with_char('x'); /// let mut cell = Cell::with_char('x');
/// assert_eq!(cell.ch(), 'x'); /// assert_eq!(cell.ch(), 'x');
/// ///
@ -528,8 +530,7 @@ impl Cell {
/// ///
/// # Examples /// # Examples
/// ///
/// ```norun /// ```no_run
///
/// let mut cell = Cell::with_style(Color::Blue, Color::Default, Attr::Default); /// let mut cell = Cell::with_style(Color::Blue, Color::Default, Attr::Default);
/// assert_eq!(cell.fg(), Color::Blue); /// assert_eq!(cell.fg(), Color::Blue);
/// ``` /// ```
@ -541,8 +542,7 @@ impl Cell {
/// ///
/// # Examples /// # Examples
/// ///
/// ```norun /// ```no_run
///
/// let mut cell = Cell::default(); /// let mut cell = Cell::default();
/// assert_eq!(cell.fg(), Color::Default); /// assert_eq!(cell.fg(), Color::Default);
/// ///
@ -560,7 +560,7 @@ impl Cell {
/// ///
/// # Examples /// # Examples
/// ///
/// ```norun /// ```no_run
/// let mut cell = Cell::with_style(Color::Default, Color::Green, Attr::Default); /// let mut cell = Cell::with_style(Color::Default, Color::Green, Attr::Default);
/// assert_eq!(cell.bg(), Color::Green); /// assert_eq!(cell.bg(), Color::Green);
/// ``` /// ```
@ -572,7 +572,7 @@ impl Cell {
/// ///
/// # Examples /// # Examples
/// ///
/// ```norun /// ```no_run
/// let mut cell = Cell::default(); /// let mut cell = Cell::default();
/// assert_eq!(cell.bg(), Color::Default); /// assert_eq!(cell.bg(), Color::Default);
/// ///
@ -626,8 +626,7 @@ impl Default for Cell {
/// ///
/// # Examples /// # Examples
/// ///
/// ```norun /// ```no_run
///
/// let mut cell = Cell::default(); /// let mut cell = Cell::default();
/// assert_eq!(cell.ch(), ' '); /// assert_eq!(cell.ch(), ' ');
/// assert_eq!(cell.fg(), Color::Default); /// assert_eq!(cell.fg(), Color::Default);
@ -650,8 +649,7 @@ impl Default for Cell {
/// ///
/// # Examples /// # Examples
/// ///
/// ```norun /// ```no_run
///
/// // The default color. /// // The default color.
/// let default = Color::Default; /// let default = Color::Default;
/// ///
@ -676,6 +674,7 @@ pub enum Color {
White, White,
Byte(u8), Byte(u8),
Rgb(u8, u8, u8), Rgb(u8, u8, u8),
/// Terminal default.
Default, Default,
} }
@ -1371,8 +1370,7 @@ impl Serialize for Color {
/// ///
/// # Examples /// # Examples
/// ///
/// ```norun /// ```no_run
///
/// // Default attribute. /// // Default attribute.
/// let def = Attr::Default; /// let def = Attr::Default;
/// ///
@ -1384,6 +1382,7 @@ impl Serialize for Color {
/// ``` /// ```
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Attr { pub enum Attr {
/// Terminal default.
Default = 0b000, Default = 0b000,
Bold = 0b001, Bold = 0b001,
Underline = 0b100, Underline = 0b100,
@ -2003,7 +2002,7 @@ pub mod ansi {
/// the iterator will simply return `None` when it reaches the end of the row. /// the iterator will simply return `None` when it reaches the end of the row.
/// `RowIterator` can be created via the `CellBuffer::row_iter` method and can be returned by /// `RowIterator` can be created via the `CellBuffer::row_iter` method and can be returned by
/// `BoundsIterator` which iterates each row. /// `BoundsIterator` which iterates each row.
/// ```norun /// ```no_run
/// for c in grid.row_iter( /// for c in grid.row_iter(
/// x..(x + 11), /// x..(x + 11),
/// 0, /// 0,
@ -2017,7 +2016,7 @@ pub struct RowIterator {
} }
/// `BoundsIterator` iterates each row returning a `RowIterator`. /// `BoundsIterator` iterates each row returning a `RowIterator`.
/// ```norun /// ```no_run
/// /* Visit each `Cell` in `area`. */ /// /* Visit each `Cell` in `area`. */
/// for c in grid.bounds_iter(area) { /// for c in grid.bounds_iter(area) {
/// grid[c].set_ch('w'); /// grid[c].set_ch('w');

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2020 Manos Pitsidianakis * Copyright 2017-2020 Manos Pitsidianakis
* *

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2020 Manos Pitsidianakis * Copyright 2017-2020 Manos Pitsidianakis
* *

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *
@ -135,6 +135,7 @@ impl PartialEq<Key> for &Key {
} }
#[derive(PartialEq)] #[derive(PartialEq)]
/// Keep track of whether we're accepting normal user input or a pasted string.
enum InputMode { enum InputMode {
Normal, Normal,
Paste, Paste,
@ -142,10 +143,13 @@ enum InputMode {
} }
#[derive(Debug)] #[derive(Debug)]
/// Main process sends commands to the input thread.
pub enum InputCommand { pub enum InputCommand {
/// Exit thread
Kill, Kill,
/// Send Raw bytes as well /// Send raw bytes as well
Raw, Raw,
/// Ignore raw bytes
NoRaw, NoRaw,
} }
@ -158,6 +162,7 @@ use termion::input::TermReadEventsAndRaw;
* *
* The main loop uses try_wait_on_child() to check if child has exited. * The main loop uses try_wait_on_child() to check if child has exited.
*/ */
/// The thread function that listens for user input and forwards it to the main event loop.
pub fn get_events( pub fn get_events(
mut closure: impl FnMut(Key), mut closure: impl FnMut(Key),
closure_raw: impl FnMut((Key, Vec<u8>)), closure_raw: impl FnMut((Key, Vec<u8>)),
@ -202,6 +207,7 @@ pub fn get_events(
} }
} }
/// Same as `get_events` but also forwards the raw bytes of the input as well
pub fn get_events_raw( pub fn get_events_raw(
closure_nonraw: impl FnMut(Key), closure_nonraw: impl FnMut(Key),
mut closure: impl FnMut((Key, Vec<u8>)), mut closure: impl FnMut((Key, Vec<u8>)),

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2020 Manos Pitsidianakis * Copyright 2017-2020 Manos Pitsidianakis
* *

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *
@ -18,6 +18,18 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
/*! UI types used throughout meli.
*
* The `segment_tree` module performs maximum range queries. This is used in getting the maximum
* element of a column within a specific range in e-mail lists. That way a very large value that
* is not the in the currently displayed page does not cause the column to be rendered bigger
* than it has to.
*
* `UIMode` describes the application's... mode. Same as in the modal editor `vi`.
*
* `UIEvent` is the type passed around `Component`s when something happens.
*/
extern crate serde; extern crate serde;
#[macro_use] #[macro_use]
mod helpers; mod helpers;

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2018 Manos Pitsidianakis * Copyright 2017-2018 Manos Pitsidianakis
* *
@ -19,177 +19,29 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
/*! /*! UNIX and POSIX interfaces.
* This library exports the public types and methods of its modules
*/ */
#[macro_use]
extern crate melib;
extern crate notify_rust;
extern crate text_processing;
pub extern crate xdg_utils;
#[macro_use]
extern crate serde_derive;
extern crate linkify;
extern crate uuid;
extern crate fnv;
extern crate termion;
#[macro_use]
extern crate nom;
extern crate serde_json;
pub extern crate smallvec;
use melib::*;
use std::collections::VecDeque;
use text_processing::*;
#[macro_use]
mod types;
pub use crate::types::*;
#[macro_use]
mod terminal;
pub use crate::terminal::*;
#[macro_use]
mod execute;
use crate::execute::*;
pub mod state;
pub use crate::state::*;
pub mod components;
pub use crate::components::*;
#[macro_use]
pub mod conf;
pub use crate::conf::*;
pub mod workers;
pub use crate::workers::*;
#[cfg(feature = "sqlite3")]
pub mod sqlite3;
pub mod cache;
pub mod mailcap;
pub mod plugins;
pub use crate::username::*;
pub mod username {
use libc;
use std::ptr::null_mut;
/* taken from whoami-0.1.1 */
fn getpwuid() -> libc::passwd {
let mut pwentp = null_mut();
#[cfg(target_arch = "aarch64")]
let mut buffer = [0u8; 16384]; // from the man page
#[cfg(not(target_arch = "aarch64"))]
let mut buffer = [0i8; 16384]; // from the man page
#[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd",
target_os = "netbsd"
))]
{
let mut pwent = libc::passwd {
pw_name: null_mut(),
pw_passwd: null_mut(),
pw_uid: 0,
pw_gid: 0,
pw_change: 0,
pw_class: null_mut(),
pw_gecos: null_mut(),
pw_dir: null_mut(),
pw_shell: null_mut(),
pw_expire: 0,
};
unsafe {
libc::getpwuid_r(
libc::geteuid(),
&mut pwent,
&mut buffer[0],
16384,
&mut pwentp,
);
}
pwent
}
#[cfg(target_os = "linux")]
{
let mut pwent = libc::passwd {
pw_name: null_mut(),
pw_passwd: null_mut(),
pw_uid: 0,
pw_gid: 0,
pw_gecos: null_mut(),
pw_dir: null_mut(),
pw_shell: null_mut(),
};
unsafe {
libc::getpwuid_r(
libc::geteuid(),
&mut pwent,
&mut buffer[0],
16384,
&mut pwentp,
);
}
pwent
}
}
#[cfg(target_arch = "aarch64")]
fn ptr_to_string(name: *mut u8) -> String {
let uname = name as *const u8;
let s;
let string;
unsafe {
s = ::std::slice::from_raw_parts(uname, libc::strlen(name));
string = String::from_utf8_lossy(s).to_string();
}
string
}
#[cfg(not(target_arch = "aarch64"))]
fn ptr_to_string(name: *mut i8) -> String {
let uname = name as *mut _ as *mut u8;
let s;
let string;
unsafe {
s = ::std::slice::from_raw_parts(uname, libc::strlen(name));
string = String::from_utf8_lossy(s).to_string();
}
string
}
pub fn username() -> String {
let pwent = getpwuid();
ptr_to_string(pwent.pw_name)
}
}
pub mod timer { pub mod timer {
use super::{MeliError, Result}; //! POSIX timers
//!
//! # Example usage
//! ```no_run
//! let timer = crate::timer::PosixTimer::new_with_signal(
//! std::time::Duration::from_secs(0),
//! std::time::Duration::from_secs(1),
//! nix::sys::signal::Signal::SIGALRM,
//! )
//! .unwrap();
//!
//! // some time passes, we should receive and handle the SIGALRM
//! // The timer remains unarmed since the interval given was zero, until we rearm it explicitly.
//! self.timer.rearm();
//! ```
use libc::clockid_t; use libc::clockid_t;
use libc::sigevent; use libc::sigevent;
use libc::{itimerspec, timespec}; use libc::{itimerspec, timespec};
use melib::{MeliError, Result};
use nix::sys::signal::{SigEvent, SigevNotify}; use nix::sys::signal::{SigEvent, SigevNotify};
use std::cell::RefCell; use std::cell::RefCell;
use std::convert::TryInto; use std::convert::TryInto;
@ -216,8 +68,11 @@ pub mod timer {
#[derive(Debug)] #[derive(Debug)]
pub struct PosixTimer { pub struct PosixTimer {
timer_id: timer_t, timer_id: timer_t,
/// Interval for periodic timer.
interval: Duration, interval: Duration,
/// Time until next expiration.
value: Duration, value: Duration,
/// `si_value` is a byte accessible from the signal handler when it receives signals from this timer.
pub si_value: u8, pub si_value: u8,
} }
@ -230,6 +85,7 @@ pub mod timer {
} }
impl PosixTimer { impl PosixTimer {
/// Arm without changing interval and value.
pub fn rearm(&mut self) { pub fn rearm(&mut self) {
let spec = itimerspec { let spec = itimerspec {
it_interval: timespec { it_interval: timespec {
@ -316,6 +172,7 @@ pub mod timer {
self.interval = interval; self.interval = interval;
} }
/// Arm by changing interval and value.
pub fn arm(&mut self, value: Duration) { pub fn arm(&mut self, value: Duration) {
let spec = itimerspec { let spec = itimerspec {
it_interval: timespec { it_interval: timespec {

View File

@ -1,5 +1,5 @@
/* /*
* meli - ui crate. * meli
* *
* Copyright 2017-2020 Manos Pitsidianakis * Copyright 2017-2020 Manos Pitsidianakis
* *
@ -19,6 +19,8 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
/*! Simple blocking job control.
*/
use crate::types::ThreadEvent; use crate::types::ThreadEvent;
use crossbeam::{ use crossbeam::{
channel::{bounded, unbounded, Sender}, channel::{bounded, unbounded, Sender},

View File

@ -8,9 +8,6 @@ edition = "2018"
[[bin]] [[bin]]
name = "emailparse" name = "emailparse"
path = "src/email_parse.rs" path = "src/email_parse.rs"
[[bin]]
name = "linebreak"
path = "src/linebreak.rs"
[[bin]] [[bin]]
name = "imapconn" name = "imapconn"

View File

@ -1,23 +1,32 @@
extern crate melib; extern crate melib;
use melib::Result;
use melib::*; use melib::*;
use melib::Result; /// Parses e-mail from files and prints the debug information of the parsed `Envelope`
///
/// # Example invocation
/// ```sh
/// ./emailparse /path/to/email [/path/to/email2 /path/to/email3 ..]"
/// ```
fn main() -> Result<()> { fn main() -> Result<()> {
if args.len() == 1 {
eprintln!("Usage: ./emailparse /path/to/email [/path/to/email2 /path/to/email3 ..]");
std::process::exit(1);
}
for i in std::env::args().skip(1) { for i in std::env::args().skip(1) {
println!("i is {}", i); println!("Path is {}", i);
let filename = std::path::PathBuf::from(i); let filename = std::path::PathBuf::from(i);
if filename.is_file() { if filename.exists() && filename.is_file() {
let buffer = std::fs::read_to_string(&filename).expect(&format!( let buffer = std::fs::read_to_string(&filename)
"Something went wrong reading the file {}", .expect(&format!("Something went wrong reading the file {}", i,));
filename.display()
));
let env = Envelope::from_bytes(&buffer.as_bytes(), None).expect("Couldn't parse email"); let env = Envelope::from_bytes(&buffer.as_bytes(), None).expect("Couldn't parse email");
eprintln!("Env is {:#?}", env); println!("Env is {:#?}", env);
eprintln!("{:?}", env.body_bytes(buffer.as_bytes())); println!("{:?}", env.body_bytes(buffer.as_bytes()));
} else { } else {
println!("it's not a file"); println!("{} is not a valid file.", i);
} }
} }
Ok(()) Ok(())

View File

@ -4,6 +4,15 @@ use melib::backends::ImapType;
use melib::AccountSettings; use melib::AccountSettings;
use melib::Result; use melib::Result;
/// Opens an interactive shell on an IMAP server. Suggested use is with rlwrap(1)
///
/// # Example invocation:
/// ```sh
/// ./imap_conn server_hostname server_username server_password server_port");
/// ```
///
/// `danger_accept_invalid_certs` is turned on by default, so no certificate validation is performed.
fn main() -> Result<()> { fn main() -> Result<()> {
let mut args = std::env::args().skip(1).collect::<Vec<String>>(); let mut args = std::env::args().skip(1).collect::<Vec<String>>();
if args.len() != 4 { if args.len() != 4 {

View File

@ -1,6 +1,4 @@
use melib; use melib;
use ui::xdg_utils;
use melib::email::Draft; use melib::email::Draft;
use xdg_utils::query_mime_info; use xdg_utils::query_mime_info;

View File

@ -1,41 +0,0 @@
[package]
name = "ui"
version = "0.4.1"
authors = ["Manos Pitsidianakis <el13635@mail.ntua.gr>"]
workspace = ".."
edition = "2018"
[dependencies]
xdg = "2.1.0" # >:c
serde = "1.0.71"
serde_derive = "1.0.71"
serde_json = "1.0"
toml = "0.5.3"
crossbeam = "0.7.2"
fnv = "1.0.3" # >:c
linkify = "0.3.1" # >:c
melib = { path = "../melib", version = "*" }
xdg-utils = "0.3.0"
nom = "3.2.0"
notify = "4.0.1" # >:c
notify-rust = "^3" # >:c
termion = "1.5.1"
bincode = "1.2.0"
uuid = { version = "0.7.4", features = ["serde", "v4"] }
unicode-segmentation = "1.2.1" # >:c
text_processing = { path = "../text_processing", version = "*" }
libc = {version = "0.2.59", features = ["extra_traits",]}
nix = "0.16.1"
rusqlite = {version = "0.20.0", optional =true }
rmp = "^0.8"
rmpv = { version = "^0.4.2", features=["with-serde",] }
rmp-serde = "^0.14.0"
smallvec = { version = "1.1.0", features = ["serde", ] }
[features]
default = ["sqlite3"]
notmuch = []
sqlite3 = ["rusqlite"]
# Print tracing logs as meli runs
debug-tracing = []

View File

@ -1,134 +0,0 @@
/*
* meli - ui crate.
*
* Copyright 2017-2018 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/>.
*/
/*! Entities that handle Mail specific functions.
*/
use super::*;
pub mod index;
pub use self::index::*;
#[derive(Debug)]
struct MenuEntry {
name: String,
subentries: Vec<MenuEntry>,
index: Index,
}
#[derive(Debug)]
pub struct Indexer {
entries: Vec<MenuEntry>,
dirty: bool,
cursor: Vec<usize>,
id: ComponentId,
}
impl fmt::Display for Indexer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// TODO display subject/info
write!(f, "index")
}
}
impl Default for Indexer {
fn default() -> Self {
Indexer {
entries: Vec::with_capacity(8),
dirty: true,
cursor: Vec::with_capacity(8),
id: ComponentId::new_v4(),
}
}
}
impl Indexer {
fn draw_menu(&mut self, _grid: &mut CellBuffer, _area: Area, _context: &mut Context) {}
}
impl Component for Indexer {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
if !self.is_dirty() {
return;
}
clear_area(grid, area);
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
let total_cols = get_x(bottom_right) - get_x(upper_left);
let index_entity_width = (30 * total_cols) / 100;
let mid = get_x(bottom_right) - index_entity_width;
for i in get_y(upper_left)..=get_y(bottom_right) {
set_and_join_box(grid, (mid, i), BoxBoundary::Vertical);
}
let left_menu_area = (upper_left, (set_x(bottom_right, mid - 1)));
let right_index_area = (set_x(upper_left, mid + 1), bottom_right);
self.draw_menu(grid, left_menu_area, context);
self.entries[self.cursor[0]]
.index
.draw(grid, right_index_area, context);
self.dirty = false;
context.dirty_areas.push_back(area);
}
fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool {
if !self.entries[self.cursor[0]]
.index
.process_event(event, _context)
{
for i in 0..self.entries.len() {
if i == self.cursor[0] {
continue;
}
self.entries[i].index.process_event(event, _context);
}
}
match *event {
UIEvent::ChangeMode(UIMode::Normal) => {
self.dirty = true;
}
UIEvent::Resize => {
self.dirty = true;
}
_ => {}
}
false
}
fn is_dirty(&self) -> bool {
self.dirty
}
fn set_dirty(&mut self, value: bool) {
self.dirty = value;
}
fn id(&self) -> ComponentId {
self.id
}
fn set_id(&mut self, id: ComponentId) {
self.id = id;
}
}

View File

@ -1,185 +0,0 @@
use super::*;
pub trait IndexContent: Component {
/* Handles the drawing of one entry */
fn make_entry(&mut self, idx: usize) -> ();
/* Handles what happens when the user selects an entry in the index listing */
fn enter_entry(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) -> ();
/* Refreshes content */
fn refresh(&mut self, context: &mut Context) -> ();
fn search(&self, term: &str) -> Option<usize>;
}
#[derive(Debug, PartialEq)]
enum IndexState {
//Uninitialized,
Listing,
Unfocused,
//Search,
}
#[derive(Debug)]
pub struct Index {
cursor_pos: usize,
new_cursor_pos: usize,
length: usize,
/// Cache current view.
canvas: CellBuffer,
/// If we must redraw on next redraw event
dirty: bool,
state: IndexState,
content: Box<dyn IndexContent>,
id: ComponentId,
}
impl Index {
fn highlight_line(&self, grid: &mut CellBuffer, area: Area, idx: usize) {
let fg_color = Color::Default;
let bg_color = if self.cursor_pos == idx {
Color::Byte(246)
/* } else if idx % 2 == 0 {
Color::Byte(236)*/
} else {
Color::Default
};
change_colors(grid, area, fg_color, bg_color);
}
}
impl Component for Index {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
if !self.dirty {
return;
}
match self.state {
IndexState::Listing => {
/* rehighlight entries, redraw pages */
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
let rows = get_y(bottom_right) - get_y(upper_left) + 1;
let prev_page_no = (self.cursor_pos).wrapping_div(rows);
let page_no = (self.new_cursor_pos).wrapping_div(rows);
let top_idx = page_no * rows;
if self.new_cursor_pos >= self.length {
self.new_cursor_pos = self.length - 1;
}
/* If cursor position has changed, remove the highlight from the previous position and
* apply it in the new one. */
if self.cursor_pos != self.new_cursor_pos && prev_page_no == page_no {
let old_cursor_pos = self.cursor_pos;
self.cursor_pos = self.new_cursor_pos;
for idx in &[old_cursor_pos, self.new_cursor_pos] {
if *idx >= self.length {
continue; //bounds check
}
let new_area = (
set_y(upper_left, get_y(upper_left) + (*idx % rows)),
set_y(bottom_right, get_y(upper_left) + (*idx % rows)),
);
self.highlight_line(grid, new_area, *idx);
context.dirty_areas.push_back(new_area);
}
return;
} else if self.cursor_pos != self.new_cursor_pos {
self.cursor_pos = self.new_cursor_pos;
}
/* Page_no has changed, so draw new page */
copy_area(
grid,
&self.canvas,
area,
((0, top_idx), (500 - 1, self.length)),
);
self.highlight_line(
grid,
(
(
get_x(upper_left),
get_y(upper_left) + (self.cursor_pos % rows),
),
(
get_x(bottom_right),
get_y(upper_left) + (self.cursor_pos % rows),
),
),
self.cursor_pos,
);
context.dirty_areas.push_back(area);
}
IndexState::Unfocused => {
self.content.draw(grid, area, context);
}
}
self.dirty = false;
return;
}
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
if self.content.process_event(event, context) {
return true;
}
match *event {
UIEvent::Input(Key::Up) => {
if self.cursor_pos > 0 {
self.new_cursor_pos = self.new_cursor_pos.saturating_sub(1);
self.set_dirty(true);
}
return true;
}
UIEvent::Input(Key::Down) => {
if self.length > 0 && self.new_cursor_pos < self.length - 1 {
self.new_cursor_pos += 1;
self.set_dirty(true);
}
return true;
}
UIEvent::Input(Key::Char('\n')) if self.state == IndexState::Listing => {
self.state = IndexState::Unfocused;
self.set_dirty(true);
return true;
}
UIEvent::Input(Key::Char('i')) if self.state == IndexState::Unfocused => {
self.state = IndexState::Listing;
self.set_dirty(true);
return true;
}
UIEvent::ChangeMode(UIMode::Normal) => {
self.set_dirty(true);
}
UIEvent::Resize => {
self.set_dirty(true);
}
_ => {}
}
false
}
fn is_dirty(&self) -> bool {
self.dirty || self.content.is_dirty()
}
fn set_dirty(&mut self, value: bool) {
self.dirty = value;
}
fn id(&self) -> ComponentId {
self.id
}
fn set_id(&mut self, id: ComponentId) {
self.id = id;
}
}
impl fmt::Display for Index {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(&self.content, f)
}
}

View File

@ -1,133 +0,0 @@
#! /usr/bin/env python3
"""
meli - sample plugin
Copyright 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/>.
"""
import msgpack
import socket
import time
import struct
import json
import sys
class IPCError(Exception):
pass
class UnknownMessageClass(IPCError):
pass
class InvalidSerialization(IPCError):
pass
class ConnectionClosed(IPCError):
pass
def _read_objects(sock):
unpacker = msgpack.Unpacker()
ret = []
#reader = socket.socket.makefile(sock, 'rb')
while True:
try:
buf = sock.recv(1024**2)
if not buf:
break
unpacker.feed(buf)
for o in unpacker:
ret.append(o)
except:
break
return ret
#try:
# for unpack in unpacker:
# return unpack
#except Exception as e:
# print("_read_objects error ", e, file=sys.stderr,)
# return None
#finally:
# reader.flush()
def _write_objects(sock, objects):
data = msgpack.packb(objects)
sock.sendall(data)
def _recursive_subclasses(cls):
classmap = {}
for subcls in cls.__subclasses__():
classmap[subcls.__name__] = subcls
classmap.update(_recursive_subclasses(subcls))
return classmap
class Client(object):
def __init__(self, server_address):
self.addr = server_address
if isinstance(self.addr, str):
address_family = socket.AF_UNIX
else:
address_family = socket.AF_INET
self.sock = socket.socket(address_family, socket.SOCK_STREAM)
self.sock.setblocking(0)
def connect(self):
try:
self.sock.connect(self.addr)
print("connected", file=sys.stderr)
except socket.error as msg:
print(msg,file=sys.stderr, )
sys.exit(1)
def close(self):
self.sock.close()
def __enter__(self):
self.connect()
return self
def __exit__(self, exc_type, exc_value, traceback):
self.close()
def send(self, objects):
_write_objects(self.sock, objects)
print("wrote object ", objects, file=sys.stderr)
return self.read()
def read(self):
return _read_objects(self.sock)
if __name__ == "__main__":
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
server_address = './soworkfile'
client = Client(server_address)
client.connect()
client.send({ "version": "dev" })
counter = 0
try:
while True:
message = "This is the message. And this is the well {}.".format(counter)
counter += 1
time.sleep(0.05)
print('sending {!r}'.format(message),file=sys.stderr, )
print('returned :', client.send(message), file=sys.stderr,)
except Exception as msg:
print(msg, file=sys.stderr,)