Feature flag: contact
[dependencies]
arvo = { version = "1.0", features = ["contact"] }A validated, normalised email address.
Normalisation: trimmed, lowercased.
Validation: must match local@domain.tld pattern (RFC 5322 lite — full compliance intentionally out of scope).
use arvo::contact::EmailAddress;
use arvo::traits::{PrimitiveValue, ValueObject};
let email = EmailAddress::new("User@Example.COM".into())?;
assert_eq!(email.value(), "user@example.com");
assert_eq!(email.local_part(), "user");
assert_eq!(email.domain(), "example.com");
// try_into from &str
let email: EmailAddress = "hello@example.com".try_into()?;| Method | Returns | Example |
|---|---|---|
value() |
&String |
"user@example.com" |
local_part() |
&str |
"user" |
domain() |
&str |
"example.com" |
into_inner() |
String |
"user@example.com" |
| Input | Error |
|---|---|
"" |
ValidationError::Empty |
"notanemail" |
ValidationError::InvalidFormat |
A validated ISO 3166-1 alpha-2 country code.
Normalisation: trimmed, uppercased.
Validation: exactly 2 ASCII letters.
use arvo::contact::CountryCode;
use arvo::traits::{PrimitiveValue, ValueObject};
let code = CountryCode::new("cz".into())?;
assert_eq!(code.value(), "CZ");
// try_into from &str
let code: CountryCode = "DE".try_into()?;| Method | Returns | Example |
|---|---|---|
value() |
&String |
"CZ" |
into_inner() |
String |
"CZ" |
| Input | Error |
|---|---|
"" |
ValidationError::InvalidFormat |
"USA" |
ValidationError::InvalidFormat (3 letters) |
"C1" |
ValidationError::InvalidFormat (digit) |
A validated phone number stored in canonical E.164 format.
Normalisation: non-digit characters stripped from the local number.
Validation: local number must be 4–14 digits. Calling code is derived from CountryCode.
use arvo::contact::{CountryCode, PhoneNumber, PhoneNumberInput};
use arvo::traits::{PrimitiveValue, ValueObject};
let phone = PhoneNumber::new(PhoneNumberInput {
country_code: CountryCode::new("CZ".into())?,
number: "123 456 789".into(), // spaces stripped automatically
})?;
assert_eq!(phone.value(), "+420123456789");
assert_eq!(phone.calling_code(), "+420");
assert_eq!(phone.number(), "123456789");
assert_eq!(phone.country_code().value(), "CZ");| Method | Returns | Example |
|---|---|---|
value() |
&String |
"+420123456789" |
calling_code() |
&str |
"+420" |
number() |
&str |
"123456789" |
country_code() |
&CountryCode |
CountryCode("CZ") |
into_inner() |
PhoneNumberInput |
{ country_code: "CZ", number: "123456789" } |
pub struct PhoneNumberInput {
pub country_code: CountryCode,
pub number: String, // digits only; formatting characters are stripped
}| Input | Error |
|---|---|
number: "" |
ValidationError::InvalidFormat |
number: "123" |
ValidationError::InvalidFormat (too short, min 4 digits) |
number: "123456789012345" |
ValidationError::InvalidFormat (too long, max 14 digits) |
Serde: serialises as a JSON object
{ "country_code": "CZ", "number": "123456789" }(the input struct).
TryFrom<&str>: not implemented — the E.164 canonical string cannot be unambiguously decoded back to structuredPhoneNumberInput.
A validated website URL. Accepts http and https schemes only. Scheme and host are normalised to lowercase on construction.
Normalisation: scheme and host lowercased.
Validation: must be a valid URL with http or https scheme and a host.
use arvo::contact::Website;
use arvo::traits::{PrimitiveValue, ValueObject};
let site = Website::new("https://EXAMPLE.COM/path".into())?;
assert_eq!(site.value(), "https://example.com/path");
assert!(site.is_https());
assert_eq!(site.host(), "example.com");
// try_into from &str
let site: Website = "https://example.com".try_into()?;| Method | Returns | Example |
|---|---|---|
value() |
&String |
"https://example.com/" |
is_https() |
bool |
true |
host() |
&str |
"example.com" |
into_inner() |
String |
"https://example.com/" |
| Input | Error |
|---|---|
"" |
ValidationError::Empty |
"not-a-url" |
ValidationError::InvalidFormat |
"ftp://example.com" |
ValidationError::InvalidFormat (scheme not allowed) |
A validated composite postal address. All text fields are trimmed; empty or whitespace-only values are rejected. The country field requires a valid [CountryCode].
Normalisation: street, city, and zip are trimmed.
Validation: all fields must be non-empty after trimming.
use arvo::contact::{CountryCode, PostalAddress, PostalAddressInput};
use arvo::traits::{PrimitiveValue, ValueObject};
let addr = PostalAddress::new(PostalAddressInput {
street: "Václavské náměstí 1".into(),
city: "Prague".into(),
zip: "110 00".into(),
country: CountryCode::new("CZ".into())?,
})?;
assert_eq!(addr.street(), "Václavské náměstí 1");
assert_eq!(addr.zip(), "110 00");
assert_eq!(addr.country().value(), "CZ");
// value() / Display — multi-line canonical form
assert_eq!(addr.value(), "Václavské náměstí 1\n110 00 Prague\nCZ");pub struct PostalAddressInput {
pub street: String,
pub city: String,
pub zip: String,
pub country: CountryCode,
}| Method | Returns | Example |
|---|---|---|
value() |
&String |
"Main St 1\n10115 Berlin\nDE" |
street() |
&str |
"Main St 1" |
city() |
&str |
"Berlin" |
zip() |
&str |
"10115" |
country() |
&CountryCode |
CountryCode("DE") |
into_inner() |
PostalAddressInput |
original input fields |
| Field | Input | Error |
|---|---|---|
street |
"" or whitespace |
ValidationError::Empty |
city |
"" or whitespace |
ValidationError::Empty |
zip |
"" or whitespace |
ValidationError::Empty |
Serde: serialises as a JSON object matching
PostalAddressInput(the input struct).
TryFrom<&str>: not implemented — the formatted multi-line string cannot be unambiguously decoded back to structured fields.