refactor msg model (#173)

* Adding Mail structure

Adding a main structure which can be used for *everything* which has to do with
a mail:
    - Writing a new mail
    - Fetching the information of a mail

* Write mails

User can write mails now

* Writing mail

When mail is converted to a sendable message, it'll print out a nice little
error message what to do and which field is missing a value.

* Mail

List subcommand works with new struct now.

* Forwarding

Started implementation for forwarding message

* Breaking Commit

This is just a "backup" commit

* First finished

Himalaya can compile successfully now.

* Removed uneccessary files

- Moved everything from msg/mail to msg/model
- Removed uneccessary files

* Renaming

Renamed all "Mail" and "Mails" struct to "Msg" and "Msgs".

* Cleaning

Removed an CLI-Subcommand which can't be used anymore

* Flags

Fixed flags to vector and added the template subcommand back

* Changes to Flags

Changed the datatype from Vec<Flag<'static>> to HashSet<Flag<'static>>, because
each Message/Mail can include only one flag-type, so why not a HashSet for this
job?

* Cargo.toml changes

Fixed the lettre-dependencie which points to the pull request with the given
serde implementation for ContentType (needed for Attachments).

* Fix Template bug and removed unnecessary files.

- Removed the msg/flag/flag.rs file since we can use the imap::types::Flag
  implementation now
- `himalaya template new` printed the template two times. This should be fixed
  now

* Template command

Fixed formatting when printing out template

* Sending Mail

Fixed bug that user can't send a mail

* Msg

Moved the body from the attachment-vector out to an external attribute of the
struct.

* Msg listing and changed Msg::from to Msg::try_from

- Fixed bug that listing didn't showed up addresses in the `From:` field for
  example
- Made each `from` trait function to `try_from` for better error-handling

* Tests

- Fixed tests in `tests/imap_conn.rs`

* Cargo.toml changes, Bug fixes, Documentation

- Updated mailparse to 0.13.4
- Added new "new" function to Account
- Cleaned up some functions (removed some)
- Added Eq and PartialEq derives for msg
- Bugfix:
    It couldn't get the body of some mails, because they were inside a
    multipart/alternative part. Now the mail is iterating through all subparts
    and picks up the firs text/plain "attachment" and uses it as the body.

* Changed Msg attributes viewability

- Made the "main attributes" of the Msg struct public
- Removed to getter functions

* Big envelope changes

- Added documentation
- Removed the getter functions, beacuse the attributes are public

* Documentation and Cleanup

- Removed the `new` constructor of the envelope, since it's actually the same as
  Envelope::default()
- Addded tests and Documentation to Attachments.rs

* Documentation and Tests

- Added docuemntation for msg/body.rs
- Fixed some syntax errors in the doc strings

* General msg

- Added `get_raw` function and `raw` field for the `Msg` struct.
- Fixed raw output of msg
- Started documentation + tests for the Msg struct

* Changes to Msg

- Added Clone derive
- Added documentation for change_to_reply method
- Added tests to change_to_reply method

* Msg tests and Account changes

- Changed `Account::new()` function
- Added more documentation to Msg struct
- Added more tests to Msg struct

* Removed an unknown file

Removed src/.rust_info.json (don't know where it came)

* Msgs finished(?)

Added final documentation to the Msg struct.

* ImapConnector Fix

Fixed the bug, when trying to move a msg, the envelope wasn't applied to the
fetch. Fixed that in the `get_msg` method.

* Msg

- Bug fixes:
    - Adding Message ID and Subject in the to_sendable_msg function

- Removed an println statement for debugging
- Added more error messages

* Cargo.toml

Changed order and added some comments to the dependencies.

* Msg

Removed an unnecessary documentation part.

* Fixed documentation

* Removing non-debugflags for dev profile

Removing debug=false for the dev profile since it was just for me.

* Cleanup

Removed the comment blocks and reduced some comments

* Cleanup

Reformatted some stuff

* Cleanup

Replaced the word "mail" with "msg".

* Formatting

Fixed formatting in src/flag/model.rs file

* Little fix

* Changes and tests

- New "feature":
    If you reply to a reply, the subject won't look like this for example:

        Re: Re: Re: Re: Re: Re: The subject

- Fixed tests. All tests pass now (run `cargo test`)

* Idea(?)

Renamed all <module>_matches/_subcmds to general "matches" and "subcmds()".
All modules have the same: "matches()" and "subcmds()"

* Little fix

Changed the name from "imap_conn" to "conn" by mistake. Fixed that

* Bug fix

When sending a message, himalaya will generate a UUID on its own if there's no
message-id for the message yet.

* Bug fix

Removed angle brackets, since they are added through the lettre library.

* Bugfix

Removed an unnecessary (old) line.

* Cleanup

Removed the last comment blocks.

* Fixed lettre dependencie

* Bugfixes and Error handling

- When calling the msg_interaction function, the user can edit the msg first,
  before the prompt comes up
- Also added a error output, if the msg couldn't be converted into a sendable
  message.

* Error handling

Improved output of error

* Bug fixes, Error Handling

- Improved error handling for the string parsing
- Added attempt to fix the bug that a whitespace is added in the end of an
  address

* Trimming

Added trims to avoid invalid white spaces in the addresses.

* Fixing whitespace bug

All addresses are gonna trimmed before adding to a header now

* Adding encoding, Changed dependencie

- Added encoding for the body part
- Changed the lettre dependencie of lettre to TornaxO7's fork of lettre, because
  the "ContentTypeEncoding" struct needs the "Eq", "Serialize" and "Deserialize"
  derives.

* Improved Error handling

Added a warning, if a message included an unknown attachment.

* Fixed tests

Fixed the documentation for passing the tests.

* Doc change

* Bugfis: When replying, signature is added now as well

* Bugfix: Forwarding Message

When forwarding a message, himalaya, put the signature in the end of the
mail/msg. Now it's added above the '-------Forwarded Message---------' line.

* Readjusted tests and new method

- Changed the way to create a new account:
    - Account::new => Sets signautre to "None"
    - Account::new_with_signature => Sets signature to the given argument

    This makes it more flexible to create specifique accounts for tests for
    example.

- Fixed the tests so all are passing now

* improve sig and sig delim concat process

* add signature delim struct comment

* fix signatures + tests

* fix body and signature new lines

* Adding [serde(rename_all = "camelCase")] to structs

* fix reply indentation and signature new lines

* add default rustfmt.toml

* apply fmt on all the project

* fix msg tests

* Makeing Ctx struct independent

- The Ctx struct doesn't include references anymore. This makes it easier to
    create new Ctx instances by doing the following:

        Ctx {
            <attribute>: <value>,
            .. Ctx::default()
        }

    This helps especially for writing tests.

Also the attributes of the Ctx struct in the main-entry function aren't used
anymore after creating the Ctx struct. So there's no need to have only
references in the Ctx struct.

* Fixing JSON output

- JSON of message includes `hasAttachment` key now
- JSON output shows both body types: Text and Html
- Changed `Body` struct so it can store html and text now.

* Tests

Updated tests with the latest Body implementation

* Fixes

- Removed suspicious println macro in serializer of msg... *cough cough*
- Fixed output in the "read" command
- othe small fixes

* Formatting

Formatted all files

* Msg

- Adding 'get_full_message' method which prints out all information of the
  message in a string

* New Msg-Struct

Adding MsgSerialized, a struct, which represents the "correct" serialized
version of a message because it includes another attribute: `has_attachment`.

* Cleanup

Removed the manual serialize implementation of `Msg` and added a little more
info about the MsgSerialized.

* Test fixes

Adjusted all tests so all are passing now.

* Little changes

- Used a better condition for checking if the message includes attachments or
  not
- format fixes

* Fixing tests and Docs

- Provided more docs
- Refactored tests and added more tests

* Expanding specials

Added more "special characters" which will add some quotes around the name if it
includes at least one of them.

* Fixing test

Improved the detection if the mail-name includes a special character or not.

* Variable renaming

Renamed a variable for better readability.

* Envelope renaming

* Small change

Renamed the variable of the `TryFrom` implementation for the
imap_proto::Envelope.

* Last stuff

- Making the attributes of mboxes independent. We can store them now as well!
- Added more docs
- Added type-safety for flags
- Expanded flags a bit
- Added more tests
- Added a short summary of the file-structure in the beginning of the doc.

* Help command fix

Fixing help command description.

* Small doc change

* Doc fix

Fixing the link to the mbox delimiter.

* Fixing typo

* Doc fix

* Added docs for Output struct

* Fixing tests

Fixing a little test issue

* Formatting changes + doc change

- Removed bold + capital words for logout-doc
- Run format on each *.rs file

* Fixing tests

- Testing the return value of the flags struct as a string doesn't really work
  since it's a HashSet => Converted it into a Vec (in the test) to set the order
  as well.
- Fixed imap test by reverting the changes in the test.

* Error handling

Changed error output when creating an Imap-Connection. Should help debugging :)

* Formatting fixes and refactoring

- Using `trim_end_matches` instead of "pop"s now.
- Executed `cargo fmt`

* Trying to fix test workflow

* Fixes

Updated dependencies with `cargo update` and let cargo point to master branch of
TornaxO7's lettre-fork because this should probably fix the issue with the
nix-build.

* Test fix

Fixing the workflow.

* Workflow fix

Removing semicolon

* Starting workflow

Added a new line to be able to push.

* Workflow

Reverting the workflow command.

* Workflows

Reverting workflow to master workflow.

* let actions/checkout@v2 run first

* Forwarded message's signature misplaced

Changes the order of the signature for forwarded messages.

* Output change

Changed the output if an error occurs.

* Fixing output for template-building

* Template shows raw data with JSON format #23

When printing the message in json, the raw message is printed out as a string
now.

* the_sender_is_not_displayed_properly_in_table_and_json #21

- When displaying the table, we'll look first, if a name exists, if yes => use
  it otherwise use the email address.

- Added the rfc2047_decoder for parsing addresses

* Formatting

Run 'cargo fmt'

Co-authored-by: Clément DOUIN <soywod@users.noreply.github.com>
Co-authored-by: Erik <erik1000@protonmail.com>
This commit is contained in:
TornaxO7
2021-09-11 00:35:22 +02:00
committed by GitHub
parent 2ac2f53f31
commit 0e68801a35
30 changed files with 4381 additions and 1967 deletions
+19 -17
View File
@@ -10,28 +10,14 @@ error_chain! {
}
}
pub fn mbox_source_arg<'a>() -> clap::Arg<'a, 'a> {
clap::Arg::with_name("mailbox")
.short("m")
.long("mailbox")
.help("Selects a specific mailbox")
.value_name("MAILBOX")
.default_value("INBOX")
}
pub fn mbox_target_arg<'a>() -> clap::Arg<'a, 'a> {
clap::Arg::with_name("target")
.help("Specifies the targetted mailbox")
.value_name("TARGET")
}
pub fn mbox_subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
// == Main functions ==
pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
vec![clap::SubCommand::with_name("mailboxes")
.aliases(&["mailbox", "mboxes", "mbox", "m"])
.about("Lists all mailboxes")]
}
pub fn mbox_matches(ctx: &Ctx) -> Result<bool> {
pub fn matches(ctx: &Ctx) -> Result<bool> {
if let Some(_) = ctx.arg_matches.subcommand_matches("mailboxes") {
debug!("mailboxes command matched");
@@ -49,3 +35,19 @@ pub fn mbox_matches(ctx: &Ctx) -> Result<bool> {
debug!("nothing matched");
Ok(false)
}
// == Argument Functions ==
pub fn source_arg<'a>() -> clap::Arg<'a, 'a> {
clap::Arg::with_name("mailbox")
.short("m")
.long("mailbox")
.help("Selects a specific mailbox")
.value_name("MAILBOX")
.default_value("INBOX")
}
pub fn mbox_target_arg<'a>() -> clap::Arg<'a, 'a> {
clap::Arg::with_name("target")
.help("Specifies the targetted mailbox")
.value_name("TARGET")
}
+65 -37
View File
@@ -1,8 +1,10 @@
use imap;
use imap::types::NameAttribute;
use serde::{
ser::{self, SerializeSeq},
Serialize,
};
use std::borrow::Cow;
use std::collections::HashSet;
use std::fmt;
use crate::table::{Cell, Row, Table};
@@ -10,16 +12,16 @@ use crate::table::{Cell, Row, Table};
// Attribute
#[derive(Debug, PartialEq)]
struct SerializableAttribute<'a>(&'a imap::types::NameAttribute<'a>);
struct SerializableAttribute<'a>(&'a NameAttribute<'a>);
impl<'a> Into<&'a str> for &'a SerializableAttribute<'a> {
fn into(self) -> &'a str {
match &self.0 {
imap::types::NameAttribute::NoInferiors => "\\NoInferiors",
imap::types::NameAttribute::NoSelect => "\\NoSelect",
imap::types::NameAttribute::Marked => "\\Marked",
imap::types::NameAttribute::Unmarked => "\\Unmarked",
imap::types::NameAttribute::Custom(cow) => cow,
NameAttribute::NoInferiors => "\\NoInferiors",
NameAttribute::NoSelect => "\\NoSelect",
NameAttribute::Marked => "\\Marked",
NameAttribute::Unmarked => "\\Unmarked",
NameAttribute::Custom(cow) => cow,
}
}
}
@@ -33,41 +35,47 @@ impl<'a> ser::Serialize for SerializableAttribute<'a> {
}
}
/// Represents the attributes of a mailbox.
#[derive(Debug, PartialEq)]
pub struct Attributes<'a>(&'a [imap::types::NameAttribute<'a>]);
pub struct Attributes(pub HashSet<NameAttribute<'static>>);
impl<'a> From<&'a [imap::types::NameAttribute<'a>]> for Attributes<'a> {
fn from(attrs: &'a [imap::types::NameAttribute<'a>]) -> Self {
Self(attrs)
impl<'a> From<&[NameAttribute<'a>]> for Attributes {
fn from(attrs: &[NameAttribute<'a>]) -> Self {
Self(
attrs
.iter()
.map(|attribute| convert_to_static(attribute).unwrap())
.collect::<HashSet<NameAttribute<'static>>>(),
)
}
}
impl<'a> ToString for Attributes<'a> {
impl ToString for Attributes {
fn to_string(&self) -> String {
match self.0.len() {
0 => String::new(),
1 => {
let attr = &SerializableAttribute(&self.0[0]);
let attr: &str = attr.into();
attr.to_owned()
}
_ => {
let attr = &SerializableAttribute(&self.0[0]);
let attr: &str = attr.into();
format!("{}, {}", attr, Attributes(&self.0[1..]).to_string())
}
let mut attributes = String::new();
for attribute in &self.0 {
let attribute = SerializableAttribute(&attribute);
attributes.push_str((&attribute).into());
attributes.push_str(", ");
}
// remove the trailing whitespace with the comma
attributes = attributes.trim_end_matches(' ').to_string();
attributes.pop();
attributes
}
}
impl<'a> ser::Serialize for Attributes<'a> {
impl ser::Serialize for Attributes {
fn serialize<T>(&self, serializer: T) -> Result<T::Ok, T::Error>
where
T: ser::Serializer,
{
let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
for attr in self.0 {
for attr in &self.0 {
seq.serialize_element(&SerializableAttribute(attr))?;
}
@@ -75,16 +83,23 @@ impl<'a> ser::Serialize for Attributes<'a> {
}
}
// Mailbox
// --- Mailbox ---
/// Represents a general mailbox.
#[derive(Debug, Serialize)]
pub struct Mbox<'a> {
pub struct Mbox {
/// The [hierarchie delimiter].
///
/// [hierarchie delimiter]: https://docs.rs/imap/2.4.1/imap/types/struct.Name.html#method.delimiter
pub delim: String,
/// The name of the mailbox.
pub name: String,
pub attributes: Attributes<'a>,
/// Its attributes.
pub attributes: Attributes,
}
impl<'a> From<&'a imap::types::Name> for Mbox<'a> {
impl<'a> From<&'a imap::types::Name> for Mbox {
fn from(name: &'a imap::types::Name) -> Self {
Self {
delim: name.delimiter().unwrap_or_default().to_owned(),
@@ -94,7 +109,7 @@ impl<'a> From<&'a imap::types::Name> for Mbox<'a> {
}
}
impl<'a> Table for Mbox<'a> {
impl Table for Mbox {
fn head() -> Row {
Row::new()
.cell(Cell::new("DELIM").bold().underline().white())
@@ -116,19 +131,32 @@ impl<'a> Table for Mbox<'a> {
}
}
// Mboxes
// --- Mboxes ---
/// A simple wrapper to acces a bunch of mboxes which are in this vector.
#[derive(Debug, Serialize)]
pub struct Mboxes<'a>(pub Vec<Mbox<'a>>);
pub struct Mboxes(pub Vec<Mbox>);
impl<'a> From<&'a imap::types::ZeroCopy<Vec<imap::types::Name>>> for Mboxes<'a> {
impl<'a> From<&'a imap::types::ZeroCopy<Vec<imap::types::Name>>> for Mboxes {
fn from(names: &'a imap::types::ZeroCopy<Vec<imap::types::Name>>) -> Self {
Self(names.iter().map(Mbox::from).collect::<Vec<_>>())
}
}
impl fmt::Display for Mboxes<'_> {
impl fmt::Display for Mboxes {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "\n{}", Table::render(&self.0))
}
}
// == Helper Functions ==
fn convert_to_static<'func>(
attribute: &'func NameAttribute<'func>,
) -> Result<NameAttribute<'static>, ()> {
match attribute {
NameAttribute::NoInferiors => Ok(NameAttribute::NoInferiors),
NameAttribute::NoSelect => Ok(NameAttribute::NoSelect),
NameAttribute::Marked => Ok(NameAttribute::Marked),
NameAttribute::Unmarked => Ok(NameAttribute::Unmarked),
NameAttribute::Custom(cow) => Ok(NameAttribute::Custom(Cow::Owned(cow.to_string()))),
}
}