chore: refactored type enums

This commit is contained in:
əlemi 2024-03-19 01:00:44 +01:00
parent 0adeb667c4
commit 7d69f8148c
Signed by: alemi
GPG key ID: A4895B84D311642C
12 changed files with 439 additions and 192 deletions

View file

@ -2,10 +2,55 @@ pub trait Activity : super::Object {
fn activity_type(&self) -> Option<super::types::ActivityType> { None } fn activity_type(&self) -> Option<super::types::ActivityType> { None }
fn actor_id(&self) -> Option<&str> { None } fn actor_id(&self) -> Option<&str> { None }
fn actor(&self) -> Option<&impl super::Object> { None::<&()> } fn actor(&self) -> Option<super::LinkedObject<impl super::Object>> { None::<super::LinkedObject<()>> }
fn object_id(&self) -> Option<&str> { None } fn object_id(&self) -> Option<&str> { None }
fn object(&self) -> Option<&impl super::Object> { None::<&()> } fn object(&self) -> Option<super::LinkedObject<impl super::Object>> { None::<super::LinkedObject<()>> }
fn target(&self) -> Option<&str> { None } fn target(&self) -> Option<&str> { None }
} }
impl Activity for serde_json::Value {
fn activity_type(&self) -> Option<super::types::ActivityType> {
let serde_json::Value::String(t) = self.get("type")? else { return None };
super::types::ActivityType::try_from(t.as_str()).ok()
}
fn object(&self) -> Option<super::LinkedObject<impl super::Object>> {
let obj = self.get("object")?;
match obj {
serde_json::Value::Object(_) => Some(obj.clone().into()),
_ => None,
}
}
fn object_id(&self) -> Option<&str> {
match self.get("object")? {
serde_json::Value::Object(map) => match map.get("id")? {
serde_json::Value::String(id) => Some(id),
_ => None,
},
serde_json::Value::String(id) => Some(id),
_ => None,
}
}
fn actor(&self) -> Option<super::LinkedObject<impl super::Object>> {
let obj = self.get("actor")?;
match obj {
serde_json::Value::Object(_) => Some(obj.clone().into()),
_ => None,
}
}
fn actor_id(&self) -> Option<&str> {
match self.get("actor")? {
serde_json::Value::Object(map) => match map.get("id")? {
serde_json::Value::String(id) => Some(id),
_ => None,
},
serde_json::Value::String(id) => Some(id),
_ => None,
}
}
}

View file

@ -0,0 +1,3 @@
pub trait Actor : super::Object {
fn actor_type(&self) -> Option<super::ActorType> { None }
}

View file

@ -0,0 +1,66 @@
pub trait Link {
fn href(&self) -> &str;
fn rel(&self) -> Option<&str> { None }
fn media_type(&self) -> Option<&str> { None } // also in obj
fn name(&self) -> Option<&str> { None } // also in obj
fn hreflang(&self) -> Option<&str> { None }
fn height(&self) -> Option<&str> { None }
fn width(&self) -> Option<&str> { None }
fn preview(&self) -> Option<&str> { None } // also in obj
}
pub enum LinkedObject<T> {
Object(T),
Link(Box<dyn Link>),
}
impl<T> LinkedObject<T>
where
T : for<'de> serde::Deserialize<'de>,
{
pub async fn resolve(self) -> T {
match self {
LinkedObject::Object(o) => o,
LinkedObject::Link(l) =>
reqwest::get(l.href())
.await.unwrap()
.json::<T>()
.await.unwrap(),
}
}
}
impl Link for String {
fn href(&self) -> &str {
self
}
}
impl Link for serde_json::Value {
// TODO this is unchecked and can panic
fn href(&self) -> &str {
match self {
serde_json::Value::String(x) => x,
serde_json::Value::Object(map) =>
map.get("href")
.unwrap()
.as_str()
.unwrap(),
_ => panic!("invalid value for Link"),
}
}
// ... TODO!
}
impl From<serde_json::Value> for LinkedObject<serde_json::Value> {
fn from(value: serde_json::Value) -> Self {
if value.is_string() || value.get("href").is_some() {
Self::Link(Box::new(value))
} else {
Self::Object(value)
}
}
}

