mirror of
https://github.com/soywod/himalaya.git
synced 2024-07-05 17:15:12 +00:00
split input into submodules
This commit is contained in:
parent
8fbbb324da
commit
94e9711c58
|
@ -90,7 +90,7 @@ impl Config {
|
|||
Some(name) => self
|
||||
.accounts
|
||||
.get(name)
|
||||
.ok_or_else(|| anyhow!(format!("cannot find account `{}`", name))),
|
||||
.ok_or_else(|| anyhow!("cannot find account `{}`", name)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -513,7 +513,7 @@ impl<'a> TryFrom<(&'a Config, Option<&str>)> for Account {
|
|||
.accounts
|
||||
.get(name)
|
||||
.map(|account| (name.to_owned(), account))
|
||||
.ok_or_else(|| anyhow!(format!("cannot find account `{}`", name))),
|
||||
.ok_or_else(|| anyhow!("cannot find account `{}`", name)),
|
||||
}?;
|
||||
|
||||
let downloads_dir = account
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! Modules related to the user's configuration.
|
||||
//! Module related to the user's configuration.
|
||||
|
||||
pub mod arg;
|
||||
pub mod entity;
|
||||
|
|
|
@ -339,7 +339,7 @@ impl<'a> ImapServiceInterface for ImapService<'a> {
|
|||
for fetch in fetches.iter() {
|
||||
let msg = Msg::try_from(fetch)?;
|
||||
let uid = fetch.uid.ok_or_else(|| {
|
||||
anyhow!(format!("cannot retrieve message {}'s UID", fetch.message))
|
||||
anyhow!("cannot retrieve message {}'s UID", fetch.message)
|
||||
})?;
|
||||
|
||||
let subject = msg.headers.subject.clone().unwrap_or_default();
|
||||
|
@ -386,10 +386,10 @@ impl<'a> ImapServiceInterface for ImapService<'a> {
|
|||
}
|
||||
|
||||
fn logout(&mut self) -> Result<()> {
|
||||
debug!("logout from IMAP server");
|
||||
self.sess()?
|
||||
.logout()
|
||||
.context("cannot logout from IMAP server")?;
|
||||
if let Some(ref mut sess) = self.sess {
|
||||
debug!("logout from IMAP server");
|
||||
sess.logout().context("cannot logout from IMAP server")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::{
|
|||
};
|
||||
|
||||
#[cfg(not(test))]
|
||||
use crate::input;
|
||||
use crate::ui::editor;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
|
@ -435,7 +435,7 @@ impl Msg {
|
|||
// tests, because we just need to look, if the headers are set
|
||||
// correctly
|
||||
#[cfg(not(test))]
|
||||
let msg = input::open_editor_with_tpl(msg.as_bytes())?;
|
||||
let msg = editor::open_editor_with_tpl(msg.as_bytes())?;
|
||||
|
||||
// refresh the state of the msg
|
||||
self.parse_from_str(&msg)?;
|
||||
|
@ -481,7 +481,7 @@ impl Msg {
|
|||
/// ```
|
||||
pub fn parse_from_str(&mut self, content: &str) -> Result<()> {
|
||||
let parsed = mailparse::parse_mail(content.as_bytes())
|
||||
.with_context(|| format!("How the message looks like currently:\n{}", self))?;
|
||||
.context(format!("How the message looks like currently:\n{}", self))?;
|
||||
|
||||
self.headers = Headers::from(&parsed);
|
||||
|
||||
|
@ -577,10 +577,7 @@ impl Msg {
|
|||
// add "from"
|
||||
for mailaddress in &self.headers.from {
|
||||
msg = msg.from(
|
||||
match mailaddress
|
||||
.parse()
|
||||
.with_context(|| "cannot parse `From` header")
|
||||
{
|
||||
match mailaddress.parse().context("cannot parse `From` header") {
|
||||
Ok(from) => from,
|
||||
Err(err) => return Err(anyhow!(err.to_string())),
|
||||
},
|
||||
|
@ -590,10 +587,7 @@ impl Msg {
|
|||
// add "to"
|
||||
for mailaddress in &self.headers.to {
|
||||
msg = msg.to(
|
||||
match mailaddress
|
||||
.parse()
|
||||
.with_context(|| "cannot parse `To` header")
|
||||
{
|
||||
match mailaddress.parse().context("cannot parse `To` header") {
|
||||
Ok(from) => from,
|
||||
Err(err) => return Err(anyhow!(err.to_string())),
|
||||
},
|
||||
|
@ -605,10 +599,7 @@ impl Msg {
|
|||
if let Some(bcc) = &self.headers.bcc {
|
||||
for mailaddress in bcc {
|
||||
msg = msg.bcc(
|
||||
match mailaddress
|
||||
.parse()
|
||||
.with_context(|| "cannot parse `Bcc` header")
|
||||
{
|
||||
match mailaddress.parse().context("cannot parse `Bcc` header") {
|
||||
Ok(from) => from,
|
||||
Err(err) => return Err(anyhow!(err.to_string())),
|
||||
},
|
||||
|
@ -620,10 +611,7 @@ impl Msg {
|
|||
if let Some(cc) = &self.headers.cc {
|
||||
for mailaddress in cc {
|
||||
msg = msg.cc(
|
||||
match mailaddress
|
||||
.parse()
|
||||
.with_context(|| "cannot parse `Cc` header")
|
||||
{
|
||||
match mailaddress.parse().context("cannot parse `Cc` header") {
|
||||
Ok(from) => from,
|
||||
Err(err) => return Err(anyhow!(err.to_string())),
|
||||
},
|
||||
|
@ -636,7 +624,7 @@ impl Msg {
|
|||
msg = msg.in_reply_to(
|
||||
match in_reply_to
|
||||
.parse()
|
||||
.with_context(|| "cannot parse `In-Reply-To` header")
|
||||
.context("cannot parse `In-Reply-To` header")
|
||||
{
|
||||
Ok(from) => from,
|
||||
Err(err) => return Err(anyhow!(err.to_string())),
|
||||
|
@ -665,7 +653,7 @@ impl Msg {
|
|||
msg = msg.reply_to(
|
||||
match mailaddress
|
||||
.parse()
|
||||
.with_context(|| "cannot parse `Reply-To` header")
|
||||
.context("cannot parse `Reply-To` header")
|
||||
{
|
||||
Ok(from) => from,
|
||||
Err(err) => return Err(anyhow!(err.to_string())),
|
||||
|
@ -677,10 +665,7 @@ impl Msg {
|
|||
// add "sender"
|
||||
if let Some(sender) = &self.headers.sender {
|
||||
msg = msg.sender(
|
||||
match sender
|
||||
.parse()
|
||||
.with_context(|| "cannot parse `Sender` header")
|
||||
{
|
||||
match sender.parse().context("cannot parse `Sender` header") {
|
||||
Ok(from) => from,
|
||||
Err(err) => return Err(anyhow!(err.to_string())),
|
||||
},
|
||||
|
|
|
@ -17,13 +17,14 @@ use crate::{
|
|||
imap::service::ImapServiceInterface,
|
||||
mbox::entity::Mbox,
|
||||
msg::{
|
||||
self,
|
||||
body::Body,
|
||||
entity::{Msg, Msgs},
|
||||
},
|
||||
smtp::service::SmtpServiceInterface,
|
||||
},
|
||||
input,
|
||||
output::service::{OutputService, OutputServiceInterface},
|
||||
ui::choice::{self, PostEditChoice},
|
||||
};
|
||||
|
||||
use super::{entity::MsgSerialized, flag::entity::Flags, headers::Headers};
|
||||
|
@ -39,9 +40,9 @@ fn msg_interaction<ImapService: ImapServiceInterface, SmtpService: SmtpServiceIn
|
|||
msg.edit_body()?;
|
||||
|
||||
loop {
|
||||
match input::post_edit_choice() {
|
||||
match choice::post_edit() {
|
||||
Ok(choice) => match choice {
|
||||
input::PostEditChoice::Send => {
|
||||
PostEditChoice::Send => {
|
||||
debug!("sending message…");
|
||||
|
||||
// prepare the msg to be send
|
||||
|
@ -68,12 +69,12 @@ fn msg_interaction<ImapService: ImapServiceInterface, SmtpService: SmtpServiceIn
|
|||
imap.append_msg(&mbox, msg)?;
|
||||
|
||||
// remove the draft, since we sent it
|
||||
input::remove_draft()?;
|
||||
msg::utils::remove_draft()?;
|
||||
output.print("Message successfully sent")?;
|
||||
break;
|
||||
}
|
||||
// edit the body of the msg
|
||||
input::PostEditChoice::Edit => {
|
||||
PostEditChoice::Edit => {
|
||||
// Did something goes wrong when the user changed the
|
||||
// content?
|
||||
if let Err(err) = msg.edit_body() {
|
||||
|
@ -84,8 +85,8 @@ fn msg_interaction<ImapService: ImapServiceInterface, SmtpService: SmtpServiceIn
|
|||
));
|
||||
}
|
||||
}
|
||||
input::PostEditChoice::LocalDraft => break,
|
||||
input::PostEditChoice::RemoteDraft => {
|
||||
PostEditChoice::LocalDraft => break,
|
||||
PostEditChoice::RemoteDraft => {
|
||||
debug!("saving to draft…");
|
||||
|
||||
msg.flags.insert(Flag::Seen);
|
||||
|
@ -93,7 +94,7 @@ fn msg_interaction<ImapService: ImapServiceInterface, SmtpService: SmtpServiceIn
|
|||
let mbox = Mbox::from("Drafts");
|
||||
match imap.append_msg(&mbox, msg) {
|
||||
Ok(_) => {
|
||||
input::remove_draft()?;
|
||||
msg::utils::remove_draft()?;
|
||||
output.print("Message successfully saved to Drafts")?;
|
||||
}
|
||||
Err(err) => {
|
||||
|
@ -103,8 +104,8 @@ fn msg_interaction<ImapService: ImapServiceInterface, SmtpService: SmtpServiceIn
|
|||
};
|
||||
break;
|
||||
}
|
||||
input::PostEditChoice::Discard => {
|
||||
input::remove_draft()?;
|
||||
PostEditChoice::Discard => {
|
||||
msg::utils::remove_draft()?;
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -38,3 +38,4 @@ pub mod body;
|
|||
pub mod flag;
|
||||
pub mod handler;
|
||||
pub mod tpl;
|
||||
pub mod utils;
|
||||
|
|
15
src/domain/msg/utils.rs
Normal file
15
src/domain/msg/utils.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
use anyhow::{Context, Result};
|
||||
use log::debug;
|
||||
use std::{env, fs, path::PathBuf};
|
||||
|
||||
pub fn draft_path() -> PathBuf {
|
||||
let path = env::temp_dir().join("himalaya-draft.mail");
|
||||
debug!("draft path: `{:?}`", path);
|
||||
path
|
||||
}
|
||||
|
||||
pub fn remove_draft() -> Result<()> {
|
||||
let path = draft_path();
|
||||
debug!("remove draft path: `{:?}`", path);
|
||||
fs::remove_file(&path).context(format!("cannot delete draft file at `{:?}`", path))
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
//! Modules related to SMTP.
|
||||
//! Module related to SMTP.
|
||||
|
||||
pub mod service;
|
||||
|
|
162
src/input.rs
162
src/input.rs
|
@ -1,162 +0,0 @@
|
|||
use anyhow::{anyhow, Context, Result};
|
||||
use log::{debug, error, trace};
|
||||
use std::{
|
||||
env,
|
||||
fs::{self, File},
|
||||
io::{self, Read, Write},
|
||||
path::PathBuf,
|
||||
process::Command,
|
||||
};
|
||||
|
||||
fn draft_path() -> PathBuf {
|
||||
env::temp_dir().join("himalaya-draft.mail")
|
||||
}
|
||||
|
||||
pub fn remove_draft() -> Result<()> {
|
||||
debug!("[input] remove draft");
|
||||
|
||||
let draft_path = draft_path();
|
||||
debug!("[input] draft path: {:?}", draft_path);
|
||||
|
||||
fs::remove_file(&draft_path)
|
||||
.with_context(|| format!("cannot delete draft file {:?}", draft_path))
|
||||
}
|
||||
|
||||
pub fn open_editor_with_tpl(tpl: &[u8]) -> Result<String> {
|
||||
debug!("[input] open editor with tpl");
|
||||
trace!("{}", String::from_utf8(tpl.to_vec())?);
|
||||
|
||||
let draft_path = draft_path();
|
||||
debug!("[input] draft path: {:?}", draft_path);
|
||||
|
||||
if draft_path.exists() {
|
||||
debug!("[input] draft found");
|
||||
loop {
|
||||
match pre_edit_choice() {
|
||||
Ok(choice) => match choice {
|
||||
PreEditChoice::Edit => return open_editor_with_draft(),
|
||||
PreEditChoice::Discard => break,
|
||||
PreEditChoice::Quit => return Err(anyhow!("Edition aborted")),
|
||||
},
|
||||
Err(err) => error!("{}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("[Input] create draft");
|
||||
File::create(&draft_path)
|
||||
.with_context(|| format!("cannot create draft file {:?}", draft_path))?
|
||||
.write(tpl)
|
||||
.with_context(|| format!("cannot write draft file {:?}", draft_path))?;
|
||||
|
||||
debug!("[Input] open editor");
|
||||
Command::new(env::var("EDITOR").with_context(|| "cannot find `EDITOR` env var")?)
|
||||
.arg(&draft_path)
|
||||
.status()
|
||||
.with_context(|| "cannot launch editor")?;
|
||||
|
||||
debug!("[Input] read draft");
|
||||
let mut draft = String::new();
|
||||
File::open(&draft_path)
|
||||
.with_context(|| format!("cannot open draft file {:?}", draft_path))?
|
||||
.read_to_string(&mut draft)
|
||||
.with_context(|| format!("cannot read draft file {:?}", draft_path))?;
|
||||
|
||||
Ok(draft)
|
||||
}
|
||||
|
||||
pub fn open_editor_with_draft() -> Result<String> {
|
||||
debug!("[input] open editor with draft");
|
||||
|
||||
let draft_path = draft_path();
|
||||
debug!("[input] draft path: {:?}", draft_path);
|
||||
|
||||
// Opens editor and saves user input to draft file
|
||||
Command::new(env::var("EDITOR").with_context(|| "cannot find `EDITOR` env var")?)
|
||||
.arg(&draft_path)
|
||||
.status()
|
||||
.with_context(|| "cannot launch editor")?;
|
||||
|
||||
// Extracts draft file content
|
||||
let mut draft = String::new();
|
||||
File::open(&draft_path)
|
||||
.with_context(|| format!("cannot open file {:?}", draft_path))?
|
||||
.read_to_string(&mut draft)
|
||||
.with_context(|| format!("cannot read file {:?}", draft_path))?;
|
||||
|
||||
Ok(draft)
|
||||
}
|
||||
|
||||
pub enum PreEditChoice {
|
||||
Edit,
|
||||
Discard,
|
||||
Quit,
|
||||
}
|
||||
|
||||
pub fn pre_edit_choice() -> Result<PreEditChoice> {
|
||||
debug!("[input] pre edit choice");
|
||||
|
||||
println!("A draft was found:");
|
||||
print!("(e)dit, (d)iscard or (q)uit? ");
|
||||
io::stdout()
|
||||
.flush()
|
||||
.with_context(|| "cannot flush stdout")?;
|
||||
|
||||
let mut buf = String::new();
|
||||
io::stdin()
|
||||
.read_line(&mut buf)
|
||||
.with_context(|| "cannot read stdin")?;
|
||||
|
||||
match buf.bytes().next().map(|bytes| bytes as char) {
|
||||
Some('e') => {
|
||||
debug!("[input] pre edit choice: edit matched");
|
||||
Ok(PreEditChoice::Edit)
|
||||
}
|
||||
Some('d') => {
|
||||
debug!("[input] pre edit choice: discard matched");
|
||||
Ok(PreEditChoice::Discard)
|
||||
}
|
||||
Some('q') => {
|
||||
debug!("[input] pre edit choice: quit matched");
|
||||
Ok(PreEditChoice::Quit)
|
||||
}
|
||||
Some(choice) => {
|
||||
debug!("[input] pre edit choice: invalid choice {}", choice);
|
||||
Err(anyhow!(format!("Invalid choice `{}`", choice)))
|
||||
}
|
||||
None => {
|
||||
debug!("[input] pre edit choice: empty choice");
|
||||
Err(anyhow!("Empty choice"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum PostEditChoice {
|
||||
Send,
|
||||
Edit,
|
||||
LocalDraft,
|
||||
RemoteDraft,
|
||||
Discard,
|
||||
}
|
||||
|
||||
pub fn post_edit_choice() -> Result<PostEditChoice> {
|
||||
print!("(s)end, (e)dit, (l)ocal/(r)emote draft or (d)iscard? ");
|
||||
io::stdout()
|
||||
.flush()
|
||||
.with_context(|| "cannot flush stdout")?;
|
||||
|
||||
let mut buf = String::new();
|
||||
io::stdin()
|
||||
.read_line(&mut buf)
|
||||
.with_context(|| "cannot read stdin")?;
|
||||
|
||||
match buf.bytes().next().map(|bytes| bytes as char) {
|
||||
Some('s') => Ok(PostEditChoice::Send),
|
||||
Some('l') => Ok(PostEditChoice::LocalDraft),
|
||||
Some('r') => Ok(PostEditChoice::RemoteDraft),
|
||||
Some('e') => Ok(PostEditChoice::Edit),
|
||||
Some('d') => Ok(PostEditChoice::Discard),
|
||||
Some(choice) => Err(anyhow!(format!("Invalid choice `{}`", choice))),
|
||||
None => Err(anyhow!("Empty choice")),
|
||||
}
|
||||
}
|
|
@ -17,10 +17,6 @@ pub mod compl;
|
|||
/// Everything which is related to the config files. For example the structure of your config file.
|
||||
pub mod config;
|
||||
|
||||
/// 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.
|
||||
pub mod input;
|
||||
|
||||
/// Handles the output. For example the JSON and HTML output.
|
||||
pub mod output;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! Modules related to output formatting and printing.
|
||||
//! Module related to output formatting and printing.
|
||||
|
||||
pub mod cli;
|
||||
pub mod service;
|
||||
|
|
92
src/ui/choice.rs
Normal file
92
src/ui/choice.rs
Normal file
|
@ -0,0 +1,92 @@
|
|||
use anyhow::{anyhow, Context, Result};
|
||||
use log::debug;
|
||||
use std::io::{self, Write};
|
||||
|
||||
pub enum PreEditChoice {
|
||||
Edit,
|
||||
Discard,
|
||||
Quit,
|
||||
}
|
||||
|
||||
pub fn pre_edit() -> Result<PreEditChoice> {
|
||||
println!("A draft was found:");
|
||||
print!("(e)dit, (d)iscard or (q)uit? ");
|
||||
io::stdout().flush().context("cannot flush stdout")?;
|
||||
|
||||
let mut buf = String::new();
|
||||
io::stdin()
|
||||
.read_line(&mut buf)
|
||||
.context("cannot read stdin")?;
|
||||
|
||||
match buf.bytes().next().map(|bytes| bytes as char) {
|
||||
Some('e') => {
|
||||
debug!("edit choice matched");
|
||||
Ok(PreEditChoice::Edit)
|
||||
}
|
||||
Some('d') => {
|
||||
debug!("discard choice matched");
|
||||
Ok(PreEditChoice::Discard)
|
||||
}
|
||||
Some('q') => {
|
||||
debug!("quit choice matched");
|
||||
Ok(PreEditChoice::Quit)
|
||||
}
|
||||
Some(choice) => {
|
||||
debug!("invalid choice `{}`", choice);
|
||||
Err(anyhow!("invalid choice `{}`", choice))
|
||||
}
|
||||
None => {
|
||||
debug!("empty choice");
|
||||
Err(anyhow!("empty choice"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum PostEditChoice {
|
||||
Send,
|
||||
Edit,
|
||||
LocalDraft,
|
||||
RemoteDraft,
|
||||
Discard,
|
||||
}
|
||||
|
||||
pub fn post_edit() -> Result<PostEditChoice> {
|
||||
print!("(s)end, (e)dit, (l)ocal/(r)emote draft or (d)iscard? ");
|
||||
io::stdout().flush().context("cannot flush stdout")?;
|
||||
|
||||
let mut buf = String::new();
|
||||
io::stdin()
|
||||
.read_line(&mut buf)
|
||||
.context("cannot read stdin")?;
|
||||
|
||||
match buf.bytes().next().map(|bytes| bytes as char) {
|
||||
Some('s') => {
|
||||
debug!("send choice matched");
|
||||
Ok(PostEditChoice::Send)
|
||||
}
|
||||
Some('l') => {
|
||||
debug!("save local draft choice matched");
|
||||
Ok(PostEditChoice::LocalDraft)
|
||||
}
|
||||
Some('r') => {
|
||||
debug!("save remote draft matched");
|
||||
Ok(PostEditChoice::RemoteDraft)
|
||||
}
|
||||
Some('e') => {
|
||||
debug!("edit choice matched");
|
||||
Ok(PostEditChoice::Edit)
|
||||
}
|
||||
Some('d') => {
|
||||
debug!("discard choice matched");
|
||||
Ok(PostEditChoice::Discard)
|
||||
}
|
||||
Some(choice) => {
|
||||
debug!("invalid choice `{}`", choice);
|
||||
Err(anyhow!("invalid choice `{}`", choice))
|
||||
}
|
||||
None => {
|
||||
debug!("empty choice");
|
||||
Err(anyhow!("empty choice"))
|
||||
}
|
||||
}
|
||||
}
|
70
src/ui/editor.rs
Normal file
70
src/ui/editor.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
use anyhow::{anyhow, Context, Result};
|
||||
use log::{debug, error};
|
||||
use std::{
|
||||
env,
|
||||
fs::File,
|
||||
io::{Read, Write},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
domain::msg,
|
||||
ui::choice::{self, PreEditChoice},
|
||||
};
|
||||
|
||||
pub fn open_editor_with_tpl(tpl: &[u8]) -> Result<String> {
|
||||
let path = msg::utils::draft_path();
|
||||
if path.exists() {
|
||||
debug!("draft found");
|
||||
loop {
|
||||
match choice::pre_edit() {
|
||||
Ok(choice) => match choice {
|
||||
PreEditChoice::Edit => return open_editor_with_draft(),
|
||||
PreEditChoice::Discard => break,
|
||||
PreEditChoice::Quit => return Err(anyhow!("edition aborted")),
|
||||
},
|
||||
Err(err) => error!("{}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("create draft");
|
||||
File::create(&path)
|
||||
.context(format!("cannot create draft file `{:?}`", path))?
|
||||
.write(tpl)
|
||||
.context(format!("cannot write draft file `{:?}`", path))?;
|
||||
|
||||
debug!("open editor");
|
||||
Command::new(env::var("EDITOR").context("cannot find `$EDITOR` env var")?)
|
||||
.arg(&path)
|
||||
.status()
|
||||
.context("cannot launch editor")?;
|
||||
|
||||
debug!("read draft");
|
||||
let mut draft = String::new();
|
||||
File::open(&path)
|
||||
.context(format!("cannot open draft file `{:?}`", path))?
|
||||
.read_to_string(&mut draft)
|
||||
.context(format!("cannot read draft file `{:?}`", path))?;
|
||||
|
||||
Ok(draft)
|
||||
}
|
||||
|
||||
pub fn open_editor_with_draft() -> Result<String> {
|
||||
let path = msg::utils::draft_path();
|
||||
|
||||
// Opens editor and saves user input to draft file
|
||||
Command::new(env::var("EDITOR").context("cannot find `EDITOR` env var")?)
|
||||
.arg(&path)
|
||||
.status()
|
||||
.context("cannot launch editor")?;
|
||||
|
||||
// Extracts draft file content
|
||||
let mut draft = String::new();
|
||||
File::open(&path)
|
||||
.context(format!("cannot open file `{:?}`", path))?
|
||||
.read_to_string(&mut draft)
|
||||
.context(format!("cannot read file `{:?}`", path))?;
|
||||
|
||||
Ok(draft)
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
//! Modules related to User Interface.
|
||||
//! Module related to User Interface.
|
||||
|
||||
pub mod choice;
|
||||
pub mod editor;
|
||||
pub mod table;
|
||||
|
|
Loading…
Reference in a new issue