bin: add backend specific validation functions for --test-config flag
parent
4677f9c6bb
commit
ba52c59859
|
@ -58,7 +58,12 @@ pub type BackendCreator = Box<
|
|||
/// A hashmap containing all available mail backends.
|
||||
/// An abstraction over any available backends.
|
||||
pub struct Backends {
|
||||
map: FnvHashMap<std::string::String, Box<dyn Fn() -> BackendCreator>>,
|
||||
map: FnvHashMap<std::string::String, Backend>,
|
||||
}
|
||||
|
||||
pub struct Backend {
|
||||
pub create_fn: Box<dyn Fn() -> BackendCreator>,
|
||||
pub validate_conf_fn: Box<dyn Fn(&AccountSettings) -> Result<()>>,
|
||||
}
|
||||
|
||||
impl Default for Backends {
|
||||
|
@ -76,28 +81,40 @@ impl Backends {
|
|||
{
|
||||
b.register(
|
||||
"maildir".to_string(),
|
||||
Box::new(|| Box::new(|f, i| MaildirType::new(f, i))),
|
||||
Backend {
|
||||
create_fn: Box::new(|| Box::new(|f, i| MaildirType::new(f, i))),
|
||||
validate_conf_fn: Box::new(MaildirType::validate_config),
|
||||
},
|
||||
);
|
||||
}
|
||||
#[cfg(feature = "mbox_backend")]
|
||||
{
|
||||
b.register(
|
||||
"mbox".to_string(),
|
||||
Box::new(|| Box::new(|f, i| MboxType::new(f, i))),
|
||||
Backend {
|
||||
create_fn: Box::new(|| Box::new(|f, i| MboxType::new(f, i))),
|
||||
validate_conf_fn: Box::new(MboxType::validate_config),
|
||||
},
|
||||
);
|
||||
}
|
||||
#[cfg(feature = "imap_backend")]
|
||||
{
|
||||
b.register(
|
||||
"imap".to_string(),
|
||||
Box::new(|| Box::new(|f, i| ImapType::new(f, i))),
|
||||
Backend {
|
||||
create_fn: Box::new(|| Box::new(|f, i| ImapType::new(f, i))),
|
||||
validate_conf_fn: Box::new(ImapType::validate_config),
|
||||
},
|
||||
);
|
||||
}
|
||||
#[cfg(feature = "notmuch_backend")]
|
||||
{
|
||||
b.register(
|
||||
"notmuch".to_string(),
|
||||
Box::new(|| Box::new(|f, i| NotmuchDb::new(f, i))),
|
||||
Backend {
|
||||
create_fn: Box::new(|| Box::new(|f, i| NotmuchDb::new(f, i))),
|
||||
validate_conf_fn: Box::new(NotmuchDb::validate_config),
|
||||
},
|
||||
);
|
||||
}
|
||||
b
|
||||
|
@ -107,15 +124,23 @@ impl Backends {
|
|||
if !self.map.contains_key(key) {
|
||||
panic!("{} is not a valid mail backend", key);
|
||||
}
|
||||
self.map[key]()
|
||||
(self.map[key].create_fn)()
|
||||
}
|
||||
|
||||
pub fn register(&mut self, key: String, backend: Box<dyn Fn() -> BackendCreator>) {
|
||||
pub fn register(&mut self, key: String, backend: Backend) {
|
||||
if self.map.contains_key(&key) {
|
||||
panic!("{} is an already registered backend", key);
|
||||
}
|
||||
self.map.insert(key, backend);
|
||||
}
|
||||
|
||||
pub fn validate_config(&self, key: &str, s: &AccountSettings) -> Result<()> {
|
||||
(self
|
||||
.map
|
||||
.get(key)
|
||||
.ok_or_else(|| MeliError::new(format!("{} is not a valid mail backend", key)))?
|
||||
.validate_conf_fn)(s)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -84,6 +84,34 @@ impl std::ops::Deref for IsSubscribedFn {
|
|||
}
|
||||
type Capabilities = FnvHashSet<Vec<u8>>;
|
||||
|
||||
macro_rules! get_conf_val {
|
||||
($s:ident[$var:literal]) => {
|
||||
$s.extra.get($var).ok_or_else(|| {
|
||||
MeliError::new(format!(
|
||||
"Configuration error ({}): IMAP connection requires the field `{}` set",
|
||||
$s.name.as_str(),
|
||||
$var
|
||||
))
|
||||
})
|
||||
};
|
||||
($s:ident[$var:literal], $default:expr) => {
|
||||
$s.extra
|
||||
.get($var)
|
||||
.map(|v| {
|
||||
<_>::from_str(v).map_err(|e| {
|
||||
MeliError::new(format!(
|
||||
"Configuration error ({}): Invalid value for field `{}`: {}\n{}",
|
||||
$s.name.as_str(),
|
||||
$var,
|
||||
v,
|
||||
e
|
||||
))
|
||||
})
|
||||
})
|
||||
.unwrap_or_else(|| Ok($default))
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UIDStore {
|
||||
uidvalidity: Arc<Mutex<FnvHashMap<FolderHash, UID>>>,
|
||||
|
@ -313,7 +341,9 @@ impl MailBackend for ImapType {
|
|||
let path = {
|
||||
let folders = self.folders.read().unwrap();
|
||||
|
||||
let f_result = folders.values().find(|v| v.name == folder);
|
||||
let f_result = folders
|
||||
.values()
|
||||
.find(|v| v.path == folder || v.name == folder);
|
||||
if f_result
|
||||
.map(|f| !f.permissions.lock().unwrap().create_messages)
|
||||
.unwrap_or(false)
|
||||
|
@ -417,34 +447,6 @@ impl MailBackend for ImapType {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! get_conf_val {
|
||||
($s:ident[$var:literal]) => {
|
||||
$s.extra.get($var).ok_or_else(|| {
|
||||
MeliError::new(format!(
|
||||
"Configuration error ({}): IMAP connection requires the field `{}` set",
|
||||
$s.name.as_str(),
|
||||
$var
|
||||
))
|
||||
})
|
||||
};
|
||||
($s:ident[$var:literal], $default:expr) => {
|
||||
$s.extra
|
||||
.get($var)
|
||||
.map(|v| {
|
||||
<_>::from_str(v).map_err(|e| {
|
||||
MeliError::new(format!(
|
||||
"Configuration error ({}): Invalid value for field `{}`: {}\n{}",
|
||||
$s.name.as_str(),
|
||||
$var,
|
||||
v,
|
||||
e
|
||||
))
|
||||
})
|
||||
})
|
||||
.unwrap_or_else(|| Ok($default))
|
||||
};
|
||||
}
|
||||
|
||||
impl ImapType {
|
||||
pub fn new(
|
||||
s: &AccountSettings,
|
||||
|
@ -609,4 +611,14 @@ impl ImapType {
|
|||
}
|
||||
Err(MeliError::new(response))
|
||||
}
|
||||
|
||||
pub fn validate_config(s: &AccountSettings) -> Result<()> {
|
||||
get_conf_val!(s["server_hostname"])?;
|
||||
get_conf_val!(s["server_username"])?;
|
||||
get_conf_val!(s["server_password"])?;
|
||||
get_conf_val!(s["server_port"], 143)?;
|
||||
get_conf_val!(s["use_starttls"], false)?;
|
||||
get_conf_val!(s["danger_accept_invalid_certs"], false)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -877,6 +877,25 @@ impl MaildirType {
|
|||
writer.write_all(bytes).unwrap();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn validate_config(s: &AccountSettings) -> Result<()> {
|
||||
let root_path = PathBuf::from(s.root_folder()).expand();
|
||||
if !root_path.exists() {
|
||||
return Err(MeliError::new(format!(
|
||||
"Configuration error ({}): root_path `{}` is not a valid directory.",
|
||||
s.name(),
|
||||
s.root_folder.as_str()
|
||||
)));
|
||||
} else if !root_path.is_dir() {
|
||||
return Err(MeliError::new(format!(
|
||||
"Configuration error ({}): root_path `{}` is not a directory.",
|
||||
s.name(),
|
||||
s.root_folder.as_str()
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn add_path_to_index(
|
||||
|
|
|
@ -432,9 +432,14 @@ impl MailBackend for MboxType {
|
|||
work_context: WorkContext,
|
||||
) -> Result<std::thread::ThreadId> {
|
||||
let (tx, rx) = channel();
|
||||
let mut watcher = watcher(tx, std::time::Duration::from_secs(10)).unwrap();
|
||||
let mut watcher = watcher(tx, std::time::Duration::from_secs(10))
|
||||
.map_err(|e| e.to_string())
|
||||
.map_err(MeliError::new)?;
|
||||
for f in self.folders.lock().unwrap().values() {
|
||||
watcher.watch(&f.path, RecursiveMode::Recursive).unwrap();
|
||||
watcher
|
||||
.watch(&f.path, RecursiveMode::Recursive)
|
||||
.map_err(|e| e.to_string())
|
||||
.map_err(MeliError::new)?;
|
||||
debug!("watching {:?}", f.path.as_path());
|
||||
}
|
||||
let index = self.index.clone();
|
||||
|
@ -646,4 +651,16 @@ impl MboxType {
|
|||
*/
|
||||
Ok(Box::new(ret))
|
||||
}
|
||||
|
||||
pub fn validate_config(s: &AccountSettings) -> Result<()> {
|
||||
let path = Path::new(s.root_folder.as_str()).expand();
|
||||
if !path.exists() {
|
||||
return Err(MeliError::new(format!(
|
||||
"\"root_folder\" {} for account {} is not a valid path.",
|
||||
s.root_folder.as_str(),
|
||||
s.name()
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,11 +111,11 @@ impl NotmuchDb {
|
|||
let mut database: *mut notmuch_database_t = std::ptr::null_mut();
|
||||
let path = Path::new(s.root_folder.as_str()).expand().to_path_buf();
|
||||
if !path.exists() {
|
||||
panic!(
|
||||
return Err(MeliError::new(format!(
|
||||
"\"root_folder\" {} for account {} is not a valid path.",
|
||||
s.root_folder.as_str(),
|
||||
s.name()
|
||||
);
|
||||
)));
|
||||
}
|
||||
|
||||
let path_c = std::ffi::CString::new(path.to_str().unwrap()).unwrap();
|
||||
|
@ -170,6 +170,26 @@ impl NotmuchDb {
|
|||
save_messages_to: None,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn validate_config( s: &AccountSettings) -> Result<()> {
|
||||
let path = Path::new(s.root_folder.as_str()).expand().to_path_buf();
|
||||
if !path.exists() {
|
||||
return Err(MeliError::new(format!(
|
||||
"\"root_folder\" {} for account {} is not a valid path.",
|
||||
s.root_folder.as_str(),
|
||||
s.name()
|
||||
)));
|
||||
}
|
||||
for (k, f) in s.folders.iter() {
|
||||
if f.extra.get("query").is_none() {
|
||||
return Err(MeliError::new(format!(
|
||||
"notmuch folder configuration entry \"{}\" should have a \"query\" value set.",
|
||||
k
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl MailBackend for NotmuchDb {
|
||||
|
|
|
@ -328,12 +328,40 @@ impl FileSettings {
|
|||
let mut file = File::open(path)?;
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)?;
|
||||
let s: std::result::Result<FileSettings, toml::de::Error> = toml::from_str(&contents);
|
||||
if let Err(e) = s {
|
||||
return Err(MeliError::new(format!(
|
||||
"Config file contains errors: {}",
|
||||
e.to_string()
|
||||
)));
|
||||
let s: FileSettings = toml::from_str(&contents).map_err(|e| {
|
||||
MeliError::new(format!("Config file contains errors: {}", e.to_string()))
|
||||
})?;
|
||||
let backends = melib::backends::Backends::new();
|
||||
for (name, acc) in s.accounts {
|
||||
let FileAccount {
|
||||
root_folder,
|
||||
format,
|
||||
identity,
|
||||
read_only,
|
||||
display_name,
|
||||
subscribed_folders,
|
||||
folders,
|
||||
extra,
|
||||
index_style: _,
|
||||
cache_type: _,
|
||||
} = acc;
|
||||
|
||||
let lowercase_format = format.to_lowercase();
|
||||
let s = AccountSettings {
|
||||
name,
|
||||
root_folder,
|
||||
format: format.clone(),
|
||||
identity,
|
||||
read_only,
|
||||
display_name,
|
||||
subscribed_folders,
|
||||
folders: folders
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, v.folder_conf))
|
||||
.collect(),
|
||||
extra,
|
||||
};
|
||||
backends.validate_config(&lowercase_format, &s)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -10,13 +10,13 @@ pub struct Shortcuts {
|
|||
pub listing: ListingShortcuts,
|
||||
#[serde(default)]
|
||||
pub composing: ComposingShortcuts,
|
||||
#[serde(default)]
|
||||
#[serde(default, alias = "compact-listing")]
|
||||
pub compact_listing: CompactListingShortcuts,
|
||||
#[serde(default)]
|
||||
#[serde(default, alias = "contact-list")]
|
||||
pub contact_list: ContactListShortcuts,
|
||||
#[serde(default)]
|
||||
#[serde(default, alias = "envelope-view")]
|
||||
pub envelope_view: EnvelopeViewShortcuts,
|
||||
#[serde(default)]
|
||||
#[serde(default, alias = "thread-view")]
|
||||
pub thread_view: ThreadViewShortcuts,
|
||||
#[serde(default)]
|
||||
pub pager: PagerShortcuts,
|
||||
|
|
Loading…
Reference in New Issue