View file

@ -1,6 +1,9 @@
pub mod object; pub mod object;
pub use object::{Object, Link, ObjectOrLink}; pub use object::Object;
pub mod actor;
pub use actor::Actor;
pub mod activity; pub mod activity;
@ -8,4 +11,52 @@ pub use activity::Activity;
pub mod types; pub mod types;
pub use types::Type; pub use types::{BaseType, ObjectType, ActivityType, ActorType};
pub mod link;
pub use link::{Link, LinkedObject};
pub trait ToJson : Object {
fn json(&self) -> serde_json::Value;
}
impl<T> ToJson for T where T : Object {
fn json(&self) -> serde_json::Value {
let mut map = serde_json::Map::new();
let mp = &mut map;
put_str(mp, "id", self.id());
put_str(mp, "attributedTo", self.attributed_to());
put_str(mp, "name", self.name());
put_str(mp, "summary", self.summary());
put_str(mp, "content", self.content());
if let Some(t) = self.full_type() {
map.insert(
"type".to_string(),
serde_json::Value::String(format!("{t:?}")),
);
}
if let Some(published) = self.published() {
map.insert(
"published".to_string(),
serde_json::Value::String(published.to_rfc3339()),
);
}
// ... TODO!
serde_json::Value::Object(map)
}
}
fn put_str(map: &mut serde_json::Map<String, serde_json::Value>, k: &str, v: Option<&str>) {
if let Some(v) = v {
map.insert(
k.to_string(),
serde_json::Value::String(v.to_string()),
);
}
}

View file

@ -1,33 +1,6 @@
pub enum ObjectOrLink {
Object(Box<dyn Object>),
Link(Box<dyn Link>),
}
impl From<serde_json::Value> for ObjectOrLink {
fn from(value: serde_json::Value) -> Self {
if value.get("href").is_some() {
Self::Link(Box::new(value))
} else {
Self::Object(Box::new(value))
}
}
}
pub trait Link {
fn href(&self) -> Option<&str> { None }
fn rel(&self) -> Option<&str> { None }
fn media_type(&self) -> Option<&str> { None } // also in obj
fn name(&self) -> Option<&str> { None } // also in obj
fn hreflang(&self) -> Option<&str> { None }
fn height(&self) -> Option<&str> { None }
fn width(&self) -> Option<&str> { None }
fn preview(&self) -> Option<&str> { None } // also in obj
}
pub trait Object { pub trait Object {
fn id(&self) -> Option<&str> { None } fn id(&self) -> Option<&str> { None }
fn full_type(&self) -> Option<super::Type> { None } fn full_type(&self) -> Option<super::BaseType> { None }
fn attachment (&self) -> Option<&str> { None } fn attachment (&self) -> Option<&str> { None }
fn attributed_to (&self) -> Option<&str> { None } fn attributed_to (&self) -> Option<&str> { None }
fn audience (&self) -> Option<&str> { None } fn audience (&self) -> Option<&str> { None }
@ -66,61 +39,9 @@ impl Object for serde_json::Value {
self.get("id")?.as_str() self.get("id")?.as_str()
} }
fn full_type(&self) -> Option<super::Type> { fn full_type(&self) -> Option<super::BaseType> {
todo!() self.get("type")?.as_str()?.try_into().ok()
} }
// ... TODO! // ... TODO!
} }
impl Link for serde_json::Value {
fn href(&self) -> Option<&str> {
self.get("href")?.as_str()
}
// ... TODO!
}
pub trait ToJson : Object {
fn json(&self) -> serde_json::Value;
}
impl<T> ToJson for T where T : Object {
fn json(&self) -> serde_json::Value {
let mut map = serde_json::Map::new();
let mp = &mut map;
put_str(mp, "id", self.id());
put_str(mp, "attributedTo", self.attributed_to());
put_str(mp, "name", self.name());
put_str(mp, "summary", self.summary());
put_str(mp, "content", self.content());
if let Some(t) = self.full_type() {
map.insert(
"type".to_string(),
serde_json::Value::String(format!("{t}")),
);
}
if let Some(published) = self.published() {
map.insert(
"published".to_string(),
serde_json::Value::String(published.to_rfc3339()),
);
}
// ... TODO!
serde_json::Value::Object(map)
}
}
fn put_str(map: &mut serde_json::Map<String, serde_json::Value>, k: &str, v: Option<&str>) {
if let Some(v) = v {
map.insert(
k.to_string(),
serde_json::Value::String(v.to_string()),
);
}
}

