partial implement client app

This commit is contained in:
2025-12-15 16:31:53 -06:00
parent 2f3dc5b623
commit 72e52a7d03
11 changed files with 416 additions and 75 deletions

110
src/models/email.rs Normal file
View File

@@ -0,0 +1,110 @@
use std::{fmt, str::FromStr};
use email_address::{EmailAddress, Options};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Email(String);
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EmailError {
Empty,
InvalidUtf8,
InvalidFormat,
}
impl Email {
pub fn new(s: impl AsRef<str>) -> Result<Self, EmailError> {
let s = s.as_ref().trim();
if s.is_empty() {
return Err(EmailError::Empty);
}
// Strict "address only" validation (no "Name <a@b.com>")
EmailAddress::parse_with_options(s, Options::default().without_display_text())
.map_err(|_| EmailError::InvalidFormat)?;
Ok(Self(s.to_owned()))
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
pub fn into_inner(self) -> String {
self.0
}
pub fn into_bytes(self) -> Vec<u8> {
self.0.into_bytes()
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, EmailError> {
let s = std::str::from_utf8(bytes).map_err(|_| EmailError::InvalidUtf8)?;
Self::new(s)
}
}
/* ---- std traits ---- */
impl fmt::Display for Email {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for Email {
type Err = EmailError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Email::new(s)
}
}
impl TryFrom<String> for Email {
type Error = EmailError;
fn try_from(value: String) -> Result<Self, Self::Error> {
Email::new(value)
}
}
impl TryFrom<&str> for Email {
type Error = EmailError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Email::new(value)
}
}
impl TryFrom<&[u8]> for Email {
type Error = EmailError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
Email::from_bytes(value)
}
}
impl TryFrom<Vec<u8>> for Email {
type Error = EmailError;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
Email::from_bytes(&value)
}
}
impl AsRef<str> for Email {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl AsRef<[u8]> for Email {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl From<Email> for Vec<u8> {
fn from(value: Email) -> Self {
value.into_bytes()
}
}