use std::{ops::Deref, str::FromStr}; use rusqlite::{ ToSql, types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef}, }; use crate::ParseError; const USER_ID_LENGTH: usize = 6; const SESSION_ID_LENGTH: usize = 12; #[derive(Copy, Clone, Debug, PartialEq)] pub struct Base58Id([u8; N]); pub type UserId = Base58Id; pub type SessionId = Base58Id; impl Base58Id { pub fn new(id: [u8; N]) -> Result { id.iter().try_for_each(|b| { if !b.is_ascii_graphic() { Err(ParseError::IdNotPrintable) } else { Ok(()) } })?; Ok(Self(id)) } pub unsafe fn new_unchecked(id: [u8; N]) -> Self { Self(id) } pub fn as_str(&self) -> &str { // SAFTEY: the UserID is only ever expected to contain base58 characters. // All constructors are privated to the crate or do not allow setting the // data of the ID itself without checks str::from_utf8(&self.0).unwrap() } pub fn as_slice(&self) -> &[u8] { &self.0 } } impl Deref for Base58Id { type Target = [u8; N]; fn deref(&self) -> &Self::Target { &self.0 } } impl ToSql for Base58Id { fn to_sql(&self) -> rusqlite::Result> { self.as_str().to_sql() } } impl FromSql for Base58Id { fn column_result(value: ValueRef<'_>) -> FromSqlResult { match value { ValueRef::Text(data) => data.try_into().map_err(|pe| FromSqlError::from(pe)), _ => Err(FromSqlError::InvalidType), } } } impl FromStr for Base58Id { type Err = ParseError; fn from_str(s: &str) -> Result { s.as_bytes().try_into() } } impl TryFrom<&[u8]> for Base58Id { type Error = ParseError; fn try_from(bytes: &[u8]) -> Result { let tryinto: [u8; N] = bytes.try_into().map_err(|_| ParseError::IdIncorrectSize { expected: N, actual: bytes.len(), })?; Base58Id::new(tryinto).map_err(|pe| pe.into()) } } #[derive(Clone, Debug)] pub struct HashedPassword(String); impl HashedPassword { pub fn new(hash: String) -> Self { Self(hash) } pub fn to_string(&self) -> String { self.0.clone() } } pub trait AsUserId { fn userid(&self) -> &UserId; } #[derive(Clone, Debug, PartialEq)] pub struct AnonymousUser { id: UserId, } impl AnonymousUser { pub fn new(id: UserId) -> Self { Self { id } } } impl AsUserId for AnonymousUser { fn userid(&self) -> &UserId { &self.id } } #[derive(Clone, Debug)] pub struct User { id: UserId, name: String, } impl AsUserId for User { fn userid(&self) -> &UserId { &self.id } } impl User { pub fn new(id: UserId, name: String) -> Self { Self { id, name } } pub fn name(&self) -> &str { &self.name } } #[derive(Clone, Debug)] pub struct UserAuth { user: User, hash: HashedPassword, } impl UserAuth { pub fn new(user: User, hash: HashedPassword) -> Self { Self { user, hash } } pub fn hash(&self) -> &HashedPassword { &self.hash } pub fn user(&self) -> &User { &self.user } } impl Into for UserAuth { fn into(self) -> User { self.user } } #[derive(Clone, Debug)] pub struct Session { id: SessionId, user: User, } impl Session { pub fn new(id: SessionId, user: User) -> Self { Self { id, user } } pub fn sessionid(&self) -> &SessionId { &self.id } pub fn user(&self) -> &User { &self.user } } impl Into for Session { fn into(self) -> User { self.user } }