forked from alemi/upub
chore: refactored type enums
This commit is contained in:
parent
0adeb667c4
commit
7d69f8148c
12 changed files with 439 additions and 192 deletions
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub trait Actor : super::Object {
|
||||||
|
fn actor_type(&self) -> Option<super::ActorType> { None }
|
||||||
|
}
|
66
src/activitystream/link.rs
Normal file
66
src/activitystream/link.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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,
|
|
||||||
ObjectType(ObjectType),
|
|
||||||
Link,
|
|
||||||
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::sea_query::ValueTypeErr {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn from(_: TypeValueError) -> Self {
|
||||||
match self {
|
sea_orm::sea_query::ValueTypeErr
|
||||||
Self::ObjectType(x) => write!(f, "{:?}", x),
|
|
||||||
Self::ActivityType(x) => write!(f, "{:?}", x),
|
|
||||||
Self::ActorType(x) => write!(f, "{:?}", x),
|
|
||||||
_ => write!(f, "{:?}", self),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(sea_orm::EnumIter, sea_orm::DeriveActiveEnum, PartialEq, Eq, Debug, Clone, Copy)]
|
impl From<TypeValueError> for sea_orm::TryGetError {
|
||||||
#[sea_orm(rs_type = "i32", db_type = "Integer")]
|
fn from(_: TypeValueError) -> Self {
|
||||||
pub enum ActivityType {
|
sea_orm::TryGetError::Null("value is not a valid type".into())
|
||||||
Accept = 1,
|
}
|
||||||
Add = 2,
|
|
||||||
Announce = 3,
|
|
||||||
Arrive = 4,
|
|
||||||
Block = 5,
|
|
||||||
Create = 6,
|
|
||||||
Delete = 7,
|
|
||||||
Dislike = 8,
|
|
||||||
Flag = 9,
|
|
||||||
Follow = 10,
|
|
||||||
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)]
|
macro_rules! strenum {
|
||||||
#[sea_orm(rs_type = "i32", db_type = "Integer")]
|
( $(pub enum $enum_name:ident { $($flat:ident),+ $($deep:ident($inner:ident)),*};)+ ) => {
|
||||||
pub enum ActorType {
|
$(
|
||||||
Application = 1,
|
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||||
Group = 2,
|
pub enum $enum_name {
|
||||||
Organization = 3,
|
$($flat,)*
|
||||||
Person = 4,
|
$($deep($inner),)*
|
||||||
Service = 5,
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for $enum_name {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
$(Self::$flat => stringify!($flat),)*
|
||||||
|
$(Self::$deep(x) => x.as_ref(),)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for $enum_name {
|
||||||
|
type Error = TypeValueError;
|
||||||
|
|
||||||
|
fn try_from(value:&str) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
$(stringify!($flat) => Ok(Self::$flat),)*
|
||||||
|
_ => {
|
||||||
|
$(
|
||||||
|
if let Ok(x) = $inner::try_from(value) {
|
||||||
|
return Ok(Self::$deep(x));
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
Err(TypeValueError)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<$enum_name> for sea_orm::Value {
|
||||||
|
fn from(value: $enum_name) -> sea_orm::Value {
|
||||||
|
sea_orm::Value::String(Some(Box::new(value.as_ref().to_string())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl sea_orm::sea_query::ValueType for $enum_name {
|
||||||
|
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())?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(sea_orm::EnumIter, sea_orm::DeriveActiveEnum, PartialEq, Eq, Debug, Clone, Copy)]
|
strenum! {
|
||||||
#[sea_orm(rs_type = "i32", db_type = "Integer")]
|
pub enum BaseType {
|
||||||
pub enum ObjectType {
|
Invalid
|
||||||
Article = 1,
|
|
||||||
Audio = 2,
|
Object(ObjectType),
|
||||||
Document = 3,
|
Link(LinkType)
|
||||||
Event = 4,
|
};
|
||||||
Image = 5,
|
|
||||||
Note = 6,
|
pub enum LinkType {
|
||||||
Page = 7,
|
Base,
|
||||||
Place = 8,
|
Mention
|
||||||
Profile = 9,
|
};
|
||||||
Relationship = 10,
|
|
||||||
Tombstone = 11,
|
pub enum ObjectType {
|
||||||
Video = 12,
|
Object,
|
||||||
|
Relationship,
|
||||||
|
Tombstone
|
||||||
|
|
||||||
|
Activity(ActivityType),
|
||||||
|
Actor(ActorType),
|
||||||
|
Collection(CollectionType),
|
||||||
|
Status(StatusType)
|
||||||
|
};
|
||||||
|
|
||||||
|
pub enum ActorType {
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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>> {
|
||||||
|
|
|
@ -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())),
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue