mirror of
https://github.com/soywod/himalaya.git
synced 2024-09-05 10:31:13 +00:00
move domain stuff to domain/, improve completion
This commit is contained in:
parent
223537c4ca
commit
0ed1147f3d
|
@ -1,29 +0,0 @@
|
||||||
use anyhow::Result;
|
|
||||||
use clap::{self, App, Arg, ArgMatches, Shell, SubCommand};
|
|
||||||
use log::debug;
|
|
||||||
use std::io;
|
|
||||||
|
|
||||||
pub fn subcmds<'s>() -> Vec<App<'s, 's>> {
|
|
||||||
vec![SubCommand::with_name("completion")
|
|
||||||
.about("Generates the completion script for the given shell")
|
|
||||||
.args(&[Arg::with_name("shell")
|
|
||||||
.possible_values(&["bash", "zsh", "fish"])
|
|
||||||
.required(true)])]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn matches<'a>(app: fn() -> App<'a, 'a>, matches: &ArgMatches) -> Result<bool> {
|
|
||||||
if let Some(matches) = matches.subcommand_matches("completion") {
|
|
||||||
debug!("completion command matched");
|
|
||||||
let shell = match matches.value_of("shell").unwrap() {
|
|
||||||
"fish" => Shell::Fish,
|
|
||||||
"zsh" => Shell::Zsh,
|
|
||||||
"bash" | _ => Shell::Bash,
|
|
||||||
};
|
|
||||||
debug!("shell: {}", shell);
|
|
||||||
app().gen_completions_to("himalaya", shell, &mut io::stdout());
|
|
||||||
return Ok(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
debug!("nothing matched");
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
pub mod cli;
|
|
34
src/compl/arg.rs
Normal file
34
src/compl/arg.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
//! Module related to completion arguments.
|
||||||
|
//!
|
||||||
|
//! This module provides subcommands and an argument matcher for the completion command.
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::{self, App, Arg, ArgMatches, Shell, SubCommand};
|
||||||
|
use log::debug;
|
||||||
|
|
||||||
|
/// Subcommands related to the completion generation.
|
||||||
|
pub fn subcmds<'a>() -> Vec<App<'a, 'a>> {
|
||||||
|
vec![SubCommand::with_name("completion")
|
||||||
|
.about("Generates the completion script for the given shell")
|
||||||
|
.args(&[Arg::with_name("shell")
|
||||||
|
.possible_values(&Shell::variants()[..])
|
||||||
|
.required(true)])]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumeration of all possible completion matches.
|
||||||
|
pub enum Match<'a> {
|
||||||
|
/// Generate completion script for the given shell slice.
|
||||||
|
Generate(&'a str),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Completion arg matcher.
|
||||||
|
pub fn matches<'a>(m: &'a ArgMatches) -> Result<Option<Match<'a>>> {
|
||||||
|
if let Some(m) = m.subcommand_matches("completion") {
|
||||||
|
debug!("completion command matched");
|
||||||
|
let shell = m.value_of("shell").unwrap();
|
||||||
|
debug!("shell: {}", shell);
|
||||||
|
return Ok(Some(Match::Generate(shell)));
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
16
src/compl/handler.rs
Normal file
16
src/compl/handler.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
//! Module related to completion handling.
|
||||||
|
//!
|
||||||
|
//! This module gathers all completion actions triggered by the CLI.
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use clap::{App, Shell};
|
||||||
|
use std::{io, str::FromStr};
|
||||||
|
|
||||||
|
/// Generate completion script from the given [`clap::App`] for the given shell slice.
|
||||||
|
pub fn generate<'a>(shell: &'a str, mut app: App<'a, 'a>) -> Result<()> {
|
||||||
|
let shell = Shell::from_str(shell)
|
||||||
|
.map_err(|err| anyhow!(err))
|
||||||
|
.context("cannot parse shell")?;
|
||||||
|
app.gen_completions_to("himalaya", shell, &mut io::stdout());
|
||||||
|
Ok(())
|
||||||
|
}
|
9
src/compl/mod.rs
Normal file
9
src/compl/mod.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
//! Module related to shell completion.
|
||||||
|
//!
|
||||||
|
//! This module allows users to generate autocompletion scripts for their shells. You can see the
|
||||||
|
//! list of available shells directly on the [clap's docs.rs website].
|
||||||
|
//!
|
||||||
|
//! [clap's docs.rs website]: https://docs.rs/clap/2.33.3/clap/enum.Shell.html
|
||||||
|
|
||||||
|
pub mod arg;
|
||||||
|
pub mod handler;
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
||||||
use clap;
|
use clap;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
||||||
use crate::domain::{config::entity::Config, imap::ImapServiceInterface};
|
use crate::domain::{config::entity::Config, imap::service::ImapServiceInterface};
|
||||||
|
|
||||||
pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
|
||||||
vec![
|
vec![
|
4
src/domain/imap/mod.rs
Normal file
4
src/domain/imap/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
//! Modules related to IMAP.
|
||||||
|
|
||||||
|
pub mod cli;
|
||||||
|
pub mod service;
|
|
@ -4,9 +4,10 @@ use log::{debug, trace};
|
||||||
use native_tls::{self, TlsConnector, TlsStream};
|
use native_tls::{self, TlsConnector, TlsStream};
|
||||||
use std::{collections::HashSet, convert::TryFrom, iter::FromIterator, net::TcpStream};
|
use std::{collections::HashSet, convert::TryFrom, iter::FromIterator, net::TcpStream};
|
||||||
|
|
||||||
use crate::{domain::account::entity::Account, flag::model::Flags, msg::model::Msg};
|
use crate::{
|
||||||
|
domain::{account::entity::Account, config::entity::Config, msg::entity::Msg},
|
||||||
use super::config::entity::Config;
|
flag::model::Flags,
|
||||||
|
};
|
||||||
|
|
||||||
type ImapSession = imap::Session<TlsStream<TcpStream>>;
|
type ImapSession = imap::Session<TlsStream<TcpStream>>;
|
||||||
type ImapMsgs = imap::types::ZeroCopy<Vec<imap::types::Fetch>>;
|
type ImapMsgs = imap::types::ZeroCopy<Vec<imap::types::Fetch>>;
|
|
@ -3,8 +3,7 @@ use clap;
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
domain::imap::ImapServiceInterface,
|
domain::{imap::service::ImapServiceInterface, mbox::entity::Mboxes},
|
||||||
mbox::model::Mboxes,
|
|
||||||
output::service::{OutputService, OutputServiceInterface},
|
output::service::{OutputService, OutputServiceInterface},
|
||||||
};
|
};
|
||||||
|
|
2
src/domain/mbox/mod.rs
Normal file
2
src/domain/mbox/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod cli;
|
||||||
|
pub mod entity;
|
|
@ -3,4 +3,6 @@
|
||||||
pub mod account;
|
pub mod account;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod imap;
|
pub mod imap;
|
||||||
|
pub mod mbox;
|
||||||
|
pub mod msg;
|
||||||
pub mod smtp;
|
pub mod smtp;
|
||||||
|
|
|
@ -15,14 +15,16 @@ use url::Url;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
body::Body,
|
body::Body,
|
||||||
|
entity::{Msg, MsgSerialized, Msgs},
|
||||||
headers::Headers,
|
headers::Headers,
|
||||||
model::{Msg, MsgSerialized, Msgs},
|
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
domain::{account::entity::Account, imap::ImapServiceInterface, smtp::*},
|
domain::{
|
||||||
|
account::entity::Account, imap::service::ImapServiceInterface, mbox::cli::mbox_target_arg,
|
||||||
|
smtp::service::SmtpServiceInterface,
|
||||||
|
},
|
||||||
flag::model::Flags,
|
flag::model::Flags,
|
||||||
input,
|
input,
|
||||||
mbox::cli::mbox_target_arg,
|
|
||||||
output::service::{OutputService, OutputServiceInterface},
|
output::service::{OutputService, OutputServiceInterface},
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,7 +22,7 @@ pub mod cli;
|
||||||
|
|
||||||
/// Here are the two **main structs** of this module: `Msg` and `Msgs` which
|
/// Here are the two **main structs** of this module: `Msg` and `Msgs` which
|
||||||
/// represent a *Mail* or *multiple Mails* in this crate.
|
/// represent a *Mail* or *multiple Mails* in this crate.
|
||||||
pub mod model;
|
pub mod entity;
|
||||||
|
|
||||||
/// This module is used in the `Msg` struct, which should represent an
|
/// This module is used in the `Msg` struct, which should represent an
|
||||||
/// attachment of a msg.
|
/// attachment of a msg.
|
3
src/domain/smtp/mod.rs
Normal file
3
src/domain/smtp/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
//! Modules related to SMTP.
|
||||||
|
|
||||||
|
pub mod service;
|
|
@ -2,7 +2,10 @@ use anyhow::Result;
|
||||||
use clap;
|
use clap;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
||||||
use crate::{domain::imap::ImapServiceInterface, flag::model::Flags, msg::cli::uid_arg};
|
use crate::{
|
||||||
|
domain::{imap::service::ImapServiceInterface, msg::cli::uid_arg},
|
||||||
|
flag::model::Flags,
|
||||||
|
};
|
||||||
|
|
||||||
fn flags_arg<'a>() -> clap::Arg<'a, 'a> {
|
fn flags_arg<'a>() -> clap::Arg<'a, 'a> {
|
||||||
clap::Arg::with_name("flags")
|
clap::Arg::with_name("flags")
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
pub mod cli;
|
|
13
src/lib.rs
13
src/lib.rs
|
@ -12,9 +12,7 @@
|
||||||
//!
|
//!
|
||||||
//! [here]: https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html
|
//! [here]: https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html
|
||||||
|
|
||||||
/// `comp` stands for `completion`. This module makes it possible to create autocompletion-settings
|
pub mod compl;
|
||||||
/// for himalaya for your shell :)
|
|
||||||
pub mod comp;
|
|
||||||
|
|
||||||
/// Everything which is related to the config files. For example the structure of your config file.
|
/// Everything which is related to the config files. For example the structure of your config file.
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
@ -23,19 +21,10 @@ pub mod config;
|
||||||
/// read-flag.
|
/// read-flag.
|
||||||
pub mod flag;
|
pub mod flag;
|
||||||
|
|
||||||
/// A wrapper for creating connections easier to the IMAP-Servers.
|
|
||||||
pub mod imap;
|
|
||||||
|
|
||||||
/// Handles the input-interaction with the user. For example if you want to edit the body of your
|
/// Handles the input-interaction with the user. For example if you want to edit the body of your
|
||||||
/// message, his module takes care of the draft and calls your ~(neo)vim~ your favourite editor.
|
/// message, his module takes care of the draft and calls your ~(neo)vim~ your favourite editor.
|
||||||
pub mod input;
|
pub mod input;
|
||||||
|
|
||||||
/// Everything which is related to mboxes, for example creating or deleting some.
|
|
||||||
pub mod mbox;
|
|
||||||
|
|
||||||
/// Includes everything related to a message. This means: Body, Headers, Attachments, etc.
|
|
||||||
pub mod msg;
|
|
||||||
|
|
||||||
/// Handles the output. For example the JSON and HTML output.
|
/// Handles the output. For example the JSON and HTML output.
|
||||||
pub mod output;
|
pub mod output;
|
||||||
|
|
||||||
|
|
42
src/main.rs
42
src/main.rs
|
@ -5,16 +5,20 @@ use log::{debug, trace};
|
||||||
use std::{convert::TryFrom, env, path::PathBuf};
|
use std::{convert::TryFrom, env, path::PathBuf};
|
||||||
|
|
||||||
use himalaya::{
|
use himalaya::{
|
||||||
comp,
|
compl,
|
||||||
config::cli::config_args,
|
config::cli::config_args,
|
||||||
domain::{
|
domain::{
|
||||||
account::entity::Account, config::entity::Config, imap::ImapService, smtp::SmtpService,
|
account::entity::Account,
|
||||||
|
config::entity::Config,
|
||||||
|
imap::{self, service::ImapService},
|
||||||
|
mbox, msg,
|
||||||
|
smtp::service::SmtpService,
|
||||||
},
|
},
|
||||||
flag, imap, mbox, msg,
|
flag,
|
||||||
output::{cli::output_args, service::OutputService},
|
output::{cli::output_args, service::OutputService},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn parse_args<'a>() -> clap::App<'a, 'a> {
|
fn create_app<'a>() -> clap::App<'a, 'a> {
|
||||||
clap::App::new(env!("CARGO_PKG_NAME"))
|
clap::App::new(env!("CARGO_PKG_NAME"))
|
||||||
.version(env!("CARGO_PKG_VERSION"))
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
.about(env!("CARGO_PKG_DESCRIPTION"))
|
.about(env!("CARGO_PKG_DESCRIPTION"))
|
||||||
|
@ -27,7 +31,7 @@ fn parse_args<'a>() -> clap::App<'a, 'a> {
|
||||||
.subcommands(imap::cli::subcmds())
|
.subcommands(imap::cli::subcmds())
|
||||||
.subcommands(mbox::cli::subcmds())
|
.subcommands(mbox::cli::subcmds())
|
||||||
.subcommands(msg::cli::subcmds())
|
.subcommands(msg::cli::subcmds())
|
||||||
.subcommands(comp::cli::subcmds())
|
.subcommands(compl::arg::subcmds())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
|
@ -50,23 +54,23 @@ fn main() -> Result<()> {
|
||||||
// return Ok(msg_matches_mailto(&app, &url, smtp)?);
|
// return Ok(msg_matches_mailto(&app, &url, smtp)?);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
let args = parse_args();
|
let app = create_app();
|
||||||
let arg_matches = args.get_matches();
|
let m = app.get_matches();
|
||||||
|
|
||||||
// Check completion before init config
|
// Check shell completion BEFORE any entity or service initialization.
|
||||||
if comp::cli::matches(parse_args, &arg_matches)? {
|
if let Some(compl::arg::Match::Generate(shell)) = compl::arg::matches(&m)? {
|
||||||
|
let app = create_app();
|
||||||
|
compl::handler::generate(shell, app)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("init output service");
|
let output = OutputService::new(m.value_of("output").unwrap())?;
|
||||||
let output = OutputService::new(arg_matches.value_of("output").unwrap());
|
|
||||||
debug!("output service: {:?}", output);
|
|
||||||
|
|
||||||
debug!("init mbox");
|
debug!("init mbox");
|
||||||
let mbox = arg_matches.value_of("mailbox").unwrap();
|
let mbox = m.value_of("mailbox").unwrap();
|
||||||
debug!("mbox: {}", mbox);
|
debug!("mbox: {}", mbox);
|
||||||
|
|
||||||
let config_path: PathBuf = arg_matches
|
let config_path: PathBuf = m
|
||||||
.value_of("config")
|
.value_of("config")
|
||||||
.map(|s| s.into())
|
.map(|s| s.into())
|
||||||
.unwrap_or(Config::path()?);
|
.unwrap_or(Config::path()?);
|
||||||
|
@ -74,7 +78,7 @@ fn main() -> Result<()> {
|
||||||
let config = Config::try_from(config_path.clone())?;
|
let config = Config::try_from(config_path.clone())?;
|
||||||
trace!("{:#?}", config);
|
trace!("{:#?}", config);
|
||||||
|
|
||||||
let account_name = arg_matches.value_of("account");
|
let account_name = m.value_of("account");
|
||||||
debug!("init account `{}`", account_name.unwrap_or("default"));
|
debug!("init account `{}`", account_name.unwrap_or("default"));
|
||||||
let account = Account::try_from((&config, account_name))?;
|
let account = Account::try_from((&config, account_name))?;
|
||||||
trace!("{:#?}", account);
|
trace!("{:#?}", account);
|
||||||
|
@ -86,10 +90,10 @@ fn main() -> Result<()> {
|
||||||
let mut smtp = SmtpService::new(&account)?;
|
let mut smtp = SmtpService::new(&account)?;
|
||||||
|
|
||||||
debug!("begin matching");
|
debug!("begin matching");
|
||||||
let _matched = mbox::cli::matches(&arg_matches, &output, &mut imap)?
|
let _matched = mbox::cli::matches(&m, &output, &mut imap)?
|
||||||
|| flag::cli::matches(&arg_matches, &mut imap)?
|
|| flag::cli::matches(&m, &mut imap)?
|
||||||
|| imap::cli::matches(&arg_matches, &config, &mut imap)?
|
|| imap::cli::matches(&m, &config, &mut imap)?
|
||||||
|| msg::cli::matches(&arg_matches, mbox, &account, &output, &mut imap, &mut smtp)?;
|
|| msg::cli::matches(&m, mbox, &account, &output, &mut imap, &mut smtp)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
pub mod cli;
|
|
||||||
pub mod model;
|
|
|
@ -1,6 +1,7 @@
|
||||||
use anyhow::Result;
|
use anyhow::{anyhow, Error, Result};
|
||||||
|
use log::debug;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::fmt;
|
use std::{fmt, str::FromStr};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum OutputFmt {
|
pub enum OutputFmt {
|
||||||
|
@ -8,11 +9,13 @@ pub enum OutputFmt {
|
||||||
Json,
|
Json,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for OutputFmt {
|
impl FromStr for OutputFmt {
|
||||||
fn from(slice: &str) -> Self {
|
type Err = Error;
|
||||||
|
fn from_str(slice: &str) -> Result<Self, Self::Err> {
|
||||||
match slice {
|
match slice {
|
||||||
"json" => Self::Json,
|
"JSON" | _ if slice.eq_ignore_ascii_case("json") => Ok(Self::Json),
|
||||||
"plain" | _ => Self::Plain,
|
"PLAIN" | _ if slice.eq_ignore_ascii_case("plain") => Ok(Self::Plain),
|
||||||
|
_ => Err(anyhow!("cannot parse output `{}`", slice)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +24,7 @@ impl fmt::Display for OutputFmt {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let slice = match self {
|
let slice = match self {
|
||||||
&OutputFmt::Json => "JSON",
|
&OutputFmt::Json => "JSON",
|
||||||
&OutputFmt::Plain => "Plain",
|
&OutputFmt::Plain => "PLAIN",
|
||||||
};
|
};
|
||||||
write!(f, "{}", slice)
|
write!(f, "{}", slice)
|
||||||
}
|
}
|
||||||
|
@ -51,8 +54,11 @@ pub struct OutputService {
|
||||||
|
|
||||||
impl OutputService {
|
impl OutputService {
|
||||||
/// Create a new output-handler by setting the given formatting style.
|
/// Create a new output-handler by setting the given formatting style.
|
||||||
pub fn new(fmt: &str) -> Self {
|
pub fn new(slice: &str) -> Result<Self> {
|
||||||
Self { fmt: fmt.into() }
|
debug!("init output service");
|
||||||
|
debug!("output: {}", slice);
|
||||||
|
let fmt = OutputFmt::from_str(slice)?;
|
||||||
|
Ok(Self { fmt })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true, if the formatting should be plaintext.
|
/// Returns true, if the formatting should be plaintext.
|
||||||
|
|
Loading…
Reference in a new issue