server: move domain types to a separate file

This commit is contained in:
Valentin Tolmer
2022-11-25 14:27:09 +01:00
committed by nitnelave
parent e89b1538af
commit 09a0522e2d
26 changed files with 492 additions and 494 deletions

View File

@@ -1,223 +1,13 @@
use super::error::*;
use super::{
error::Result,
types::{
Group, GroupDetails, GroupId, JpegPhoto, User, UserAndGroups, UserColumn, UserId, Uuid,
},
};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
pub use super::model::{GroupColumn, UserColumn};
#[derive(PartialEq, Hash, Eq, Clone, Debug, Default, Serialize, Deserialize)]
#[serde(try_from = "&str")]
pub struct Uuid(String);
impl Uuid {
pub fn from_name_and_date(name: &str, creation_date: &chrono::DateTime<chrono::Utc>) -> Self {
Uuid(
uuid::Uuid::new_v3(
&uuid::Uuid::NAMESPACE_X500,
&[name.as_bytes(), creation_date.to_rfc3339().as_bytes()].concat(),
)
.to_string(),
)
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn into_string(self) -> String {
self.0
}
}
impl<'a> std::convert::TryFrom<&'a str> for Uuid {
type Error = anyhow::Error;
fn try_from(s: &'a str) -> anyhow::Result<Self> {
Ok(Uuid(uuid::Uuid::parse_str(s)?.to_string()))
}
}
impl std::string::ToString for Uuid {
fn to_string(&self) -> String {
self.0.clone()
}
}
impl sea_orm::TryGetable for Uuid {
fn try_get(
res: &sea_orm::QueryResult,
pre: &str,
col: &str,
) -> std::result::Result<Self, sea_orm::TryGetError> {
Ok(Uuid(String::try_get(res, pre, col)?))
}
}
#[cfg(test)]
#[macro_export]
macro_rules! uuid {
($s:literal) => {
<$crate::domain::handler::Uuid as std::convert::TryFrom<_>>::try_from($s).unwrap()
};
}
#[derive(PartialEq, Eq, Clone, Debug, Default, Serialize, Deserialize)]
#[serde(from = "String")]
pub struct UserId(String);
impl UserId {
pub fn new(user_id: &str) -> Self {
Self(user_id.to_lowercase())
}
pub fn as_str(&self) -> &str {
self.0.as_str()
}
pub fn into_string(self) -> String {
self.0
}
}
impl std::fmt::Display for UserId {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<String> for UserId {
fn from(s: String) -> Self {
Self::new(&s)
}
}
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
pub struct JpegPhoto(#[serde(with = "serde_bytes")] Vec<u8>);
impl JpegPhoto {
pub fn null() -> Self {
Self(vec![])
}
}
impl From<JpegPhoto> for sea_orm::Value {
fn from(photo: JpegPhoto) -> Self {
photo.0.into()
}
}
impl From<&JpegPhoto> for sea_orm::Value {
fn from(photo: &JpegPhoto) -> Self {
photo.0.as_slice().into()
}
}
impl TryFrom<&[u8]> for JpegPhoto {
type Error = anyhow::Error;
fn try_from(bytes: &[u8]) -> anyhow::Result<Self> {
if bytes.is_empty() {
return Ok(JpegPhoto::null());
}
// Confirm that it's a valid Jpeg, then store only the bytes.
image::io::Reader::with_format(std::io::Cursor::new(bytes), image::ImageFormat::Jpeg)
.decode()?;
Ok(JpegPhoto(bytes.to_vec()))
}
}
impl TryFrom<Vec<u8>> for JpegPhoto {
type Error = anyhow::Error;
fn try_from(bytes: Vec<u8>) -> anyhow::Result<Self> {
if bytes.is_empty() {
return Ok(JpegPhoto::null());
}
// Confirm that it's a valid Jpeg, then store only the bytes.
image::io::Reader::with_format(
std::io::Cursor::new(bytes.as_slice()),
image::ImageFormat::Jpeg,
)
.decode()?;
Ok(JpegPhoto(bytes))
}
}
impl TryFrom<String> for JpegPhoto {
type Error = anyhow::Error;
fn try_from(string: String) -> anyhow::Result<Self> {
// The String format is in base64.
Self::try_from(base64::decode(string.as_str())?)
}
}
impl From<&JpegPhoto> for String {
fn from(val: &JpegPhoto) -> Self {
base64::encode(&val.0)
}
}
impl JpegPhoto {
pub fn into_bytes(self) -> Vec<u8> {
self.0
}
#[cfg(test)]
pub fn for_tests() -> Self {
use image::{ImageOutputFormat, Rgb, RgbImage};
let img = RgbImage::from_fn(32, 32, |x, y| {
if (x + y) % 2 == 0 {
Rgb([0, 0, 0])
} else {
Rgb([255, 255, 255])
}
});
let mut bytes: Vec<u8> = Vec::new();
img.write_to(
&mut std::io::Cursor::new(&mut bytes),
ImageOutputFormat::Jpeg(0),
)
.unwrap();
Self(bytes)
}
}
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize, sea_orm::FromQueryResult)]
pub struct User {
pub user_id: UserId,
pub email: String,
pub display_name: Option<String>,
pub first_name: Option<String>,
pub last_name: Option<String>,
pub avatar: Option<JpegPhoto>,
pub creation_date: chrono::DateTime<chrono::Utc>,
pub uuid: Uuid,
}
#[cfg(test)]
impl Default for User {
fn default() -> Self {
use chrono::TimeZone;
let epoch = chrono::Utc.timestamp_opt(0, 0).unwrap();
User {
user_id: UserId::default(),
email: String::new(),
display_name: None,
first_name: None,
last_name: None,
avatar: None,
creation_date: epoch,
uuid: Uuid::from_name_and_date("", &epoch),
}
}
}
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
pub struct Group {
pub id: GroupId,
pub display_name: String,
pub creation_date: chrono::DateTime<chrono::Utc>,
pub uuid: Uuid,
pub users: Vec<UserId>,
}
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
pub struct BindRequest {
pub name: UserId,
@@ -282,23 +72,6 @@ pub trait LoginHandler: Clone + Send {
async fn bind(&self, request: BindRequest) -> Result<()>;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct GroupId(pub i32);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sea_orm::FromQueryResult)]
pub struct GroupDetails {
pub group_id: GroupId,
pub display_name: String,
pub creation_date: chrono::DateTime<chrono::Utc>,
pub uuid: Uuid,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UserAndGroups {
pub user: User,
pub groups: Option<Vec<GroupDetails>>,
}
#[async_trait]
pub trait GroupBackendHandler {
async fn list_groups(&self, filters: Option<GroupRequestFilter>) -> Result<Vec<Group>>;