View file

@ -1,88 +1,228 @@
// TODO merge these flat maybe? #[derive(Debug, thiserror::Error)]
// but then db could theoretically hold an actor with type "Like" ... idk! #[error("invalid type value")]
#[derive(Debug, Clone)] pub struct TypeValueError;
pub enum Type {
Object, impl From<TypeValueError> for sea_orm::sea_query::ValueTypeErr {
ObjectType(ObjectType), fn from(_: TypeValueError) -> Self {
Link, sea_orm::sea_query::ValueTypeErr
Mention, // TODO what about this??? }
Activity,
IntransitiveActivity,
ActivityType(ActivityType),
Collection,
OrderedCollection,
CollectionPage,
OrderedCollectionPage,
ActorType(ActorType),
} }
impl std::fmt::Display for Type { impl From<TypeValueError> for sea_orm::TryGetError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn from(_: TypeValueError) -> Self {
sea_orm::TryGetError::Null("value is not a valid type".into())
}
}
macro_rules! strenum {
( $(pub enum $enum_name:ident { $($flat:ident),+ $($deep:ident($inner:ident)),*};)+ ) => {
$(
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum $enum_name {
$($flat,)*
$($deep($inner),)*
}
impl AsRef<str> for $enum_name {
fn as_ref(&self) -> &str {
match self { match self {
Self::ObjectType(x) => write!(f, "{:?}", x), $(Self::$flat => stringify!($flat),)*
Self::ActivityType(x) => write!(f, "{:?}", x), $(Self::$deep(x) => x.as_ref(),)*
Self::ActorType(x) => write!(f, "{:?}", x),
_ => write!(f, "{:?}", self),
} }
} }
} }
#[derive(sea_orm::EnumIter, sea_orm::DeriveActiveEnum, PartialEq, Eq, Debug, Clone, Copy)] impl TryFrom<&str> for $enum_name {
#[sea_orm(rs_type = "i32", db_type = "Integer")] type Error = TypeValueError;
pub enum ActivityType {
Accept = 1, fn try_from(value:&str) -> Result<Self, Self::Error> {
Add = 2, match value {
Announce = 3, $(stringify!($flat) => Ok(Self::$flat),)*
Arrive = 4, _ => {
Block = 5, $(
Create = 6, if let Ok(x) = $inner::try_from(value) {
Delete = 7, return Ok(Self::$deep(x));
Dislike = 8, }
Flag = 9, )*
Follow = 10, Err(TypeValueError)
Ignore = 11, },
Invite = 12, }
Join = 13, }
Leave = 14,
Like = 15,
Listen = 16,
Move = 17,
Offer = 18,
Question = 19,
Reject = 20,
Read = 21,
Remove = 22,
TentativeReject = 23,
TentativeAccept = 24,
Travel = 25,
Undo = 26,
Update = 27,
View = 28,
} }
#[derive(sea_orm::EnumIter, sea_orm::DeriveActiveEnum, PartialEq, Eq, Debug, Clone, Copy)] impl From<$enum_name> for sea_orm::Value {
#[sea_orm(rs_type = "i32", db_type = "Integer")] fn from(value: $enum_name) -> sea_orm::Value {
pub enum ActorType { sea_orm::Value::String(Some(Box::new(value.as_ref().to_string())))
Application = 1, }
Group = 2,
Organization = 3,
Person = 4,
Service = 5,
} }
#[derive(sea_orm::EnumIter, sea_orm::DeriveActiveEnum, PartialEq, Eq, Debug, Clone, Copy)] impl sea_orm::sea_query::ValueType for $enum_name {
#[sea_orm(rs_type = "i32", db_type = "Integer")] fn try_from(v: sea_orm::Value) -> Result<Self, sea_orm::sea_query::ValueTypeErr> {
match v {
sea_orm::Value::String(Some(x)) =>
Ok(<Self as TryFrom<&str>>::try_from(x.as_str())?),
_ => Err(sea_orm::sea_query::ValueTypeErr),
}
}
fn type_name() -> String {
stringify!($enum_name).to_string()
}
fn array_type() -> sea_orm::sea_query::ArrayType {
sea_orm::sea_query::ArrayType::String
}
fn column_type() -> sea_orm::sea_query::ColumnType {
sea_orm::sea_query::ColumnType::String(Some(24))
}
}
impl sea_orm::TryGetable for $enum_name {
fn try_get_by<I: sea_orm::ColIdx>(res: &sea_orm::prelude::QueryResult, index: I) -> Result<Self, sea_orm::TryGetError> {
let x : String = res.try_get_by(index)?;
Ok(Self::try_from(x.as_str())?)
}
}
)*
};
}
strenum! {
pub enum BaseType {
Invalid
Object(ObjectType),
Link(LinkType)
};
pub enum LinkType {
Base,
Mention
};
pub enum ObjectType { pub enum ObjectType {
Article = 1, Object,
Audio = 2, Relationship,
Document = 3, Tombstone
Event = 4,
Image = 5, Activity(ActivityType),
Note = 6, Actor(ActorType),
Page = 7, Collection(CollectionType),
Place = 8, Status(StatusType)
Profile = 9, };
Relationship = 10,
Tombstone = 11, pub enum ActorType {
Video = 12, Application,
Group,
Organization,
Person,
Object
};
pub enum StatusType {
Article,
Event,
Note,
Place,
Profile
Document(DocumentType)
};
pub enum CollectionType {
Collection,
CollectionPage,
OrderedCollection,
OrderedCollectionPage
};
pub enum AcceptType {
Accept,
TentativeAccept
};
pub enum DocumentType {
Document,
Audio,
Image,
Page,
Video
};
pub enum ActivityType {
Activity,
Add,
Announce,
Create,
Delete,
Dislike,
Flag,
Follow,
Join,
Leave,
Like,
Listen,
Move,
Read,
Remove,
Undo,
Update,
View
IntransitiveActivity(IntransitiveActivityType),
Accept(AcceptType),
Ignore(IgnoreType),
Offer(OfferType),
Reject(RejectType)
};
pub enum IntransitiveActivityType {
IntransitiveActivity,
Arrive,
Question,
Travel
};
pub enum IgnoreType {
Ignore,
Block
};
pub enum OfferType {
Offer,
Invite
};
pub enum RejectType {
Reject,
TentativeReject
};
}
#[cfg(test)]
mod test {
#[test]
fn assert_flat_types_serialize() {
let x = super::IgnoreType::Block;
assert_eq!("Block", <super::IgnoreType as AsRef<str>>::as_ref(&x));
}
#[test]
fn assert_deep_types_serialize() {
let x = super::StatusType::Document(super::DocumentType::Page);
assert_eq!("Page", <super::StatusType as AsRef<str>>::as_ref(&x));
}
#[test]
fn assert_flat_types_deserialize() {
let x = super::ActorType::try_from("Person").expect("could not deserialize");
assert_eq!(super::ActorType::Person, x);
}
#[test]
fn assert_deep_types_deserialize() {
let x = super::ActivityType::try_from("Invite").expect("could not deserialize");
assert_eq!(super::ActivityType::Offer(super::OfferType::Invite), x);
}
} }

