#[cfg(feature = "diesel_support")]
use crate::schema::*;
use crate::*;
#[cfg(feature = "diesel_support")]
use diesel_migrations::embed_migrations;
#[cfg(feature = "sqlx")]
use sqlx::migrate::Migrator;
pub mod contact_information;
pub mod error;
pub use error::*;
#[cfg_attr(
feature = "diesel",
derive(Insertable, Queryable, Identifiable, AsChangeset),
primary_key(uid),
table_name = "contacts"
)]
#[derive(Default, Clone, PartialEq, Debug)]
pub struct SqlContact {
pub uid: String,
pub name_given: Option<String>,
pub name_additional: Option<String>,
pub name_family: Option<String>,
pub name_prefixes: Option<String>,
pub name_suffixes: Option<String>,
pub nickname: Option<String>,
pub anniversary: Option<chrono::NaiveDateTime>,
pub bday: Option<chrono::NaiveDateTime>,
pub gender_gender: Option<String>,
pub gender_sex: Option<String>,
pub photo_uri: Option<String>,
pub photo_bin: Option<Vec<u8>>,
pub photo_mime: Option<String>,
pub title: Option<String>,
pub role: Option<String>,
pub org_org: Option<String>,
pub org_unit: Option<String>,
pub org_office: Option<String>,
pub logo_uri: Option<String>,
pub logo_bin: Option<Vec<u8>>,
pub logo_mime: Option<String>,
pub home_address_street: Option<String>,
pub home_address_locality: Option<String>,
pub home_address_region: Option<String>,
pub home_address_code: Option<String>,
pub home_address_country: Option<String>,
pub home_address_geo_longitude: Option<f64>,
pub home_address_geo_latitude: Option<f64>,
pub work_address_street: Option<String>,
pub work_address_locality: Option<String>,
pub work_address_region: Option<String>,
pub work_address_code: Option<String>,
pub work_address_country: Option<String>,
pub work_address_geo_longitude: Option<f64>,
pub work_address_geo_latitude: Option<f64>,
}
impl TryFrom<SqlContact> for Contact {
type Error = SqlConversionError;
fn try_from(them: SqlContact) -> Result<Self, Self::Error> {
Ok(Self {
uid: them.uid,
name: Name {
given: them.name_given.map_or(Vec::new(), |x| Name::unescape_with_spaces(&x)),
additional: them.name_additional.map_or(Vec::new(), |x| Name::unescape_with_spaces(&x)),
family: them.name_family.map_or(Vec::new(), |x| Name::unescape_with_spaces(&x)),
prefixes: them.name_prefixes.map_or(Vec::new(), |x| Name::unescape_with_spaces(&x)),
suffixes: them.name_suffixes.map_or(Vec::new(), |x| Name::unescape_with_spaces(&x)),
},
nickname: them.nickname,
anniversary: match them.anniversary {
None => None,
Some(x) => Some(DateTime::try_from(x)?),
},
bday: match them.bday {
None => None,
Some(x) => Some(DateTime::try_from(x)?),
},
photo: Uri::try_from_sql_raw((
them.photo_uri,
them.photo_bin,
them.photo_mime,
String::from("photo"),
))?,
title: them.title,
role: them.role,
gender: if them.gender_gender.is_some() || them.gender_sex.is_some() {
Some(Gender {
sex: match them.gender_sex.as_deref() {
None | Some("N") => None,
Some("M") => Some(Sex::Male),
Some("F") => Some(Sex::Female),
Some("O") => Some(Sex::Other),
Some("U") => Some(Sex::Unknown),
_ => return Err(Self::Error::InvalidGender),
},
identity: them.gender_gender
})
} else {
None
},
org: {
if them.org_org.is_some()
&& them.org_unit.is_some()
&& them.org_office.is_some()
{
Some(Org::new(
them.org_org.unwrap(),
them.org_unit.unwrap(),
them.org_office.unwrap(),
))
} else if them.org_org.is_none()
&& them.org_unit.is_none()
&& them.org_office.is_none()
{
None
} else {
return Err(Self::Error::IncompleteOrg);
}
},
logo: Uri::try_from_sql_raw((
them.logo_uri,
them.logo_bin,
them.logo_mime,
String::from("logo"),
))?,
home_address: address::AddressRaw::new(
them.home_address_street,
them.home_address_locality,
them.home_address_region,
them.home_address_code,
them.home_address_country,
them.home_address_geo_longitude,
them.home_address_geo_latitude,
)
.try_from_sql_raw()?,
work_address: address::AddressRaw::new(
them.work_address_street,
them.work_address_locality,
them.work_address_region,
them.work_address_code,
them.work_address_country,
them.work_address_geo_longitude,
them.work_address_geo_latitude,
)
.try_from_sql_raw()?,
contact_information: vec![],
})
}
}
impl TryFrom<Contact> for SqlContact {
type Error = Box<dyn std::error::Error>;
fn try_from(them: Contact) -> Result<Self, Self::Error> {
let (photo_uri, photo_bin, photo_mime) = Uri::to_sql_raw(them.photo);
let (logo_uri, logo_bin, logo_mime) = Uri::to_sql_raw(them.logo);
let (org_org, org_unit, org_office) = match them.org {
Some(org) => (Some(org.org), Some(org.unit), Some(org.office)),
None => (None, None, None),
};
let (
home_address_street,
home_address_locality,
home_address_region,
home_address_code,
home_address_country,
(home_address_geo_longitude, home_address_geo_latitude),
) = Address::to_sql_raw(them.home_address);
let (
work_address_street,
work_address_locality,
work_address_region,
work_address_code,
work_address_country,
(work_address_geo_longitude, work_address_geo_latitude),
) = Address::to_sql_raw(them.work_address);
Ok(Self {
uid: them.uid,
name_given: Name::escape_with_spaces(them.name.given.iter().map(String::as_str)),
name_additional: Name::escape_with_spaces(them.name.additional.iter().map(String::as_str)),
name_family: Name::escape_with_spaces(them.name.family.iter().map(String::as_str)),
name_prefixes: Name::escape_with_spaces(them.name.prefixes.iter().map(String::as_str)),
name_suffixes: Name::escape_with_spaces(them.name.suffixes.iter().map(String::as_str)),
nickname: them.nickname,
anniversary: match them.anniversary {
Some(x) => x.try_into()?,
None => None,
},
bday: match them.bday {
Some(x) => x.try_into()?,
None => None,
},
gender_sex: them.gender.as_ref().map(|x| x.sex.map_or('N', char::from).to_string()),
gender_gender: them.gender.and_then(|x| x.identity),
photo_uri,
photo_bin,
photo_mime,
title: them.title,
role: them.role,
org_org,
org_unit,
org_office,
logo_uri,
logo_bin,
logo_mime,
home_address_street,
home_address_locality,
home_address_region,
home_address_code,
home_address_country,
home_address_geo_longitude,
home_address_geo_latitude,
work_address_street,
work_address_locality,
work_address_region,
work_address_code,
work_address_country,
work_address_geo_longitude,
work_address_geo_latitude,
})
}
}
#[cfg(feature = "diesel_support")]
embed_migrations!("diesel-migrations/");
#[cfg(feature = "diesel_support")]
pub mod embeded_diesel_migrations {
pub use super::embedded_migrations::*;
}
#[cfg(feature = "sqlx_support")]
pub static SQLX_MIGRATIONS: Migrator = {
let mut migrator = sqlx::migrate!("./sqlx-migrations");
migrator.ignore_missing = true;
migrator
};