contack 0.9.2

A simple and easy contact library.
Documentation
//! Stores information regarding contact information.
//!
//! Contact information specified anything that you can use
//! to communicate with the person, such as a phone number, an email or
//! an irc. Contack includes some added types not present in the
//! VCard specification.

use crate::contact_platform::ContactPlatform;

#[cfg(feature = "read_write")]
use crate::read_write::component::Component;
#[cfg(feature = "read_write")]
use crate::read_write::error::FromComponentError;

/// Represents any way which a contact can be contacted.
///
/// This may be a phone number, an email etc. You can add
/// any number of them to a `Contact`.
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct ContactInformation {
    /// The pid, used for representing this amongst
    /// other `ContactInformation`s.
    pub pid: String,

    /// The Preference Value. In this implementation 0 represents that it is
    /// neutral, not either way.
    pub pref: u8,

    /// The Value (for example johndoe@example.com)
    pub value: String,

    /// The platform which this is on
    pub platform: ContactPlatform,

    /// The type
    pub typ: Option<Type>,
}

#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(
    feature = "diesel-derive-enum",
    derive(DbEnum),
    DieselType = "Contack_contact_information_type",
    db_rename = "Contack_contact_information_type"
)]
#[cfg_attr(
    feature = "sqlx",
    derive(sqlx::Type),
    sqlx(
        type_name = "contack_contact_information_type",
        rename_all = "PascalCase"
    )
)]
/// Reperesents the type of a `ContactInformation`.
///
/// The type specifies whether this is a personal
/// or public `ContactInformation`.
pub enum Type {
    /// Means that it is fork work or business, ie
    /// a git commit email.
    Work,
    /// Indicates that it is personal, such as a
    /// personal phone number used on signal.
    Home,
}

impl ContactInformation {
    /// Creates a new `ContactInformation`
    ///
    /// This also auto generates a pid between 0 and 100
    #[must_use]
    pub fn new(string: String, platform: ContactPlatform) -> Self {
        Self {
            pref: 0,
            value: string,
            pid: format!("{}", rand::random::<u64>() % 100),
            platform,
            typ: None,
        }
    }
}

#[cfg(feature = "read_write")]
impl From<ContactInformation> for Component {
    fn from(ci: ContactInformation) -> Self {
        Self {
            // No group
            group: None,

            // Name of platform
            name: match ci.platform {
                ContactPlatform::Email => "EMAIL",
                ContactPlatform::Tel => "TEL",
                ContactPlatform::Discord => "X-DISCORD",
                ContactPlatform::Matrix => "X-MATRIX",
                ContactPlatform::Skype => "X-SKYPE",
                ContactPlatform::Aim => "X-AIM",
                ContactPlatform::Jabber => "X-JABBER",
                ContactPlatform::Icq => "X-ICQ",
                ContactPlatform::Groupwise => "X-GROUPWISE",
                ContactPlatform::Gadugadu => "X-GADUGADU",
                ContactPlatform::Unknown => "IMPP",
            }
            .to_string(),

            // Parameters
            parameters: {
                // HashMap
                let mut hashmap = std::collections::HashMap::new();

                // Append the preference value.
                if ci.pref != 0 {
                    hashmap.insert("PREF".to_string(), ci.pref.to_string());
                }

                // Append the type
                if let Some(typ) = ci.typ {
                    hashmap.insert(
                        "TYPE".to_string(),
                        match typ {
                            Type::Home => "home",
                            Type::Work => "work",
                        }
                        .to_string(),
                    );
                }

                // And the pid
                hashmap.insert("PID".to_string(), ci.pid.to_string());

                hashmap
            },

            values: vec![vec![ci.value]],
        }
    }
}

#[cfg(feature = "read_write")]
impl TryFrom<Component> for ContactInformation {
    type Error = FromComponentError;

    fn try_from(mut comp: Component) -> Result<Self, Self::Error> {
        Ok(Self {
            pid: comp
                .parameters
                .remove("PID")
                .unwrap_or(format!("{}", rand::random::<u64>() % 100)),
            pref: comp.parameters.remove("PREF").map_or(Ok(0), |x| {
                x.parse().map_err(FromComponentError::ParseIntError)
            })?,
            value: comp
                .values
                .pop()
                .map(|mut x| x.pop())
                .unwrap_or_default()
                .ok_or(FromComponentError::NotEnoughValues)?,
            platform: match comp.name.as_str() {
                "EMAIL" => ContactPlatform::Email,
                "TEL" => ContactPlatform::Tel,
                "X-DISCORD" => ContactPlatform::Discord,
                "X-MATRIX" => ContactPlatform::Matrix,
                "X-SKYPE" => ContactPlatform::Skype,
                "X-AIM" => ContactPlatform::Aim,
                "X-JABBER" => ContactPlatform::Jabber,
                "X-ICQ" => ContactPlatform::Icq,
                "X-GROUPWISE" => ContactPlatform::Groupwise,
                "X-GADUGADU" => ContactPlatform::Gadugadu,
                _ => ContactPlatform::Unknown,
            },
            typ: comp
                .parameters
                .remove("TYPE")
                .map(|x| {
                    match x.to_lowercase().split(',').next().unwrap_or_default()
                    {
                        "home" => Some(Type::Home),
                        "work" => Some(Type::Work),
                        // Its not fair to throw an error since technically
                        // other values may be used.
                        _ => None,
                    }
                })
                .unwrap_or_default(),
        })
    }
}