use crate::{DatabaseAccess, DatabaseRecord, Error, Record, Validate};
use serde::{Deserialize, Serialize};
use std::ops::{Deref, DerefMut};
#[derive(Serialize, Deserialize, Clone)]
pub struct EdgeRecord<T> {
#[serde(rename(serialize = "_from", deserialize = "_from"))]
from: String,
#[serde(rename(serialize = "_to", deserialize = "_to"))]
to: String,
#[serde(flatten)]
pub data: T,
}
impl<T: Record> EdgeRecord<T> {
pub fn new(id_from: String, id_to: String, data: T) -> Result<Self, Error> {
let res = Self {
from: id_from,
to: id_to,
data,
};
res.validate()?;
Ok(res)
}
#[maybe_async::maybe_async]
pub async fn from_record<D, R>(&self, db_access: &D) -> Result<DatabaseRecord<R>, Error>
where
D: DatabaseAccess + ?Sized,
R: Record,
{
DatabaseRecord::find(self.key_from(), db_access).await
}
#[maybe_async::maybe_async]
pub async fn to_record<D, R>(&self, db_access: &D) -> Result<DatabaseRecord<R>, Error>
where
D: DatabaseAccess + ?Sized,
R: Record,
{
DatabaseRecord::find(self.key_to(), db_access).await
}
#[allow(clippy::missing_const_for_fn)] #[must_use]
#[inline]
pub fn id_from(&self) -> &String {
&self.from
}
#[allow(clippy::missing_const_for_fn)] #[must_use]
#[inline]
pub fn id_to(&self) -> &String {
&self.to
}
#[must_use]
pub fn key_from(&self) -> &str {
self.id_from().split('/').last().unwrap()
}
#[must_use]
pub fn key_to(&self) -> &str {
self.id_to().split('/').last().unwrap()
}
#[must_use]
pub fn to_collection_name(&self) -> String {
self.id_to().split('/').next().unwrap().to_string()
}
#[must_use]
pub fn from_collection_name(&self) -> &str {
self.id_from().split('/').next().unwrap()
}
fn validate_edge_fields(&self, errors: &mut Vec<String>) {
let array = [("from", self.id_from()), ("to", self.id_to())];
for (name, field) in array {
let vec: Vec<&str> = field.split('/').collect();
let [left, right]: [_; 2] = if let Ok(v) = vec.try_into() {
v
} else {
errors.push(format!(r#"{} "{}" is not a valid id"#, name, field));
continue;
};
Self::validate_min_len(name, left, 2, errors);
Self::validate_min_len(name, right, 2, errors);
}
}
}
impl<T: Record> Validate for EdgeRecord<T> {
fn validations(&self, errors: &mut Vec<String>) {
self.validate_edge_fields(errors);
}
}
#[maybe_async::maybe_async]
impl<T: Record + Send> Record for EdgeRecord<T> {
const COLLECTION_NAME: &'static str = T::COLLECTION_NAME;
async fn before_create_hook<D>(&mut self, db_accessor: &D) -> Result<(), Error>
where
D: DatabaseAccess + ?Sized,
{
self.validate()?;
self.data.before_create_hook(db_accessor).await
}
async fn before_save_hook<D>(&mut self, db_accessor: &D) -> Result<(), Error>
where
D: DatabaseAccess + ?Sized,
{
self.data.before_save_hook(db_accessor).await
}
async fn before_delete_hook<D>(&mut self, db_accessor: &D) -> Result<(), Error>
where
D: DatabaseAccess + ?Sized,
{
self.data.before_delete_hook(db_accessor).await
}
async fn after_create_hook<D>(&mut self, db_accessor: &D) -> Result<(), Error>
where
D: DatabaseAccess + ?Sized,
{
self.data.after_create_hook(db_accessor).await
}
async fn after_save_hook<D>(&mut self, db_accessor: &D) -> Result<(), Error>
where
D: DatabaseAccess + ?Sized,
{
self.validate()?;
self.data.after_save_hook(db_accessor).await
}
async fn after_delete_hook<D>(&mut self, db_accessor: &D) -> Result<(), Error>
where
D: DatabaseAccess + ?Sized,
{
self.data.after_delete_hook(db_accessor).await
}
}
impl<T: Record> Deref for EdgeRecord<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<T: Record> DerefMut for EdgeRecord<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}