View file

@ -17,7 +17,7 @@ impl MigrationTrait for Migration {
.not_null() .not_null()
.primary_key() .primary_key()
) )
.col(ColumnDef::new(Users::ActorType).integer().not_null()) .col(ColumnDef::new(Users::ActorType).string().not_null())
.col(ColumnDef::new(Users::Name).string().not_null()) .col(ColumnDef::new(Users::Name).string().not_null())
.to_owned() .to_owned()
) )
@ -34,7 +34,7 @@ impl MigrationTrait for Migration {
.not_null() .not_null()
.primary_key() .primary_key()
) )
.col(ColumnDef::new(Activities::ActivityType).integer().not_null()) .col(ColumnDef::new(Activities::ActivityType).string().not_null())
.col(ColumnDef::new(Activities::Actor).string().not_null()) .col(ColumnDef::new(Activities::Actor).string().not_null())
.col(ColumnDef::new(Activities::Object).string().null()) .col(ColumnDef::new(Activities::Object).string().null())
.col(ColumnDef::new(Activities::Target).string().null()) .col(ColumnDef::new(Activities::Target).string().null())
@ -53,7 +53,7 @@ impl MigrationTrait for Migration {
.not_null() .not_null()
.primary_key() .primary_key()
) )
.col(ColumnDef::new(Objects::ObjectType).integer().not_null()) .col(ColumnDef::new(Objects::ObjectType).string().not_null())
.col(ColumnDef::new(Objects::AttributedTo).string().null()) .col(ColumnDef::new(Objects::AttributedTo).string().null())
.col(ColumnDef::new(Objects::Name).string().null()) .col(ColumnDef::new(Objects::Name).string().null())
.col(ColumnDef::new(Objects::Summary).string().null()) .col(ColumnDef::new(Objects::Summary).string().null())

View file

@ -1,6 +1,6 @@
use sea_orm::entity::prelude::*; use sea_orm::entity::prelude::*;
use crate::activitystream; use crate::activitystream::{self, ObjectType, BaseType};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "activities")] #[sea_orm(table_name = "activities")]
@ -28,8 +28,8 @@ impl activitystream::Object for Model {
Some(&self.id) Some(&self.id)
} }
fn full_type(&self) -> Option<activitystream::Type> { fn full_type(&self) -> Option<activitystream::BaseType> {
Some(activitystream::Type::ActivityType(self.activity_type)) Some(BaseType::Object(ObjectType::Activity(self.activity_type)))
} }
fn published(&self) -> Option<chrono::DateTime<chrono::Utc>> { fn published(&self) -> Option<chrono::DateTime<chrono::Utc>> {

View file

@ -4,7 +4,7 @@ pub mod activity;
#[derive(Debug, Clone, thiserror::Error)] #[derive(Debug, Clone, thiserror::Error)]
#[error("missing required field: '{0}'")] #[error("missing required field: '{0}'")]
pub struct FieldError(&'static str); pub struct FieldError(pub &'static str);
pub async fn faker(db: &sea_orm::DatabaseConnection) -> Result<(), sea_orm::DbErr> { pub async fn faker(db: &sea_orm::DatabaseConnection) -> Result<(), sea_orm::DbErr> {
use sea_orm::EntityTrait; use sea_orm::EntityTrait;
@ -18,7 +18,7 @@ pub async fn faker(db: &sea_orm::DatabaseConnection) -> Result<(), sea_orm::DbEr
object::Entity::insert(object::ActiveModel { object::Entity::insert(object::ActiveModel {
id: sea_orm::Set("http://localhost:3000/objects/4e28d30b-33c1-4336-918b-6fbe592bdd44".into()), id: sea_orm::Set("http://localhost:3000/objects/4e28d30b-33c1-4336-918b-6fbe592bdd44".into()),
name: sea_orm::Set(None), name: sea_orm::Set(None),
object_type: sea_orm::Set(crate::activitystream::types::ObjectType::Note), object_type: sea_orm::Set(crate::activitystream::types::StatusType::Note),
attributed_to: sea_orm::Set(Some("http://localhost:3000/users/root".into())), attributed_to: sea_orm::Set(Some("http://localhost:3000/users/root".into())),
summary: sea_orm::Set(None), summary: sea_orm::Set(None),
content: sea_orm::Set(Some("Hello world!".into())), content: sea_orm::Set(Some("Hello world!".into())),

View file

@ -1,6 +1,6 @@
use sea_orm::entity::prelude::*; use sea_orm::entity::prelude::*;
use crate::activitystream::{self, types::ObjectType}; use crate::activitystream::{Object, types::{BaseType, ObjectType, StatusType}};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "objects")] #[sea_orm(table_name = "objects")]
@ -8,7 +8,7 @@ pub struct Model {
#[sea_orm(primary_key)] #[sea_orm(primary_key)]
/// must be full uri!!! maybe not great? /// must be full uri!!! maybe not great?
pub id: String, pub id: String,
pub object_type: ObjectType, pub object_type: StatusType,
pub attributed_to: Option<String>, pub attributed_to: Option<String>,
pub name: Option<String>, pub name: Option<String>,
pub summary: Option<String>, pub summary: Option<String>,
@ -26,8 +26,8 @@ impl crate::activitystream::Object for Model {
Some(&self.id) Some(&self.id)
} }
fn full_type(&self) -> Option<crate::activitystream::Type> { fn full_type(&self) -> Option<crate::activitystream::BaseType> {
Some(crate::activitystream::Type::ObjectType(self.object_type)) Some(BaseType::Object(ObjectType::Status(self.object_type)))
} }
fn attributed_to (&self) -> Option<&str> { fn attributed_to (&self) -> Option<&str> {
@ -52,8 +52,8 @@ impl crate::activitystream::Object for Model {
} }
impl Model { impl Model {
pub fn new(object: &impl activitystream::Object) -> Result<Self, super::FieldError> { pub fn new(object: &impl Object) -> Result<Self, super::FieldError> {
let Some(activitystream::Type::ObjectType(t)) = object.full_type() else { let Some(BaseType::Object(ObjectType::Status(t))) = object.full_type() else {
return Err(super::FieldError("type")); // TODO maybe just wrong? better errors! return Err(super::FieldError("type")); // TODO maybe just wrong? better errors!
}; };
Ok(Model { Ok(Model {

View file

@ -24,8 +24,8 @@ impl activitystream::Object for Model {
Some(&self.id) Some(&self.id)
} }
fn full_type(&self) -> Option<activitystream::Type> { fn full_type(&self) -> Option<activitystream::BaseType> {
Some(activitystream::Type::ActorType(self.actor_type)) Some(activitystream::BaseType::Object(activitystream::ObjectType::Actor(self.actor_type)))
} }
fn name (&self) -> Option<&str> { fn name (&self) -> Option<&str> {
@ -35,7 +35,7 @@ impl activitystream::Object for Model {
impl Model { impl Model {
pub fn new(object: &impl activitystream::Object) -> Result<Self, super::FieldError> { pub fn new(object: &impl activitystream::Object) -> Result<Self, super::FieldError> {
let Some(activitystream::Type::ActorType(t)) = object.full_type() else { let Some(activitystream::BaseType::Object(activitystream::ObjectType::Actor(t))) = object.full_type() else {
return Err(super::FieldError("type")); // TODO maybe just wrong? better errors! return Err(super::FieldError("type")); // TODO maybe just wrong? better errors!
}; };
Ok(Model { Ok(Model {

View file

@ -1,11 +1,13 @@
use std::ops::Deref; use std::ops::Deref;
use std::sync::Arc; use std::sync::Arc;
use crate::activitystream::object::ToJson; use crate::activitystream::ObjectType;
use crate::activitystream::{types::ActivityType, Object, Type}; use crate::activitystream::ToJson;
use crate::activitystream::Activity;
use crate::activitystream::{types::ActivityType, Object, BaseType, LinkedObject};
use crate::model::{activity, object, user}; use crate::model::{activity, object, user};
use axum::{extract::{Path, State}, http::StatusCode, routing::{get, post}, Json, Router}; use axum::{extract::{Path, State}, http::StatusCode, routing::{get, post}, Json, Router};
use sea_orm::{DatabaseConnection, EntityTrait}; use sea_orm::{DatabaseConnection, EntityTrait, IntoActiveModel};
pub async fn serve(db: DatabaseConnection) { pub async fn serve(db: DatabaseConnection) {
// build our application with a single route // build our application with a single route
@ -25,14 +27,33 @@ pub async fn serve(db: DatabaseConnection) {
.unwrap(); .unwrap();
} }
async fn inbox(State(_db) : State<Arc<DatabaseConnection>>, Json(object): Json<serde_json::Value>) -> Result<Json<serde_json::Value>, StatusCode> { async fn inbox(State(db) : State<Arc<DatabaseConnection>>, Json(object): Json<serde_json::Value>) -> Result<Json<serde_json::Value>, StatusCode> {
match object.full_type() { match object.full_type() {
None => { Err(StatusCode::BAD_REQUEST) }, None => { Err(StatusCode::BAD_REQUEST) },
Some(Type::Activity) => { Err(StatusCode::UNPROCESSABLE_ENTITY) }, Some(BaseType::Link(_x)) => Err(StatusCode::UNPROCESSABLE_ENTITY), // we could but not yet
Some(Type::ActivityType(ActivityType::Follow)) => { todo!() }, Some(BaseType::Object(ObjectType::Activity(ActivityType::Activity))) => Err(StatusCode::UNPROCESSABLE_ENTITY),
Some(Type::ActivityType(ActivityType::Create)) => { todo!() }, Some(BaseType::Object(ObjectType::Activity(ActivityType::Follow))) => { todo!() },
Some(Type::ActivityType(ActivityType::Like)) => { todo!() }, Some(BaseType::Object(ObjectType::Activity(ActivityType::Like))) => { todo!() },
Some(Type::ActivityType(_x)) => { Err(StatusCode::NOT_IMPLEMENTED) }, Some(BaseType::Object(ObjectType::Activity(ActivityType::Create))) => {
let Ok(activity_entity) = activity::Model::new(&object) else {
return Err(StatusCode::UNPROCESSABLE_ENTITY);
};
let Some(LinkedObject::Object(obj)) = object.object() else {
// TODO we could process non-embedded activities but im lazy rn
return Err(StatusCode::UNPROCESSABLE_ENTITY);
};
let Ok(obj_entity) = object::Model::new(&obj) else {
return Err(StatusCode::UNPROCESSABLE_ENTITY);
};
object::Entity::insert(obj_entity.into_active_model())
.exec(db.deref())
.await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
activity::Entity::insert(activity_entity.into_active_model())
.exec(db.deref())
.await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(Json(serde_json::Value::Null)) // TODO hmmmmmmmmmmm not the best value to return....
},
Some(BaseType::Object(ObjectType::Activity(_x))) => { Err(StatusCode::NOT_IMPLEMENTED) },
Some(_x) => { Err(StatusCode::UNPROCESSABLE_ENTITY) } Some(_x) => { Err(StatusCode::UNPROCESSABLE_ENTITY) }
} }
} }