diff --git a/meli/src/contacts/editor.rs b/meli/src/contacts/editor.rs index 45329a6d..57b4ee6b 100644 --- a/meli/src/contacts/editor.rs +++ b/meli/src/contacts/editor.rs @@ -19,8 +19,7 @@ * along with meli. If not, see . */ -use std::collections::HashMap; - +use indexmap::IndexMap; use melib::Card; use crate::{ @@ -200,7 +199,7 @@ impl Component for ContactManager { None => {} Some(true) => { let fields = std::mem::take(&mut self.form).collect().unwrap(); - let fields: HashMap = fields + let fields: IndexMap = fields .into_iter() .map(|(s, v)| { ( diff --git a/meli/src/mail/view.rs b/meli/src/mail/view.rs index c0236e5b..efed3360 100644 --- a/meli/src/mail/view.rs +++ b/meli/src/mail/view.rs @@ -260,7 +260,7 @@ impl MailView { } let envelope: EnvelopeRef = account.collection.get_env(coordinates.2); - let mut entries = Vec::new(); + let mut entries: IndexMap = IndexMap::default(); for addr in envelope.from().iter().chain(envelope.to().iter()) { let mut new_card: Card = Card::new(); new_card @@ -269,12 +269,12 @@ impl MailView { if let Some(display_name) = addr.get_display_name() { new_card.set_name(display_name); } - entries.push((new_card, format!("{}", addr))); + entries.insert(new_card.clone(), (new_card, format!("{}", addr))); } drop(envelope); self.contact_selector = Some(Box::new(Selector::new( "select contacts to add", - entries, + entries.into_iter().map(|(_, v)| v).collect(), false, Some(Box::new(move |id: ComponentId, results: &[Card]| { Some(UIEvent::FinishedUIDialog(id, Box::new(results.to_vec()))) diff --git a/melib/src/addressbook.rs b/melib/src/addressbook.rs index d40cf0d0..c2bb0559 100644 --- a/melib/src/addressbook.rs +++ b/melib/src/addressbook.rs @@ -23,8 +23,13 @@ pub mod mutt; #[cfg(feature = "vcard")] pub mod vcard; -use std::{collections::HashMap, ops::Deref}; +use std::{ + collections::HashMap, + hash::{Hash, Hasher}, + ops::Deref, +}; +use indexmap::IndexMap; use uuid::Uuid; use crate::utils::{ @@ -57,11 +62,7 @@ impl From for String { impl From for CardId { fn from(s: String) -> Self { - use std::{ - collections::hash_map::DefaultHasher, - hash::{Hash, Hasher}, - str::FromStr, - }; + use std::{collections::hash_map::DefaultHasher, str::FromStr}; if let Ok(u) = Uuid::parse_str(s.as_str()) { Self::Uuid(u) @@ -80,7 +81,7 @@ pub struct AddressBook { display_name: String, created: UnixTimestamp, last_edited: UnixTimestamp, - pub cards: HashMap, + pub cards: IndexMap, } #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] @@ -97,7 +98,7 @@ pub struct Card { key: String, color: u8, last_edited: UnixTimestamp, - extra_properties: HashMap, + extra_properties: IndexMap, /// If `true`, we can't make any changes because we do not manage this /// resource. external_resource: bool, @@ -115,13 +116,24 @@ impl std::fmt::Display for Card { } } +impl Hash for Card { + fn hash(&self, state: &mut H) { + let mut serialized = serde_json::json! { self }; + // The following anonymous let bind is just to make sure at compile-time that + // `id` field is present in Self and serialized["id"] will be a valid access. + let _: &CardId = &self.id; + serialized["id"] = serde_json::json! { CardId::Hash(0) }; + serialized.to_string().hash(state); + } +} + impl AddressBook { pub fn new(display_name: String) -> Self { Self { display_name, created: now(), last_edited: now(), - cards: HashMap::default(), + cards: IndexMap::default(), } } @@ -199,9 +211,9 @@ impl AddressBook { } impl Deref for AddressBook { - type Target = HashMap; + type Target = IndexMap; - fn deref(&self) -> &HashMap { + fn deref(&self) -> &IndexMap { &self.cards } } @@ -223,6 +235,12 @@ macro_rules! set_fn { }; } +impl Default for Card { + fn default() -> Self { + Self::new() + } +} + impl Card { pub fn new() -> Self { Self { @@ -240,7 +258,7 @@ impl Card { last_edited: now(), external_resource: false, - extra_properties: HashMap::default(), + extra_properties: IndexMap::default(), color: 0, } } @@ -266,7 +284,7 @@ impl Card { self.extra_properties.get(key).map(String::as_str) } - pub fn extra_properties(&self) -> &HashMap { + pub fn extra_properties(&self) -> &IndexMap { &self.extra_properties } @@ -299,8 +317,8 @@ impl Card { } } -impl From> for Card { - fn from(mut map: HashMap) -> Self { +impl From> for Card { + fn from(mut map: IndexMap) -> Self { let mut card = Self::new(); macro_rules! get { ($key:literal, $field:tt) => { @@ -322,8 +340,9 @@ impl From> for Card { } } -impl Default for Card { - fn default() -> Self { - Self::new() +impl From> for Card { + fn from(map: HashMap) -> Self { + let map: IndexMap = map.into_iter().collect(); + Self::from(map) } } diff --git a/melib/src/addressbook/vcard.rs b/melib/src/addressbook/vcard.rs index d1a47d5c..79bb6735 100644 --- a/melib/src/addressbook/vcard.rs +++ b/melib/src/addressbook/vcard.rs @@ -186,7 +186,6 @@ impl TryInto for VCard { fn try_into(mut self) -> crate::error::Result { let mut card = Card::new(); card.set_id(CardId::Hash({ - use std::hash::Hasher; let mut hasher = std::collections::hash_map::DefaultHasher::new(); if let Some(val) = self.0.get("FN") { hasher.write(val.value.as_bytes());