Cleanup startup error exit paths
Make startup methods return Results so that the main binary can exit cleanly instead of using std::process::exit from arbitrary positions, which exits the process immediately and doesn't run destructors.jmap
parent
aeb9d046a2
commit
321be8555f
77
build.rs
77
build.rs
|
@ -1,77 +0,0 @@
|
||||||
/*
|
|
||||||
* meli - bin.rs
|
|
||||||
*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::prelude::*;
|
|
||||||
use std::io::BufWriter;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
fn main() -> Result<(), std::io::Error> {
|
|
||||||
if let Err(e) = std::fs::create_dir("src/manuals") {
|
|
||||||
if e.kind() != std::io::ErrorKind::AlreadyExists {
|
|
||||||
Err(e)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut build_flag = false;
|
|
||||||
let meli_1_metadata = std::fs::metadata("meli.1")?;
|
|
||||||
if let Ok(metadata) = std::fs::metadata("src/manuals/meli.txt") {
|
|
||||||
if metadata.modified()? < meli_1_metadata.modified()? {
|
|
||||||
build_flag = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Doesn't exist */
|
|
||||||
build_flag = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if build_flag {
|
|
||||||
let output = if let Ok(output) = Command::new("mandoc").args(&["meli.1"]).output() {
|
|
||||||
output.stdout
|
|
||||||
} else {
|
|
||||||
b"mandoc was not found on your system. It's required in order to compile the manual pages into plain text for use with the --*manual command line flags.".to_vec()
|
|
||||||
};
|
|
||||||
let man_path = PathBuf::from("src/manuals/meli.txt");
|
|
||||||
let file = File::create(&man_path)?;
|
|
||||||
BufWriter::new(file).write_all(&output)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut build_flag = false;
|
|
||||||
let meli_conf_5_metadata = std::fs::metadata("meli.conf.5")?;
|
|
||||||
if let Ok(metadata) = std::fs::metadata("src/manuals/meli_conf.txt") {
|
|
||||||
if metadata.modified()? < meli_conf_5_metadata.modified()? {
|
|
||||||
build_flag = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Doesn't exist */
|
|
||||||
build_flag = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if build_flag {
|
|
||||||
let output = if let Ok(output) = Command::new("mandoc").args(&["meli.conf.5"]).output() {
|
|
||||||
output.stdout
|
|
||||||
} else {
|
|
||||||
b"mandoc was not found on your system. It's required in order to compile the manual pages into plain text for use with the --*manual command line flags.".to_vec()
|
|
||||||
};
|
|
||||||
let man_path = PathBuf::from("src/manuals/meli_conf.txt");
|
|
||||||
let file = File::create(&man_path)?;
|
|
||||||
BufWriter::new(file).write_all(&output)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -48,8 +48,12 @@ use std::ops::Deref;
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use std;
|
use std;
|
||||||
|
|
||||||
pub type BackendCreator =
|
pub type BackendCreator = Box<
|
||||||
Box<dyn Fn(&AccountSettings, Box<dyn Fn(&str) -> bool + Send + Sync>) -> Box<dyn MailBackend>>;
|
dyn Fn(
|
||||||
|
&AccountSettings,
|
||||||
|
Box<dyn Fn(&str) -> bool + Send + Sync>,
|
||||||
|
) -> Result<Box<dyn MailBackend>>,
|
||||||
|
>;
|
||||||
|
|
||||||
/// A hashmap containing all available mail backends.
|
/// A hashmap containing all available mail backends.
|
||||||
/// An abstraction over any available backends.
|
/// An abstraction over any available backends.
|
||||||
|
@ -72,28 +76,28 @@ impl Backends {
|
||||||
{
|
{
|
||||||
b.register(
|
b.register(
|
||||||
"maildir".to_string(),
|
"maildir".to_string(),
|
||||||
Box::new(|| Box::new(|f, i| Box::new(MaildirType::new(f, i)))),
|
Box::new(|| Box::new(|f, i| MaildirType::new(f, i))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#[cfg(feature = "mbox_backend")]
|
#[cfg(feature = "mbox_backend")]
|
||||||
{
|
{
|
||||||
b.register(
|
b.register(
|
||||||
"mbox".to_string(),
|
"mbox".to_string(),
|
||||||
Box::new(|| Box::new(|f, i| Box::new(MboxType::new(f, i)))),
|
Box::new(|| Box::new(|f, i| MboxType::new(f, i))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#[cfg(feature = "imap_backend")]
|
#[cfg(feature = "imap_backend")]
|
||||||
{
|
{
|
||||||
b.register(
|
b.register(
|
||||||
"imap".to_string(),
|
"imap".to_string(),
|
||||||
Box::new(|| Box::new(|f, i| Box::new(ImapType::new(f, i)))),
|
Box::new(|| Box::new(|f, i| ImapType::new(f, i))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#[cfg(feature = "notmuch_backend")]
|
#[cfg(feature = "notmuch_backend")]
|
||||||
{
|
{
|
||||||
b.register(
|
b.register(
|
||||||
"notmuch".to_string(),
|
"notmuch".to_string(),
|
||||||
Box::new(|| Box::new(|f, i| Box::new(NotmuchDb::new(f, i)))),
|
Box::new(|| Box::new(|f, i| NotmuchDb::new(f, i))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
b
|
b
|
||||||
|
|
|
@ -112,7 +112,8 @@ impl MailBackend for ImapType {
|
||||||
($tx:expr,$($result:expr)+) => {
|
($tx:expr,$($result:expr)+) => {
|
||||||
$(if let Err(e) = $result {
|
$(if let Err(e) = $result {
|
||||||
$tx.send(AsyncStatus::Payload(Err(e.into()))).unwrap();
|
$tx.send(AsyncStatus::Payload(Err(e.into()))).unwrap();
|
||||||
std::process::exit(1);
|
$tx.send(AsyncStatus::Finished).unwrap();
|
||||||
|
return;
|
||||||
})+
|
})+
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -411,30 +412,29 @@ impl MailBackend for ImapType {
|
||||||
|
|
||||||
macro_rules! get_conf_val {
|
macro_rules! get_conf_val {
|
||||||
($s:ident[$var:literal]) => {
|
($s:ident[$var:literal]) => {
|
||||||
$s.extra.get($var).unwrap_or_else(|| {
|
$s.extra.get($var).ok_or_else(|| {
|
||||||
eprintln!(
|
MeliError::new(format!(
|
||||||
"Configuration error ({}): IMAP connection requires the field `{}` set",
|
"Configuration error ({}): IMAP connection requires the field `{}` set",
|
||||||
$s.name.as_str(),
|
$s.name.as_str(),
|
||||||
$var
|
$var
|
||||||
);
|
))
|
||||||
std::process::exit(1);
|
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
($s:ident[$var:literal], $default:expr) => {
|
($s:ident[$var:literal], $default:expr) => {
|
||||||
$s.extra
|
$s.extra
|
||||||
.get($var)
|
.get($var)
|
||||||
.map(|v| {
|
.map(|v| {
|
||||||
<_>::from_str(v).unwrap_or_else(|_| {
|
<_>::from_str(v).map_err(|e| {
|
||||||
eprintln!(
|
MeliError::new(format!(
|
||||||
"Configuration error ({}): Invalid value for field `{}`: {}",
|
"Configuration error ({}): Invalid value for field `{}`: {}\n{}",
|
||||||
$s.name.as_str(),
|
$s.name.as_str(),
|
||||||
$var,
|
$var,
|
||||||
v,
|
v,
|
||||||
);
|
e
|
||||||
std::process::exit(1);
|
))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| $default)
|
.unwrap_or_else(|| Ok($default))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,21 +442,20 @@ impl ImapType {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
s: &AccountSettings,
|
s: &AccountSettings,
|
||||||
is_subscribed: Box<dyn Fn(&str) -> bool + Send + Sync>,
|
is_subscribed: Box<dyn Fn(&str) -> bool + Send + Sync>,
|
||||||
) -> Self {
|
) -> Result<Box<dyn MailBackend>> {
|
||||||
debug!(s);
|
let server_hostname = get_conf_val!(s["server_hostname"])?;
|
||||||
let server_hostname = get_conf_val!(s["server_hostname"]);
|
let server_username = get_conf_val!(s["server_username"])?;
|
||||||
let server_username = get_conf_val!(s["server_username"]);
|
let server_password = get_conf_val!(s["server_password"])?;
|
||||||
let server_password = get_conf_val!(s["server_password"]);
|
let server_port = get_conf_val!(s["server_port"], 143)?;
|
||||||
let server_port = get_conf_val!(s["server_port"], 143);
|
|
||||||
let use_starttls = get_conf_val!(s["use_starttls"], {
|
let use_starttls = get_conf_val!(s["use_starttls"], {
|
||||||
if server_port == 993 {
|
if server_port == 993 {
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
});
|
})?;
|
||||||
let danger_accept_invalid_certs: bool =
|
let danger_accept_invalid_certs: bool =
|
||||||
get_conf_val!(s["danger_accept_invalid_certs"], false);
|
get_conf_val!(s["danger_accept_invalid_certs"], false)?;
|
||||||
let server_conf = ImapServerConf {
|
let server_conf = ImapServerConf {
|
||||||
server_hostname: server_hostname.to_string(),
|
server_hostname: server_hostname.to_string(),
|
||||||
server_username: server_username.to_string(),
|
server_username: server_username.to_string(),
|
||||||
|
@ -467,7 +466,7 @@ impl ImapType {
|
||||||
};
|
};
|
||||||
let connection = ImapConnection::new_connection(&server_conf);
|
let connection = ImapConnection::new_connection(&server_conf);
|
||||||
|
|
||||||
ImapType {
|
Ok(Box::new(ImapType {
|
||||||
account_name: s.name().to_string(),
|
account_name: s.name().to_string(),
|
||||||
online: Arc::new(Mutex::new(false)),
|
online: Arc::new(Mutex::new(false)),
|
||||||
server_conf,
|
server_conf,
|
||||||
|
@ -481,7 +480,7 @@ impl ImapType {
|
||||||
uid_index: Default::default(),
|
uid_index: Default::default(),
|
||||||
byte_cache: Default::default(),
|
byte_cache: Default::default(),
|
||||||
}),
|
}),
|
||||||
}
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shell(&mut self) {
|
pub fn shell(&mut self) {
|
||||||
|
|
|
@ -509,15 +509,18 @@ impl MailBackend for MaildirType {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaildirType {
|
impl MaildirType {
|
||||||
pub fn new(settings: &AccountSettings, is_subscribed: Box<dyn Fn(&str) -> bool>) -> Self {
|
pub fn new(
|
||||||
|
settings: &AccountSettings,
|
||||||
|
is_subscribed: Box<dyn Fn(&str) -> bool>,
|
||||||
|
) -> Result<Box<dyn MailBackend>> {
|
||||||
let mut folders: FnvHashMap<FolderHash, MaildirFolder> = Default::default();
|
let mut folders: FnvHashMap<FolderHash, MaildirFolder> = Default::default();
|
||||||
fn recurse_folders<P: AsRef<Path>>(
|
fn recurse_folders<P: AsRef<Path>>(
|
||||||
folders: &mut FnvHashMap<FolderHash, MaildirFolder>,
|
folders: &mut FnvHashMap<FolderHash, MaildirFolder>,
|
||||||
settings: &AccountSettings,
|
settings: &AccountSettings,
|
||||||
p: P,
|
p: P,
|
||||||
) -> Vec<FolderHash> {
|
) -> Result<Vec<FolderHash>> {
|
||||||
if !p.as_ref().exists() || !p.as_ref().is_dir() {
|
if !p.as_ref().exists() || !p.as_ref().is_dir() {
|
||||||
eprintln!(
|
return Err(MeliError::new(format!(
|
||||||
"Configuration error: Path \"{}\" {}",
|
"Configuration error: Path \"{}\" {}",
|
||||||
p.as_ref().display(),
|
p.as_ref().display(),
|
||||||
if !p.as_ref().exists() {
|
if !p.as_ref().exists() {
|
||||||
|
@ -525,8 +528,7 @@ impl MaildirType {
|
||||||
} else {
|
} else {
|
||||||
"is not a directory."
|
"is not a directory."
|
||||||
}
|
}
|
||||||
);
|
)));
|
||||||
std::process::exit(1);
|
|
||||||
}
|
}
|
||||||
let mut children = Vec::new();
|
let mut children = Vec::new();
|
||||||
for mut f in fs::read_dir(p).unwrap() {
|
for mut f in fs::read_dir(p).unwrap() {
|
||||||
|
@ -544,7 +546,7 @@ impl MaildirType {
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
&settings,
|
&settings,
|
||||||
) {
|
) {
|
||||||
f.children = recurse_folders(folders, settings, &path);
|
f.children = recurse_folders(folders, settings, &path)?;
|
||||||
f.children
|
f.children
|
||||||
.iter()
|
.iter()
|
||||||
.map(|c| folders.get_mut(c).map(|f| f.parent = Some(f.hash)))
|
.map(|c| folders.get_mut(c).map(|f| f.parent = Some(f.hash)))
|
||||||
|
@ -556,23 +558,21 @@ impl MaildirType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
children
|
Ok(children)
|
||||||
};
|
};
|
||||||
let root_path = PathBuf::from(settings.root_folder()).expand();
|
let root_path = PathBuf::from(settings.root_folder()).expand();
|
||||||
if !root_path.exists() {
|
if !root_path.exists() {
|
||||||
eprintln!(
|
return Err(MeliError::new(format!(
|
||||||
"Configuration error ({}): root_path `{}` is not a valid directory.",
|
"Configuration error ({}): root_path `{}` is not a valid directory.",
|
||||||
settings.name(),
|
settings.name(),
|
||||||
settings.root_folder.as_str()
|
settings.root_folder.as_str()
|
||||||
);
|
)));
|
||||||
std::process::exit(1);
|
|
||||||
} else if !root_path.is_dir() {
|
} else if !root_path.is_dir() {
|
||||||
eprintln!(
|
return Err(MeliError::new(format!(
|
||||||
"Configuration error ({}): root_path `{}` is not a directory.",
|
"Configuration error ({}): root_path `{}` is not a directory.",
|
||||||
settings.name(),
|
settings.name(),
|
||||||
settings.root_folder.as_str()
|
settings.root_folder.as_str()
|
||||||
);
|
)));
|
||||||
std::process::exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(f) = MaildirFolder::new(
|
if let Ok(f) = MaildirFolder::new(
|
||||||
|
@ -586,14 +586,14 @@ impl MaildirType {
|
||||||
}
|
}
|
||||||
|
|
||||||
if folders.is_empty() {
|
if folders.is_empty() {
|
||||||
let children = recurse_folders(&mut folders, settings, &root_path);
|
let children = recurse_folders(&mut folders, settings, &root_path)?;
|
||||||
children
|
children
|
||||||
.iter()
|
.iter()
|
||||||
.map(|c| folders.get_mut(c).map(|f| f.parent = None))
|
.map(|c| folders.get_mut(c).map(|f| f.parent = None))
|
||||||
.count();
|
.count();
|
||||||
} else {
|
} else {
|
||||||
let root_hash = *folders.keys().nth(0).unwrap();
|
let root_hash = *folders.keys().nth(0).unwrap();
|
||||||
let children = recurse_folders(&mut folders, settings, &root_path);
|
let children = recurse_folders(&mut folders, settings, &root_path)?;
|
||||||
children
|
children
|
||||||
.iter()
|
.iter()
|
||||||
.map(|c| folders.get_mut(c).map(|f| f.parent = Some(root_hash)))
|
.map(|c| folders.get_mut(c).map(|f| f.parent = Some(root_hash)))
|
||||||
|
@ -606,29 +606,24 @@ impl MaildirType {
|
||||||
f.children.retain(|c| keys.contains(c));
|
f.children.retain(|c| keys.contains(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
let hash_indexes = Arc::new(Mutex::new(FnvHashMap::with_capacity_and_hasher(
|
let mut hash_indexes =
|
||||||
folders.len(),
|
FnvHashMap::with_capacity_and_hasher(folders.len(), Default::default());
|
||||||
Default::default(),
|
for &fh in folders.keys() {
|
||||||
)));
|
hash_indexes.insert(
|
||||||
{
|
fh,
|
||||||
let mut hash_indexes = hash_indexes.lock().unwrap();
|
HashIndex {
|
||||||
for &fh in folders.keys() {
|
index: FnvHashMap::with_capacity_and_hasher(0, Default::default()),
|
||||||
hash_indexes.insert(
|
hash: fh,
|
||||||
fh,
|
},
|
||||||
HashIndex {
|
);
|
||||||
index: FnvHashMap::with_capacity_and_hasher(0, Default::default()),
|
|
||||||
hash: fh,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
MaildirType {
|
Ok(Box::new(MaildirType {
|
||||||
name: settings.name().to_string(),
|
name: settings.name().to_string(),
|
||||||
folders,
|
folders,
|
||||||
hash_indexes,
|
hash_indexes: Arc::new(Mutex::new(hash_indexes)),
|
||||||
folder_index: Default::default(),
|
folder_index: Default::default(),
|
||||||
path: root_path,
|
path: root_path,
|
||||||
}
|
}))
|
||||||
}
|
}
|
||||||
fn owned_folder_idx(&self, folder: &Folder) -> FolderHash {
|
fn owned_folder_idx(&self, folder: &Folder) -> FolderHash {
|
||||||
*self
|
*self
|
||||||
|
|
|
@ -562,14 +562,17 @@ impl MailBackend for MboxType {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MboxType {
|
impl MboxType {
|
||||||
pub fn new(s: &AccountSettings, _is_subscribed: Box<dyn Fn(&str) -> bool>) -> Self {
|
pub fn new(
|
||||||
|
s: &AccountSettings,
|
||||||
|
_is_subscribed: Box<dyn Fn(&str) -> bool>,
|
||||||
|
) -> Result<Box<dyn MailBackend>> {
|
||||||
let path = Path::new(s.root_folder.as_str()).expand();
|
let path = Path::new(s.root_folder.as_str()).expand();
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
panic!(
|
return Err(MeliError::new(format!(
|
||||||
"\"root_folder\" {} for account {} is not a valid path.",
|
"\"root_folder\" {} for account {} is not a valid path.",
|
||||||
s.root_folder.as_str(),
|
s.root_folder.as_str(),
|
||||||
s.name()
|
s.name()
|
||||||
);
|
)));
|
||||||
}
|
}
|
||||||
let ret = MboxType {
|
let ret = MboxType {
|
||||||
path,
|
path,
|
||||||
|
@ -640,6 +643,6 @@ impl MboxType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
ret
|
Ok(Box::new(ret))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,10 @@ unsafe impl Send for NotmuchFolder {}
|
||||||
unsafe impl Sync for NotmuchFolder {}
|
unsafe impl Sync for NotmuchFolder {}
|
||||||
|
|
||||||
impl NotmuchDb {
|
impl NotmuchDb {
|
||||||
pub fn new(s: &AccountSettings, _is_subscribed: Box<dyn Fn(&str) -> bool>) -> Self {
|
pub fn new(
|
||||||
|
s: &AccountSettings,
|
||||||
|
_is_subscribed: Box<dyn Fn(&str) -> bool>,
|
||||||
|
) -> Result<Box<dyn MailBackend>> {
|
||||||
let mut database: *mut notmuch_database_t = std::ptr::null_mut();
|
let mut database: *mut notmuch_database_t = std::ptr::null_mut();
|
||||||
let path = Path::new(s.root_folder.as_str()).expand().to_path_buf();
|
let path = Path::new(s.root_folder.as_str()).expand().to_path_buf();
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
|
@ -125,7 +128,7 @@ impl NotmuchDb {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if status != 0 {
|
if status != 0 {
|
||||||
panic!("notmuch_database_open returned {}.", status);
|
return Err(MeliError::new(format!("Could not open notmuch database at path {}. notmuch_database_open returned {}.", s.root_folder.as_str(), status);
|
||||||
}
|
}
|
||||||
assert!(!database.is_null());
|
assert!(!database.is_null());
|
||||||
let mut folders = FnvHashMap::default();
|
let mut folders = FnvHashMap::default();
|
||||||
|
@ -150,14 +153,13 @@ impl NotmuchDb {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
eprintln!(
|
return Err(MeliError::new(format!(
|
||||||
"notmuch folder configuration entry \"{}\" should have a \"query\" value set.",
|
"notmuch folder configuration entry \"{}\" should have a \"query\" value set.",
|
||||||
k
|
k
|
||||||
);
|
)));
|
||||||
std::process::exit(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NotmuchDb {
|
Ok(Box::new(NotmuchDb {
|
||||||
database: DbWrapper {
|
database: DbWrapper {
|
||||||
inner: Arc::new(RwLock::new(database)),
|
inner: Arc::new(RwLock::new(database)),
|
||||||
database_ph: std::marker::PhantomData,
|
database_ph: std::marker::PhantomData,
|
||||||
|
@ -166,7 +168,7 @@ impl NotmuchDb {
|
||||||
index: Arc::new(RwLock::new(Default::default())),
|
index: Arc::new(RwLock::new(Default::default())),
|
||||||
folders: Arc::new(RwLock::new(folders)),
|
folders: Arc::new(RwLock::new(folders)),
|
||||||
save_messages_to: None,
|
save_messages_to: None,
|
||||||
}
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
40
src/bin.rs
40
src/bin.rs
|
@ -70,8 +70,7 @@ fn notify(
|
||||||
|
|
||||||
macro_rules! error_and_exit {
|
macro_rules! error_and_exit {
|
||||||
($($err:expr),*) => {{
|
($($err:expr),*) => {{
|
||||||
eprintln!($($err),*);
|
return Err(MeliError::new(format!($($err),*)));
|
||||||
std::process::exit(1);
|
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +82,17 @@ struct CommandLineArguments {
|
||||||
version: bool,
|
version: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> std::result::Result<(), std::io::Error> {
|
fn main() {
|
||||||
|
::std::process::exit(match run_app() {
|
||||||
|
Ok(()) => 0,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("{}", err);
|
||||||
|
1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_app() -> Result<()> {
|
||||||
enum CommandLineFlags {
|
enum CommandLineFlags {
|
||||||
CreateConfig,
|
CreateConfig,
|
||||||
Config,
|
Config,
|
||||||
|
@ -144,12 +153,12 @@ fn main() -> std::result::Result<(), std::io::Error> {
|
||||||
println!("\t--version, -v\t\tprint version and exit");
|
println!("\t--version, -v\t\tprint version and exit");
|
||||||
println!("\t--create-config[ PATH]\tCreate a sample configuration file with available configuration options. If PATH is not specified, meli will try to create it in $XDG_CONFIG_HOME/meli/config");
|
println!("\t--create-config[ PATH]\tCreate a sample configuration file with available configuration options. If PATH is not specified, meli will try to create it in $XDG_CONFIG_HOME/meli/config");
|
||||||
println!("\t--config PATH, -c PATH\tUse specified configuration file");
|
println!("\t--config PATH, -c PATH\tUse specified configuration file");
|
||||||
std::process::exit(0);
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.version {
|
if args.version {
|
||||||
println!("meli {}", option_env!("CARGO_PKG_VERSION").unwrap_or("0.0"));
|
println!("meli {}", option_env!("CARGO_PKG_VERSION").unwrap_or("0.0"));
|
||||||
std::process::exit(0);
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
match prev {
|
match prev {
|
||||||
|
@ -162,25 +171,28 @@ fn main() -> std::result::Result<(), std::io::Error> {
|
||||||
if let Some(config_path) = args.create_config.as_mut() {
|
if let Some(config_path) = args.create_config.as_mut() {
|
||||||
let config_path: PathBuf = if config_path.is_empty() {
|
let config_path: PathBuf = if config_path.is_empty() {
|
||||||
let xdg_dirs = xdg::BaseDirectories::with_prefix("meli").unwrap();
|
let xdg_dirs = xdg::BaseDirectories::with_prefix("meli").unwrap();
|
||||||
xdg_dirs.place_config_file("config").unwrap_or_else(|e| {
|
xdg_dirs.place_config_file("config").map_err(|e| {
|
||||||
error_and_exit!("Cannot create configuration directory:\n{}", e)
|
MeliError::new(format!(
|
||||||
})
|
"Cannot create configuration directory in {}:\n{}",
|
||||||
|
xdg_dirs.get_config_home().display(),
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?
|
||||||
} else {
|
} else {
|
||||||
Path::new(config_path).to_path_buf()
|
Path::new(config_path).to_path_buf()
|
||||||
};
|
};
|
||||||
if config_path.exists() {
|
if config_path.exists() {
|
||||||
println!("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())));
|
||||||
std::process::exit(1);
|
|
||||||
}
|
}
|
||||||
let mut file = std::fs::OpenOptions::new()
|
let mut file = std::fs::OpenOptions::new()
|
||||||
.write(true)
|
.write(true)
|
||||||
.create_new(true)
|
.create_new(true)
|
||||||
.open(config_path.as_path())
|
.open(config_path.as_path())
|
||||||
.unwrap_or_else(|e| error_and_exit!("Could not create config file:\n{}", e));
|
.map_err(|e| MeliError::new(format!("Could not create config file:\n{}", e)))?;
|
||||||
file.write_all(include_bytes!("../sample-config"))
|
file.write_all(include_bytes!("../sample-config"))
|
||||||
.unwrap_or_else(|e| error_and_exit!("Could not write to config file:\n{}", e));
|
.map_err(|e| MeliError::new(format!("Could not write to config file:\n{}", e)))?;
|
||||||
println!("Written example configuration to {}", config_path.display());
|
println!("Written example configuration to {}", config_path.display());
|
||||||
std::process::exit(0);
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(config_location) = args.config.as_ref() {
|
if let Some(config_location) = args.config.as_ref() {
|
||||||
|
@ -188,7 +200,7 @@ fn main() -> std::result::Result<(), std::io::Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create the application State. */
|
/* Create the application State. */
|
||||||
let mut state = State::new();
|
let mut state = State::new()?;
|
||||||
|
|
||||||
let receiver = state.receiver();
|
let receiver = state.receiver();
|
||||||
let sender = state.sender();
|
let sender = state.sender();
|
||||||
|
|
|
@ -2,13 +2,13 @@ const LINE_BREAK_TABLE_URL: &str = "http://www.unicode.org/Public/UCD/latest/ucd
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
include!("src/types.rs");
|
include!("src/types.rs");
|
||||||
|
|
||||||
fn main() -> Result<(), std::io::Error> {
|
fn main() -> Result<(), std::io::Error> {
|
||||||
let mod_path = PathBuf::from("src/tables.rs");
|
let mod_path = Path::new("src/tables.rs");
|
||||||
if mod_path.exists() {
|
if mod_path.exists() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{} already exists, delete it if you want to replace it.",
|
"{} already exists, delete it if you want to replace it.",
|
||||||
|
|
|
@ -312,10 +312,12 @@ impl FileSettings {
|
||||||
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 config to {}", config_path.display());
|
println!("Written config to {}", config_path.display());
|
||||||
std::process::exit(1);
|
return Err(MeliError::new(
|
||||||
|
"Edit the sample configuration and relaunch meli.",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
"n" | "N" | "no" | "No" | "NO" => {
|
"n" | "N" | "no" | "No" | "NO" => {
|
||||||
std::process::exit(1);
|
return Err(MeliError::new("No configuration file found."));
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
println!(
|
println!(
|
||||||
|
@ -332,8 +334,10 @@ impl FileSettings {
|
||||||
file.read_to_string(&mut contents)?;
|
file.read_to_string(&mut contents)?;
|
||||||
let s = toml::from_str(&contents);
|
let s = toml::from_str(&contents);
|
||||||
if let Err(e) = s {
|
if let Err(e) = s {
|
||||||
eprintln!("Config file contains errors: {}", e.to_string());
|
return Err(MeliError::new(format!(
|
||||||
std::process::exit(1);
|
"Config file contains errors: {}",
|
||||||
|
e.to_string()
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(s.unwrap())
|
Ok(s.unwrap())
|
||||||
|
@ -341,11 +345,8 @@ impl FileSettings {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Settings {
|
impl Settings {
|
||||||
pub fn new() -> Settings {
|
pub fn new() -> Result<Settings> {
|
||||||
let fs = FileSettings::new().unwrap_or_else(|e| {
|
let fs = FileSettings::new()?;
|
||||||
eprintln!("Configuration error: {}", e);
|
|
||||||
std::process::exit(1);
|
|
||||||
});
|
|
||||||
let mut s: HashMap<String, AccountConf> = HashMap::new();
|
let mut s: HashMap<String, AccountConf> = HashMap::new();
|
||||||
|
|
||||||
for (id, x) in fs.accounts {
|
for (id, x) in fs.accounts {
|
||||||
|
@ -355,7 +356,7 @@ impl Settings {
|
||||||
s.insert(id, ac);
|
s.insert(id, ac);
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings {
|
Ok(Settings {
|
||||||
accounts: s,
|
accounts: s,
|
||||||
pager: fs.pager,
|
pager: fs.pager,
|
||||||
notifications: fs.notifications,
|
notifications: fs.notifications,
|
||||||
|
@ -363,7 +364,7 @@ impl Settings {
|
||||||
composing: fs.composing,
|
composing: fs.composing,
|
||||||
pgp: fs.pgp,
|
pgp: fs.pgp,
|
||||||
terminal: fs.terminal,
|
terminal: fs.terminal,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -227,7 +227,7 @@ impl Account {
|
||||||
map: &Backends,
|
map: &Backends,
|
||||||
work_context: WorkContext,
|
work_context: WorkContext,
|
||||||
notify_fn: NotifyFn,
|
notify_fn: NotifyFn,
|
||||||
) -> Self {
|
) -> Result<Self> {
|
||||||
let s = settings.clone();
|
let s = settings.clone();
|
||||||
let backend = map.get(settings.account().format())(
|
let backend = map.get(settings.account().format())(
|
||||||
settings.account(),
|
settings.account(),
|
||||||
|
@ -235,7 +235,7 @@ impl Account {
|
||||||
s.folder_confs.contains_key(path)
|
s.folder_confs.contains_key(path)
|
||||||
&& s.folder_confs[path].folder_conf().subscribe.is_true()
|
&& s.folder_confs[path].folder_conf().subscribe.is_true()
|
||||||
}),
|
}),
|
||||||
);
|
)?;
|
||||||
let notify_fn = Arc::new(notify_fn);
|
let notify_fn = Arc::new(notify_fn);
|
||||||
|
|
||||||
let data_dir = xdg::BaseDirectories::with_profile("meli", &name).unwrap();
|
let data_dir = xdg::BaseDirectories::with_profile("meli", &name).unwrap();
|
||||||
|
@ -258,7 +258,7 @@ impl Account {
|
||||||
settings.conf.cache_type = crate::conf::CacheType::None;
|
settings.conf.cache_type = crate::conf::CacheType::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Account {
|
Ok(Account {
|
||||||
index,
|
index,
|
||||||
name,
|
name,
|
||||||
is_online: false,
|
is_online: false,
|
||||||
|
@ -278,7 +278,7 @@ impl Account {
|
||||||
notify_fn,
|
notify_fn,
|
||||||
|
|
||||||
event_queue: VecDeque::with_capacity(8),
|
event_queue: VecDeque::with_capacity(8),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self) {
|
fn init(&mut self) {
|
||||||
|
|
|
@ -199,14 +199,8 @@ impl Drop for State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for State {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Result<Self> {
|
||||||
/* Create a channel to communicate with other threads. The main process is the sole receiver.
|
/* Create a channel to communicate with other threads. The main process is the sole receiver.
|
||||||
* */
|
* */
|
||||||
let (sender, receiver) = bounded(32 * ::std::mem::size_of::<ThreadEvent>());
|
let (sender, receiver) = bounded(32 * ::std::mem::size_of::<ThreadEvent>());
|
||||||
|
@ -217,13 +211,11 @@ impl State {
|
||||||
* */
|
* */
|
||||||
let input_thread = unbounded();
|
let input_thread = unbounded();
|
||||||
let backends = Backends::new();
|
let backends = Backends::new();
|
||||||
let settings = Settings::new();
|
let settings = Settings::new()?;
|
||||||
|
|
||||||
let termsize = termion::terminal_size().ok();
|
let termsize = termion::terminal_size()?;
|
||||||
let termcols = termsize.map(|(w, _)| w);
|
let cols = termsize.0 as usize;
|
||||||
let termrows = termsize.map(|(_, h)| h);
|
let rows = termsize.1 as usize;
|
||||||
let cols = termcols.unwrap_or(0) as usize;
|
|
||||||
let rows = termrows.unwrap_or(0) as usize;
|
|
||||||
|
|
||||||
let work_controller = WorkController::new(sender.clone());
|
let work_controller = WorkController::new(sender.clone());
|
||||||
let mut accounts: Vec<Account> = settings
|
let mut accounts: Vec<Account> = settings
|
||||||
|
@ -245,7 +237,7 @@ impl State {
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect::<Result<Vec<Account>>>()?;
|
||||||
accounts.sort_by(|a, b| a.name().cmp(&b.name()));
|
accounts.sort_by(|a, b| a.name().cmp(&b.name()));
|
||||||
|
|
||||||
let mut s = State {
|
let mut s = State {
|
||||||
|
@ -284,10 +276,15 @@ impl State {
|
||||||
s.switch_to_alternate_screen();
|
s.switch_to_alternate_screen();
|
||||||
debug!("inserting mailbox hashes:");
|
debug!("inserting mailbox hashes:");
|
||||||
for i in 0..s.context.accounts.len() {
|
for i in 0..s.context.accounts.len() {
|
||||||
s.context.is_online(i);
|
if s.context.is_online(i) && s.context.accounts[i].is_empty() {
|
||||||
|
return Err(MeliError::new(format!(
|
||||||
|
"Account {} has no folders configured.",
|
||||||
|
s.context.accounts[i].name()
|
||||||
|
)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
s.context.restore_input();
|
s.context.restore_input();
|
||||||
s
|
Ok(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue