pub use shopless_types::Country;
#[derive(Debug, PartialEq, Clone, sqlx::FromRow)]
pub struct Address {
pub id: i64,
pub recipient: String,
pub line1: String,
pub line2: String,
pub postal_code: String,
pub city: String,
pub province_code: Option<String>,
pub province_name: Option<String>,
pub country_code: Country,
pub country_name: String,
pub phone: Option<String>,
pub vat: Option<String>,
}
pub async fn fetch<'e>(
id: i64,
conn: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
) -> Result<Option<Address>, sqlx::Error> {
sqlx::query_as("SELECT * FROM addresses WHERE id = $1")
.bind(id)
.fetch_optional(conn)
.await
}
#[derive(Debug)]
pub struct NewAddress<'a> {
pub recipient: &'a str,
pub line1: &'a str,
pub line2: &'a str,
pub postal_code: &'a str,
pub city: &'a str,
pub province_code: Option<&'a str>,
pub province_name: Option<&'a str>,
pub country_code: Country,
pub country_name: &'a str,
pub phone: Option<&'a str>,
}
pub async fn insert<'e>(
address: &NewAddress<'_>,
conn: impl sqlx::Executor<'_, Database = sqlx::Postgres>,
) -> Result<Address, sqlx::Error> {
let NewAddress {
recipient,
line1,
line2,
postal_code,
city,
province_code,
province_name,
country_code,
country_name,
phone,
} = address;
sqlx::query_as(
"
INSERT INTO addresses (
recipient,
line1,
line2,
postal_code,
city,
province_code,
province_name,
country_code,
country_name,
phone
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10
)
RETURNING *
",
)
.bind(recipient)
.bind(line1)
.bind(line2)
.bind(postal_code)
.bind(city)
.bind(province_code)
.bind(province_name)
.bind(country_code)
.bind(country_name)
.bind(phone)
.fetch_one(conn)
.await
}
#[derive(Debug, PartialEq)]
pub struct FirstLastName<'a> {
pub first_name: &'a str,
pub last_name: &'a str,
}
impl Address {
pub fn first_last_name(&self) -> FirstLastName<'_> {
let fullname = self.recipient.trim();
if let Some(ix) = fullname.rfind(' ') {
let (first_name, last_name) = fullname.split_at(ix);
FirstLastName {
first_name,
last_name: last_name.trim(),
}
} else {
FirstLastName {
first_name: fullname,
last_name: fullname,
}
}
}
}
#[cfg(test)]
mod test {
use super::{Address, FirstLastName};
use shopless_types::Country;
impl<'a> FirstLastName<'a> {
pub fn new(first_name: &'a str, last_name: &'a str) -> Self {
FirstLastName {
first_name,
last_name,
}
}
}
impl Address {
fn with_name(fullname: &str) -> Self {
Address {
id: 0,
recipient: fullname.to_string(),
line1: String::new(),
line2: String::new(),
postal_code: String::new(),
city: String::new(),
province_code: None,
province_name: None,
country_code: Country::DE,
country_name: String::new(),
phone: None,
vat: None,
}
}
}
#[test]
fn first_last_name_split() {
assert_eq!(
Address::with_name("Foo Bar").first_last_name(),
FirstLastName::new("Foo", "Bar")
);
assert_eq!(
Address::with_name("Foo Bar ").first_last_name(),
FirstLastName::new("Foo", "Bar")
);
assert_eq!(
Address::with_name("Foo A Bar").first_last_name(),
FirstLastName::new("Foo A", "Bar")
);
assert_eq!(
Address::with_name("Foo A-Bar").first_last_name(),
FirstLastName::new("Foo", "A-Bar")
);
assert_eq!(
Address::with_name("Foo Bar GmbH - EDV").first_last_name(),
FirstLastName::new("Foo Bar GmbH -", "EDV")
);
assert_eq!(
Address::with_name("Foobar").first_last_name(),
FirstLastName::new("Foobar", "Foobar")
);
}
}