Simplified data transformation
This commit is contained in:
parent
e045d2aae4
commit
85ee56e21b
|
@ -1405,6 +1405,7 @@ dependencies = [
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1490,6 +1491,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"ts-rs",
|
"ts-rs",
|
||||||
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -55,6 +55,7 @@ tower-http = "0.4"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = "0.3"
|
||||||
ts-rs = "6"
|
ts-rs = "6"
|
||||||
|
unicode-segmentation = "1.10"
|
||||||
url = "2.3"
|
url = "2.3"
|
||||||
walkdir = "2.3"
|
walkdir = "2.3"
|
||||||
wasm-bindgen = "0.2"
|
wasm-bindgen = "0.2"
|
||||||
|
@ -95,5 +96,7 @@ serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
toml = { workspace = true }
|
toml = { workspace = true }
|
||||||
|
|
||||||
|
unicode-segmentation = { workspace = true }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
|
@ -12,4 +12,6 @@ http = { workspace = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|
||||||
ts-rs = { workspace = true, features = ["chrono", "chrono-impl"] }
|
ts-rs = { workspace = true, features = ["chrono", "chrono-impl"] }
|
||||||
|
|
||||||
|
unicode-segmentation = { workspace = true }
|
|
@ -22,7 +22,7 @@ fn default_timeline_limit<const MIN: u64, const MAX: u64>() -> U64Range<MIN, MAX
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, TS)]
|
#[derive(Serialize, Deserialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct GetTimelineRes(pub Vec<PackNoteFull>);
|
pub struct GetTimelineRes(pub Vec<PackNoteFull>);
|
||||||
|
|
||||||
#[derive(Endpoint)]
|
#[derive(Endpoint)]
|
||||||
|
|
|
@ -9,7 +9,7 @@ use magnetar_sdk_macros::pack;
|
||||||
|
|
||||||
use crate::types::Id;
|
use crate::types::Id;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Default, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct ImageMeta {
|
pub struct ImageMeta {
|
||||||
pub width: Option<u64>,
|
pub width: Option<u64>,
|
||||||
|
@ -49,9 +49,9 @@ pub struct DriveFileBase {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
pub sha256: String,
|
pub hash: Option<String>,
|
||||||
pub mime_type: String,
|
pub mime_type: String,
|
||||||
pub image_meta: ImageMeta,
|
pub media_metadata: ImageMeta,
|
||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
pub thumbnail_url: Option<String>,
|
pub thumbnail_url: Option<String>,
|
||||||
pub sensitive: bool,
|
pub sensitive: bool,
|
||||||
|
|
|
@ -23,6 +23,12 @@ pub struct Id {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&str> for Id {
|
||||||
|
fn from(id: &str) -> Self {
|
||||||
|
Self { id: id.to_string() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Copy, Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub enum NotificationType {
|
pub enum NotificationType {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::types::emoji::EmojiContext;
|
use crate::types::emoji::EmojiContext;
|
||||||
use crate::types::user::{PackUserBase, UserBase};
|
use crate::types::user::PackUserBase;
|
||||||
use crate::types::Id;
|
use crate::types::Id;
|
||||||
use crate::{Packed, Required};
|
use crate::{Packed, Required};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
@ -56,7 +56,7 @@ pub struct NoteBase {
|
||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
pub text: String,
|
pub text: String,
|
||||||
pub visibility: NoteVisibility,
|
pub visibility: NoteVisibility,
|
||||||
pub user: Box<UserBase>,
|
pub user: Box<PackUserBase>,
|
||||||
pub parent_note_id: Option<String>,
|
pub parent_note_id: Option<String>,
|
||||||
pub renoted_note_id: Option<String>,
|
pub renoted_note_id: Option<String>,
|
||||||
pub reply_count: u64,
|
pub reply_count: u64,
|
||||||
|
@ -65,6 +65,8 @@ pub struct NoteBase {
|
||||||
pub reactions: Vec<PackReactionBase>,
|
pub reactions: Vec<PackReactionBase>,
|
||||||
pub emojis: EmojiContext,
|
pub emojis: EmojiContext,
|
||||||
pub local_only: bool,
|
pub local_only: bool,
|
||||||
|
pub has_poll: bool,
|
||||||
|
pub file_ids: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(PackNoteBase, Required<Id> as id & Required<NoteBase> as note);
|
pack!(PackNoteBase, Required<Id> as id & Required<NoteBase> as note);
|
||||||
|
@ -73,7 +75,6 @@ pack!(PackNoteBase, Required<Id> as id & Required<NoteBase> as note);
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct NoteAttachmentExt {
|
pub struct NoteAttachmentExt {
|
||||||
pub attachments: Vec<PackDriveFileBase>,
|
pub attachments: Vec<PackDriveFileBase>,
|
||||||
pub poll: Option<PollBase>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(
|
pack!(
|
||||||
|
@ -93,10 +94,28 @@ pack!(
|
||||||
Required<Id> as id & Required<NoteBase> as note & Required<NoteAttachmentExt> as attachment & Required<NoteDetailExt> as detail
|
Required<Id> as id & Required<NoteBase> as note & Required<NoteAttachmentExt> as attachment & Required<NoteDetailExt> as detail
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
|
pub enum Reaction {
|
||||||
|
Unicode(String),
|
||||||
|
Shortcode(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Reaction {
|
||||||
|
pub fn guess_from(s: &str, default_emote: &str) -> Option<Self> {
|
||||||
|
let code = s.trim();
|
||||||
|
match code.chars().next() {
|
||||||
|
Some(':') => Some(Reaction::Shortcode(code.to_string())),
|
||||||
|
Some(_) => Some(Reaction::Unicode(code.to_string())),
|
||||||
|
None => Self::guess_from(default_emote, "👍"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
pub struct ReactionBase {
|
pub struct ReactionBase {
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
pub user_id: String,
|
pub user_id: String,
|
||||||
|
pub reaction: Reaction,
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(PackReactionBase, Required<Id> as id & Required<ReactionBase> as reaction);
|
pack!(PackReactionBase, Required<Id> as id & Required<ReactionBase> as reaction);
|
||||||
|
|
|
@ -5,10 +5,9 @@ use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
|
||||||
|
use crate::types::note::PackNoteFull;
|
||||||
use magnetar_sdk_macros::pack;
|
use magnetar_sdk_macros::pack;
|
||||||
|
|
||||||
use super::note::PackNoteWithAttachments;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Default, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub enum AvatarDecoration {
|
pub enum AvatarDecoration {
|
||||||
|
@ -85,7 +84,7 @@ pub struct UserProfileExt {
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct UserProfilePinsEx {
|
pub struct UserProfilePinsEx {
|
||||||
pub pinned_notes: Vec<PackNoteWithAttachments>,
|
pub pinned_notes: Vec<PackNoteFull>,
|
||||||
// pub pinned_page: Option<Page>,
|
// pub pinned_page: Option<Page>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,26 @@
|
||||||
use magnetar_calckey_model::ck;
|
use magnetar_calckey_model::ck;
|
||||||
use magnetar_sdk::types::drive::DriveFileBase;
|
use magnetar_sdk::types::drive::{DriveFileBase, ImageMeta};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::model::{PackBase, PackExtract, PackType, PackingContext};
|
use crate::model::{PackType, PackingContext};
|
||||||
|
|
||||||
impl PackType for PackExtract<DriveFileBase> {
|
impl PackType<&ck::drive_file::Model> for DriveFileBase {
|
||||||
type Input = PackBase<ck::drive_file::Model>;
|
fn extract(_context: &PackingContext, file: &ck::drive_file::Model) -> Self {
|
||||||
|
let media_metadata = ImageMeta::deserialize(file.properties.clone()).unwrap_or_default();
|
||||||
|
|
||||||
fn extract(context: &PackingContext<Self>, data: Self::Input) -> Self {
|
DriveFileBase {
|
||||||
let PackBase(file) = data;
|
name: file.name.clone(),
|
||||||
|
|
||||||
PackExtract(DriveFileBase {
|
|
||||||
name: file.name,
|
|
||||||
created_at: file.created_at.into(),
|
created_at: file.created_at.into(),
|
||||||
size: file.size as u64,
|
size: file.size as u64,
|
||||||
sha256: todo!(),
|
hash: None, // TODO: blake3
|
||||||
mime_type: file.r#type,
|
mime_type: file.r#type.clone(),
|
||||||
image_meta: todo!(),
|
media_metadata,
|
||||||
url: Some(file.url),
|
url: Some(file.url.clone()),
|
||||||
thumbnail_url: file.thumbnail_url,
|
thumbnail_url: file.thumbnail_url.clone(),
|
||||||
sensitive: file.is_sensitive,
|
sensitive: file.is_sensitive,
|
||||||
comment: file.comment,
|
comment: file.comment.clone(),
|
||||||
folder_id: file.folder_id,
|
folder_id: file.folder_id.clone(),
|
||||||
user_id: file.user_id,
|
user_id: file.user_id.clone(),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,15 @@
|
||||||
use crate::model::{PackBase, PackExtract, PackType, PackingContext};
|
use crate::model::{PackType, PackingContext};
|
||||||
use magnetar_calckey_model::ck;
|
use magnetar_calckey_model::ck;
|
||||||
use magnetar_sdk::types::emoji::EmojiBase;
|
use magnetar_sdk::types::emoji::EmojiBase;
|
||||||
|
|
||||||
impl PackType for PackExtract<EmojiBase> {
|
impl PackType<&ck::emoji::Model> for EmojiBase {
|
||||||
type Input = PackBase<ck::emoji::Model>;
|
fn extract(_context: &PackingContext, emoji: &ck::emoji::Model) -> Self {
|
||||||
|
EmojiBase {
|
||||||
fn extract(_context: &PackingContext<Self>, data: Self::Input) -> Self {
|
shortcode: emoji.name.clone(),
|
||||||
let PackBase(model) = data;
|
category: emoji.category.clone(),
|
||||||
|
url: emoji.public_url.clone(),
|
||||||
PackExtract(EmojiBase {
|
width: emoji.width,
|
||||||
shortcode: model.name.clone(),
|
height: emoji.height,
|
||||||
category: model.category.clone(),
|
}
|
||||||
url: model.public_url.clone(),
|
|
||||||
width: model.width,
|
|
||||||
height: model.height,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,57 +1,54 @@
|
||||||
use magnetar_calckey_model::ck;
|
use magnetar_calckey_model::ck;
|
||||||
|
use magnetar_sdk::types::note::Reaction;
|
||||||
|
use magnetar_sdk::types::user::PackUserBase;
|
||||||
use magnetar_sdk::types::{
|
use magnetar_sdk::types::{
|
||||||
drive::PackDriveFileBase,
|
drive::PackDriveFileBase,
|
||||||
emoji::EmojiContext,
|
emoji::EmojiContext,
|
||||||
note::{NoteAttachmentExt, NoteBase, NoteVisibility, PackReactionBase, ReactionBase},
|
note::{NoteAttachmentExt, NoteBase, NoteVisibility, PackReactionBase, ReactionBase},
|
||||||
user::UserBase,
|
user::UserBase,
|
||||||
};
|
};
|
||||||
|
use magnetar_sdk::{Packed, Required};
|
||||||
|
|
||||||
use crate::model::{
|
use crate::model::{PackType, PackingContext};
|
||||||
LinkAuto, PackAggregate, PackBase, PackChild, PackExtract, PackMulti, PackPacked, PackType,
|
|
||||||
PackingContext,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl PackType for PackExtract<ReactionBase> {
|
impl PackType<&ck::note_reaction::Model> for ReactionBase {
|
||||||
type Input = PackBase<ck::note_reaction::Model>;
|
fn extract(context: &PackingContext, reaction: &ck::note_reaction::Model) -> Self {
|
||||||
|
ReactionBase {
|
||||||
fn extract(_context: &PackingContext<Self>, data: Self::Input) -> Self {
|
|
||||||
let PackBase(reaction) = data;
|
|
||||||
|
|
||||||
PackExtract(ReactionBase {
|
|
||||||
created_at: reaction.created_at.into(),
|
created_at: reaction.created_at.into(),
|
||||||
user_id: reaction.user_id,
|
user_id: reaction.user_id.clone(),
|
||||||
})
|
reaction: Reaction::guess_from(
|
||||||
|
&reaction.reaction,
|
||||||
|
&context.instance_info.default_reaction,
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|| /* Shouldn't happen */ Reaction::Unicode("👍".to_string())),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PackType for PackExtract<NoteBase> {
|
pub struct NoteBaseSource<'a> {
|
||||||
type Input = PackMulti<(
|
pub note: &'a ck::note::Model,
|
||||||
PackBase<ck::note::Model>,
|
pub reactions: &'a Vec<PackReactionBase>,
|
||||||
PackAggregate<PackChild<PackPacked<PackReactionBase>, LinkAuto>>,
|
pub emojis: &'a EmojiContext,
|
||||||
PackChild<PackExtract<EmojiContext>, LinkAuto>,
|
pub user: &'a UserBase,
|
||||||
PackChild<PackExtract<UserBase>, LinkAuto>,
|
}
|
||||||
)>;
|
|
||||||
|
|
||||||
fn extract(_context: &PackingContext<Self>, data: Self::Input) -> Self {
|
|
||||||
let PackMulti((
|
|
||||||
PackBase(note),
|
|
||||||
PackAggregate(reactions, ..),
|
|
||||||
PackChild(PackExtract(emojis), ..),
|
|
||||||
PackChild(PackExtract(user), ..),
|
|
||||||
)) = data;
|
|
||||||
|
|
||||||
let reactions = reactions
|
|
||||||
.into_iter()
|
|
||||||
.map(|PackChild(PackPacked(t, ..), ..)| t)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
|
impl PackType<NoteBaseSource<'_>> for NoteBase {
|
||||||
|
fn extract(
|
||||||
|
_context: &PackingContext,
|
||||||
|
NoteBaseSource {
|
||||||
|
note,
|
||||||
|
reactions,
|
||||||
|
emojis,
|
||||||
|
user,
|
||||||
|
}: NoteBaseSource<'_>,
|
||||||
|
) -> Self {
|
||||||
use ck::sea_orm_active_enums::NoteVisibilityEnum as NVE;
|
use ck::sea_orm_active_enums::NoteVisibilityEnum as NVE;
|
||||||
PackExtract(NoteBase {
|
NoteBase {
|
||||||
created_at: note.created_at.into(),
|
created_at: note.created_at.into(),
|
||||||
cw: note.cw,
|
cw: note.cw.clone(),
|
||||||
uri: note.uri,
|
uri: note.uri.clone(),
|
||||||
url: note.url,
|
url: note.url.clone(),
|
||||||
text: note.text.unwrap_or_default(),
|
text: note.text.clone().unwrap_or_default(),
|
||||||
visibility: match note.visibility {
|
visibility: match note.visibility {
|
||||||
NVE::Followers => NoteVisibility::Followers,
|
NVE::Followers => NoteVisibility::Followers,
|
||||||
NVE::Hidden => NoteVisibility::Direct,
|
NVE::Hidden => NoteVisibility::Direct,
|
||||||
|
@ -59,29 +56,28 @@ impl PackType for PackExtract<NoteBase> {
|
||||||
NVE::Home => NoteVisibility::Home,
|
NVE::Home => NoteVisibility::Home,
|
||||||
NVE::Specified => NoteVisibility::Direct,
|
NVE::Specified => NoteVisibility::Direct,
|
||||||
},
|
},
|
||||||
user: Box::new(user),
|
user: Box::new(PackUserBase::pack_from((
|
||||||
parent_note_id: note.reply_id,
|
Required(note.user_id.as_str().into()),
|
||||||
renoted_note_id: note.renote_id,
|
Required(user.clone()),
|
||||||
|
))),
|
||||||
|
parent_note_id: note.reply_id.clone(),
|
||||||
|
renoted_note_id: note.renote_id.clone(),
|
||||||
reply_count: note.replies_count as u64,
|
reply_count: note.replies_count as u64,
|
||||||
renote_count: note.renote_count as u64,
|
renote_count: note.renote_count as u64,
|
||||||
hashtags: note.tags,
|
hashtags: note.tags.clone(),
|
||||||
reactions,
|
reactions: reactions.clone(),
|
||||||
emojis,
|
emojis: emojis.clone(),
|
||||||
local_only: note.local_only,
|
local_only: note.local_only,
|
||||||
})
|
has_poll: note.has_poll,
|
||||||
|
file_ids: note.file_ids.clone(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PackType for PackExtract<NoteAttachmentExt> {
|
impl PackType<&Vec<PackDriveFileBase>> for NoteAttachmentExt {
|
||||||
type Input = PackMulti<(
|
fn extract(_context: &PackingContext, data: &Vec<PackDriveFileBase>) -> Self {
|
||||||
PackBase<ck::note::Model>,
|
NoteAttachmentExt {
|
||||||
PackAggregate<PackChild<PackPacked<PackDriveFileBase>, LinkAuto>>,
|
attachments: data.clone(),
|
||||||
)>;
|
}
|
||||||
|
|
||||||
fn extract(_context: &PackingContext<Self>, data: Self::Input) -> Self {
|
|
||||||
PackExtract(NoteAttachmentExt {
|
|
||||||
attachments: todo!(),
|
|
||||||
poll: todo!(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,60 +1,29 @@
|
||||||
use magnetar_calckey_model::ck;
|
use magnetar_calckey_model::ck;
|
||||||
use magnetar_calckey_model::ck::sea_orm_active_enums::UserProfileFfvisibilityEnum;
|
use magnetar_calckey_model::ck::sea_orm_active_enums::UserProfileFfvisibilityEnum;
|
||||||
use magnetar_sdk::types::emoji::{EmojiContext, PackEmojiBase};
|
use magnetar_sdk::types::emoji::{EmojiContext, PackEmojiBase};
|
||||||
use magnetar_sdk::types::note::PackNoteWithAttachments;
|
use magnetar_sdk::types::note::PackNoteFull;
|
||||||
use magnetar_sdk::types::user::{
|
use magnetar_sdk::types::user::{
|
||||||
AvatarDecoration, PackSecurityKeyBase, SecurityKeyBase, SpeechTransform, UserBase,
|
AvatarDecoration, PackSecurityKeyBase, SecurityKeyBase, SpeechTransform, UserBase,
|
||||||
UserDetailExt, UserProfileExt, UserProfilePinsEx, UserRelationExt, UserSecretsExt,
|
UserDetailExt, UserProfileExt, UserProfilePinsEx, UserRelationExt, UserSecretsExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::model::{
|
use crate::model::{PackType, PackingContext};
|
||||||
LinkAuto, LinkIdent, PackAggregate, PackBase, PackChild, PackExtract, PackMaybe, PackMulti,
|
|
||||||
PackPacked, PackType, PackingContext,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl PackType for PackExtract<EmojiContext> {
|
impl PackType<&[PackEmojiBase]> for EmojiContext {
|
||||||
type Input = PackMulti<(
|
fn extract(_context: &PackingContext, data: &[PackEmojiBase]) -> Self {
|
||||||
PackBase<ck::user::Model>,
|
EmojiContext(data.to_owned())
|
||||||
PackChild<PackAggregate<PackPacked<PackEmojiBase>>, LinkAuto>,
|
|
||||||
)>;
|
|
||||||
|
|
||||||
fn extract(_context: &PackingContext<Self>, data: Self::Input) -> Self {
|
|
||||||
let PackMulti((PackBase(_), PackChild(PackAggregate(emojis), ..))) = data;
|
|
||||||
|
|
||||||
PackExtract(EmojiContext(
|
|
||||||
emojis
|
|
||||||
.into_iter()
|
|
||||||
.map(|PackPacked(t, ..)| t)
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
type UserBaseSource<'a> = (
|
||||||
pub struct UserAvatarLink;
|
&'a ck::user::Model,
|
||||||
impl From<UserAvatarLink> for LinkIdent {
|
&'a Option<ck::drive_file::Model>,
|
||||||
fn from(_: UserAvatarLink) -> Self {
|
&'a EmojiContext,
|
||||||
LinkIdent::Named("avatar".to_string())
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PackType for PackExtract<UserBase> {
|
impl PackType<UserBaseSource<'_>> for UserBase {
|
||||||
type Input = PackMulti<(
|
fn extract(_context: &PackingContext, (user, avatar, emoji_context): UserBaseSource) -> Self {
|
||||||
PackBase<ck::user::Model>,
|
UserBase {
|
||||||
PackChild<PackMaybe<PackBase<ck::drive_file::Model>>, UserAvatarLink>,
|
|
||||||
PackChild<PackExtract<EmojiContext>, LinkAuto>,
|
|
||||||
)>;
|
|
||||||
|
|
||||||
fn extract(_context: &PackingContext<Self>, data: Self::Input) -> Self {
|
|
||||||
let PackMulti((
|
|
||||||
PackBase(ref user),
|
|
||||||
PackChild(PackMaybe(ref avatar), _),
|
|
||||||
PackChild(PackExtract(ref emoji_context), _),
|
|
||||||
)) = data;
|
|
||||||
|
|
||||||
let avatar = avatar.as_ref().map(|PackBase(v)| v);
|
|
||||||
|
|
||||||
PackExtract(UserBase {
|
|
||||||
acct: user
|
acct: user
|
||||||
.host
|
.host
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -69,8 +38,8 @@ impl PackType for PackExtract<UserBase> {
|
||||||
SpeechTransform::None
|
SpeechTransform::None
|
||||||
},
|
},
|
||||||
created_at: user.created_at.into(),
|
created_at: user.created_at.into(),
|
||||||
avatar_url: avatar.map(|v| v.url.clone()),
|
avatar_url: avatar.as_ref().map(|v| v.url.clone()),
|
||||||
avatar_blurhash: avatar.and_then(|v| v.blurhash.clone()),
|
avatar_blurhash: avatar.as_ref().and_then(|v| v.blurhash.clone()),
|
||||||
avatar_color: None,
|
avatar_color: None,
|
||||||
avatar_decoration: if user.is_cat {
|
avatar_decoration: if user.is_cat {
|
||||||
AvatarDecoration::CatEars
|
AvatarDecoration::CatEars
|
||||||
|
@ -81,35 +50,25 @@ impl PackType for PackExtract<UserBase> {
|
||||||
is_moderator: user.is_moderator,
|
is_moderator: user.is_moderator,
|
||||||
is_bot: user.is_bot,
|
is_bot: user.is_bot,
|
||||||
emojis: emoji_context.clone(),
|
emojis: emoji_context.clone(),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PackType for PackExtract<UserProfileExt> {
|
type UserProfileExtSource<'a> = (
|
||||||
type Input = PackMulti<(
|
&'a ck::user::Model,
|
||||||
PackBase<ck::user::Model>,
|
&'a ck::user_profile::Model,
|
||||||
PackChild<PackBase<ck::user_profile::Model>, LinkAuto>,
|
Option<&'a UserRelationExt>,
|
||||||
PackMaybe<PackExtract<UserRelationExt>>,
|
);
|
||||||
)>;
|
|
||||||
|
|
||||||
fn extract(context: &PackingContext<Self>, data: Self::Input) -> Self {
|
|
||||||
let PackMulti((
|
|
||||||
PackBase(ref user),
|
|
||||||
PackChild(PackBase(ref profile), ..),
|
|
||||||
PackMaybe(ref relation),
|
|
||||||
)) = data;
|
|
||||||
|
|
||||||
let relation = relation.as_ref().map(|PackExtract(v)| v);
|
|
||||||
|
|
||||||
|
impl PackType<UserProfileExtSource<'_>> for UserProfileExt {
|
||||||
|
fn extract(context: &PackingContext, (user, profile, relation): UserProfileExtSource) -> Self {
|
||||||
let follow_visibility = match profile.ff_visibility {
|
let follow_visibility = match profile.ff_visibility {
|
||||||
UserProfileFfvisibilityEnum::Public => true,
|
UserProfileFfvisibilityEnum::Public => true,
|
||||||
UserProfileFfvisibilityEnum::Followers => {
|
UserProfileFfvisibilityEnum::Followers => relation.is_some_and(|r| r.follows_you),
|
||||||
relation.as_ref().is_some_and(|r| r.follows_you)
|
|
||||||
}
|
|
||||||
UserProfileFfvisibilityEnum::Private => false,
|
UserProfileFfvisibilityEnum::Private => false,
|
||||||
} || context.is_self(user);
|
} || context.is_self(user);
|
||||||
|
|
||||||
PackExtract(UserProfileExt {
|
UserProfileExt {
|
||||||
is_locked: user.is_locked,
|
is_locked: user.is_locked,
|
||||||
is_silenced: user.is_silenced,
|
is_silenced: user.is_silenced,
|
||||||
is_suspended: user.is_suspended,
|
is_suspended: user.is_suspended,
|
||||||
|
@ -130,131 +89,96 @@ impl PackType for PackExtract<UserProfileExt> {
|
||||||
banner_color: None,
|
banner_color: None,
|
||||||
banner_blurhash: None,
|
banner_blurhash: None,
|
||||||
has_public_reactions: profile.public_reactions,
|
has_public_reactions: profile.public_reactions,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PackType for PackExtract<UserDetailExt> {
|
impl PackType<&ck::user::Model> for UserDetailExt {
|
||||||
type Input = PackBase<ck::user::Model>;
|
fn extract(_context: &PackingContext, user: &ck::user::Model) -> Self {
|
||||||
|
UserDetailExt {
|
||||||
fn extract(_context: &PackingContext<Self>, data: Self::Input) -> Self {
|
last_fetched_at: user.last_fetched_at.map(Into::into),
|
||||||
let PackBase(model) = data;
|
uri: user.uri.clone(),
|
||||||
|
updated_at: user.updated_at.map(Into::into),
|
||||||
PackExtract(UserDetailExt {
|
}
|
||||||
last_fetched_at: model.last_fetched_at.map(Into::into),
|
|
||||||
uri: model.uri.clone(),
|
|
||||||
updated_at: model.updated_at.map(Into::into),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PackType for PackExtract<UserRelationExt> {
|
type UserRelationExtSource<'a> = (
|
||||||
type Input = PackMulti<(
|
Option<&'a ck::following::Model>,
|
||||||
PackChild<PackBase<ck::following::Model>, LinkAuto>,
|
Option<&'a ck::following::Model>,
|
||||||
PackChild<PackBase<ck::blocking::Model>, LinkAuto>,
|
Option<&'a ck::blocking::Model>,
|
||||||
PackChild<PackBase<ck::muting::Model>, LinkAuto>,
|
Option<&'a ck::blocking::Model>,
|
||||||
PackChild<PackBase<ck::renote_muting::Model>, LinkAuto>,
|
Option<&'a ck::muting::Model>,
|
||||||
)>;
|
Option<&'a ck::renote_muting::Model>,
|
||||||
|
);
|
||||||
fn extract(context: &PackingContext<Self>, data: Self::Input) -> Self {
|
|
||||||
let PackMulti((
|
|
||||||
PackChild(PackBase(ref follow), ..),
|
|
||||||
PackChild(PackBase(ref block), ..),
|
|
||||||
PackChild(PackBase(ref mute), ..),
|
|
||||||
PackChild(PackBase(ref renote_mute), ..),
|
|
||||||
)) = data;
|
|
||||||
|
|
||||||
|
impl PackType<UserRelationExtSource<'_>> for UserRelationExt {
|
||||||
|
fn extract(
|
||||||
|
context: &PackingContext,
|
||||||
|
(follow_out, follow_in, block_out, block_in, mute, renote_mute): UserRelationExtSource,
|
||||||
|
) -> Self {
|
||||||
let self_user = context.self_user();
|
let self_user = context.self_user();
|
||||||
|
|
||||||
PackExtract(UserRelationExt {
|
UserRelationExt {
|
||||||
follows_you: self_user.is_some_and(|self_user| {
|
follows_you: self_user.is_some_and(|self_user| {
|
||||||
self_user.id == follow.followee_id && self_user.id != follow.follower_id
|
follow_in.is_some_and(|follow_in| {
|
||||||
|
self_user.id == follow_in.followee_id && self_user.id != follow_in.follower_id
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
you_follow: self_user.is_some_and(|self_user| {
|
you_follow: self_user.is_some_and(|self_user| {
|
||||||
self_user.id == follow.follower_id && self_user.id != follow.followee_id
|
follow_out.is_some_and(|follow_out| {
|
||||||
|
self_user.id == follow_out.follower_id && self_user.id != follow_out.followee_id
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
blocks_you: self_user.is_some_and(|self_user| {
|
blocks_you: self_user.is_some_and(|self_user| {
|
||||||
self_user.id == block.blockee_id && self_user.id != block.blocker_id
|
block_in.is_some_and(|block_in| {
|
||||||
|
self_user.id == block_in.blockee_id && self_user.id != block_in.blocker_id
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
you_block: self_user.is_some_and(|self_user| {
|
you_block: self_user.is_some_and(|self_user| {
|
||||||
self_user.id == block.blocker_id && self_user.id != block.blockee_id
|
block_out.is_some_and(|block_out| {
|
||||||
|
self_user.id == block_out.blocker_id && self_user.id != block_out.blockee_id
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
mute: self_user.is_some_and(|self_user| {
|
mute: self_user.is_some_and(|self_user| {
|
||||||
self_user.id == mute.muter_id && self_user.id != mute.mutee_id
|
mute.is_some_and(|mute| {
|
||||||
|
self_user.id == mute.muter_id && self_user.id != mute.mutee_id
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
mute_renotes: self_user.is_some_and(|self_user| {
|
mute_renotes: self_user.is_some_and(|self_user| {
|
||||||
self_user.id == renote_mute.muter_id && self_user.id != renote_mute.mutee_id
|
renote_mute.is_some_and(|renote_mute| {
|
||||||
|
self_user.id == renote_mute.muter_id && self_user.id != renote_mute.mutee_id
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PackType for PackExtract<UserProfilePinsEx> {
|
impl PackType<&[PackNoteFull]> for UserProfilePinsEx {
|
||||||
type Input = PackMulti<(
|
fn extract(_context: &PackingContext, pinned_notes: &[PackNoteFull]) -> Self {
|
||||||
PackBase<ck::user::Model>,
|
UserProfilePinsEx {
|
||||||
PackChild<
|
pinned_notes: pinned_notes.to_owned(),
|
||||||
PackAggregate<
|
}
|
||||||
PackMulti<(
|
|
||||||
PackBase<ck::user_note_pining::Model>,
|
|
||||||
PackChild<PackPacked<PackNoteWithAttachments>, LinkAuto>,
|
|
||||||
)>,
|
|
||||||
>,
|
|
||||||
LinkAuto,
|
|
||||||
>,
|
|
||||||
)>;
|
|
||||||
|
|
||||||
fn extract(_context: &PackingContext<Self>, data: Self::Input) -> Self {
|
|
||||||
let PackMulti((PackBase(_), PackChild(PackAggregate(pins), ..))) = data;
|
|
||||||
|
|
||||||
PackExtract(UserProfilePinsEx {
|
|
||||||
pinned_notes: pins
|
|
||||||
.into_iter()
|
|
||||||
.map(|PackMulti((_, PackChild(PackPacked(note, ..), ..)))| note)
|
|
||||||
.collect(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PackType for PackExtract<SecurityKeyBase> {
|
impl PackType<ck::user_security_key::Model> for SecurityKeyBase {
|
||||||
type Input = PackBase<ck::user_security_key::Model>;
|
fn extract(_context: &PackingContext, key: ck::user_security_key::Model) -> Self {
|
||||||
|
SecurityKeyBase {
|
||||||
fn extract(_context: &PackingContext<Self>, data: Self::Input) -> Self {
|
name: key.name.clone(),
|
||||||
let PackBase(model) = data;
|
last_used_at: Some(key.last_used.into()),
|
||||||
|
}
|
||||||
PackExtract(SecurityKeyBase {
|
|
||||||
name: model.name.clone(),
|
|
||||||
last_used_at: Some(model.last_used.into()),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PackType for PackExtract<UserSecretsExt> {
|
type UserSecretsExtSource<'a> = (&'a ck::user_profile::Model, &'a [PackSecurityKeyBase]);
|
||||||
type Input = PackMulti<(
|
|
||||||
PackBase<ck::user::Model>,
|
|
||||||
PackChild<
|
|
||||||
PackMulti<(
|
|
||||||
PackBase<ck::user_profile::Model>,
|
|
||||||
PackChild<PackAggregate<PackPacked<PackSecurityKeyBase>>, LinkAuto>,
|
|
||||||
)>,
|
|
||||||
LinkAuto,
|
|
||||||
>,
|
|
||||||
)>;
|
|
||||||
|
|
||||||
fn extract(_context: &PackingContext<Self>, data: Self::Input) -> Self {
|
impl PackType<UserSecretsExtSource<'_>> for UserSecretsExt {
|
||||||
let PackMulti((
|
fn extract(_context: &PackingContext, (profile, keys): UserSecretsExtSource) -> Self {
|
||||||
_,
|
UserSecretsExt {
|
||||||
PackChild(PackMulti((PackBase(ref profile), PackChild(PackAggregate(keys), ..))), ..),
|
|
||||||
)) = data;
|
|
||||||
|
|
||||||
let keys = keys
|
|
||||||
.into_iter()
|
|
||||||
.map(|PackPacked(t, ..)| t)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
PackExtract(UserSecretsExt {
|
|
||||||
email: profile.email.clone(),
|
email: profile.email.clone(),
|
||||||
email_verified: profile.email_verified,
|
email_verified: profile.email_verified,
|
||||||
security_keys: keys.clone(),
|
security_keys: keys.to_owned(),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,163 +0,0 @@
|
||||||
use crate::model::LinkIdent;
|
|
||||||
use std::any::TypeId;
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
type PMT = PackMapType;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Multi(Vec<Arc<PMT>>);
|
|
||||||
|
|
||||||
impl Multi {
|
|
||||||
pub fn get_base(&self) -> Option<&PMT> {
|
|
||||||
for pmt in &self.0 {
|
|
||||||
match pmt.as_ref() {
|
|
||||||
PackMapType::FromType(CommandPackFromType { base: true, .. }) => return Some(pmt),
|
|
||||||
PackMapType::Maybe(CommandPackMaybe { inner }) => {
|
|
||||||
if let PackMapType::FromType(..) = inner.as_ref() {
|
|
||||||
return Some(pmt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PackMapType::Multi(CommandPackMulti { inner }) => {
|
|
||||||
if let Some(pmt) = inner.get_base() {
|
|
||||||
return Some(pmt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<PMT>> for Multi {
|
|
||||||
fn from(v: Vec<PMT>) -> Self {
|
|
||||||
Multi(v.into_iter().map(Arc::new).collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum PackMapType {
|
|
||||||
FromType(CommandPackFromType),
|
|
||||||
Maybe(CommandPackMaybe),
|
|
||||||
Child(CommandPackChild),
|
|
||||||
Aggregate(CommandPackAggregate),
|
|
||||||
Multi(CommandPackMulti),
|
|
||||||
}
|
|
||||||
|
|
||||||
trait PackCommand {}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct CommandPackFromType {
|
|
||||||
pub name: String,
|
|
||||||
pub id: TypeId,
|
|
||||||
pub base: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PackCommand for CommandPackFromType {}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct CommandPackMaybe {
|
|
||||||
pub inner: Arc<PackMapType>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PackCommand for CommandPackMaybe {}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct CommandPackChild {
|
|
||||||
pub inner: Arc<PackMapType>,
|
|
||||||
pub link: LinkIdent,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PackCommand for CommandPackChild {}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct CommandPackAggregate {
|
|
||||||
pub inner: Arc<PackMapType>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PackCommand for CommandPackAggregate {}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct CommandPackMulti {
|
|
||||||
pub inner: Multi,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PackCommand for CommandPackMulti {}
|
|
||||||
|
|
||||||
impl PackMapType {
|
|
||||||
pub fn from_type<T: Sized + 'static>(base: bool) -> Self {
|
|
||||||
PackMapType::FromType(CommandPackFromType {
|
|
||||||
name: std::any::type_name::<T>().to_string(),
|
|
||||||
id: TypeId::of::<T>(),
|
|
||||||
base,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_base_type(&self) -> TypeId {
|
|
||||||
let id = match self {
|
|
||||||
PackMapType::FromType(CommandPackFromType { base: true, .. }) => Some(self),
|
|
||||||
PackMapType::Multi(CommandPackMulti { inner }) => inner.get_base(),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let PackMapType::FromType(CommandPackFromType { id, .. }) = id.expect("no base type") else {
|
|
||||||
panic!("base type is not FromType")
|
|
||||||
};
|
|
||||||
|
|
||||||
*id
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_edges(
|
|
||||||
&self,
|
|
||||||
base_parent: &Option<(usize, String)>,
|
|
||||||
parent: &Option<(usize, String)>,
|
|
||||||
level: usize,
|
|
||||||
) -> Vec<(Option<String>, String)> {
|
|
||||||
match self {
|
|
||||||
PackMapType::FromType(CommandPackFromType {
|
|
||||||
name, base: true, ..
|
|
||||||
}) => {
|
|
||||||
vec![(base_parent.clone().map(|(_, s)| s), name.clone())]
|
|
||||||
}
|
|
||||||
PackMapType::FromType(CommandPackFromType {
|
|
||||||
name, base: false, ..
|
|
||||||
}) => {
|
|
||||||
vec![(parent.clone().map(|(_, s)| s), name.clone())]
|
|
||||||
}
|
|
||||||
PackMapType::Maybe(CommandPackMaybe { inner }) => {
|
|
||||||
inner.as_edges(base_parent, parent, level)
|
|
||||||
}
|
|
||||||
PackMapType::Aggregate(CommandPackAggregate { inner }) => {
|
|
||||||
inner.as_edges(base_parent, parent, level)
|
|
||||||
}
|
|
||||||
PackMapType::Child(CommandPackChild { inner, .. }) => {
|
|
||||||
inner.as_edges(parent, &None, level + 1)
|
|
||||||
}
|
|
||||||
PackMapType::Multi(CommandPackMulti { inner }) => {
|
|
||||||
let new_parent = if let Some(PackMapType::FromType(CommandPackFromType {
|
|
||||||
name,
|
|
||||||
base: true,
|
|
||||||
..
|
|
||||||
})) = inner.get_base()
|
|
||||||
{
|
|
||||||
if parent.clone().is_some_and(|(l, _)| l == level) {
|
|
||||||
parent.clone()
|
|
||||||
} else {
|
|
||||||
Some((level, name.clone()))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
parent.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut edges = Vec::new();
|
|
||||||
for t in &inner.0 {
|
|
||||||
edges.extend(t.as_edges(base_parent, &new_parent, level));
|
|
||||||
}
|
|
||||||
|
|
||||||
edges
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
381
src/model/mod.rs
381
src/model/mod.rs
|
@ -1,356 +1,21 @@
|
||||||
use crate::model::map_type::{
|
|
||||||
CommandPackAggregate, CommandPackChild, CommandPackMaybe, CommandPackMulti, PackMapType,
|
|
||||||
};
|
|
||||||
use magnetar_calckey_model::ck;
|
use magnetar_calckey_model::ck;
|
||||||
use magnetar_sdk::types::Id;
|
|
||||||
use magnetar_sdk::{Packed, Required};
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use self::data::id::BaseId;
|
|
||||||
|
|
||||||
pub mod data;
|
pub mod data;
|
||||||
pub mod db_pack;
|
|
||||||
pub mod map_type;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct InternalContext {
|
pub struct PackingContext {
|
||||||
|
instance_info: Arc<ck::meta::Model>,
|
||||||
self_user: Option<Arc<ck::user::Model>>,
|
self_user: Option<Arc<ck::user::Model>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
pub trait PackType<I>: 'static {
|
||||||
pub struct PackingContext<T: ?Sized> {
|
fn extract(context: &PackingContext, data: I) -> Self;
|
||||||
context: Arc<InternalContext>,
|
|
||||||
data: Arc<PackMapType>,
|
|
||||||
_type: PhantomData<T>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized> PackingContext<T> {
|
impl PackingContext {
|
||||||
fn cloned<S>(&self) -> PackingContext<S> {
|
|
||||||
PackingContext {
|
|
||||||
context: self.context.clone(),
|
|
||||||
data: self.data.clone(),
|
|
||||||
_type: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ReportType: 'static {
|
|
||||||
type Input: 'static;
|
|
||||||
|
|
||||||
fn report_type() -> PackMapType;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait PackType: 'static {
|
|
||||||
type Input: 'static;
|
|
||||||
|
|
||||||
fn extract(context: &PackingContext<Self>, data: Self::Input) -> Self;
|
|
||||||
|
|
||||||
fn is_base_type() -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PackExtract<T>(T);
|
|
||||||
|
|
||||||
impl<Out> ReportType for PackExtract<Out>
|
|
||||||
where
|
|
||||||
Self: PackType,
|
|
||||||
<Self as PackType>::Input: ReportType + PackType,
|
|
||||||
{
|
|
||||||
type Input = <Self as PackType>::Input;
|
|
||||||
|
|
||||||
fn report_type() -> PackMapType {
|
|
||||||
<Self as PackType>::Input::report_type()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum LinkIdent {
|
|
||||||
Auto,
|
|
||||||
Named(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
|
||||||
pub struct LinkAuto;
|
|
||||||
|
|
||||||
impl From<LinkAuto> for LinkIdent {
|
|
||||||
fn from(_: LinkAuto) -> Self {
|
|
||||||
LinkIdent::Auto
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Raw input type children
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PackBase<T: 'static>(T);
|
|
||||||
|
|
||||||
impl<In: 'static> ReportType for PackBase<In> {
|
|
||||||
type Input = In;
|
|
||||||
|
|
||||||
fn report_type() -> PackMapType {
|
|
||||||
PackMapType::from_type::<In>(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<In: 'static> PackType for PackBase<In> {
|
|
||||||
type Input = In;
|
|
||||||
|
|
||||||
fn extract(_context: &PackingContext<Self>, data: <Self as PackType>::Input) -> Self {
|
|
||||||
PackBase(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_base_type() -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Raw input type children, non-base
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PackExt<T: 'static>(T);
|
|
||||||
|
|
||||||
impl<In: 'static> ReportType for PackExt<In> {
|
|
||||||
type Input = In;
|
|
||||||
|
|
||||||
fn report_type() -> PackMapType {
|
|
||||||
PackMapType::from_type::<In>(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<In: 'static> PackType for PackExt<In> {
|
|
||||||
type Input = In;
|
|
||||||
|
|
||||||
fn extract(_context: &PackingContext<Self>, data: <Self as PackType>::Input) -> Self {
|
|
||||||
PackExt(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tree children
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PackChild<T: ReportType + PackType, L: Into<LinkIdent> + Default + 'static>(
|
|
||||||
T,
|
|
||||||
PhantomData<fn() -> L>,
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<In: ReportType + PackType, L: Into<LinkIdent> + Default + 'static> ReportType
|
|
||||||
for PackChild<In, L>
|
|
||||||
{
|
|
||||||
type Input = In;
|
|
||||||
|
|
||||||
fn report_type() -> PackMapType {
|
|
||||||
PackMapType::Child(CommandPackChild {
|
|
||||||
inner: Arc::new(In::report_type()),
|
|
||||||
link: L::into(L::default()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<In: ReportType + PackType, L: Into<LinkIdent> + Default + 'static> PackType
|
|
||||||
for PackChild<In, L>
|
|
||||||
{
|
|
||||||
type Input = In;
|
|
||||||
|
|
||||||
fn extract(_context: &PackingContext<Self>, data: <Self as PackType>::Input) -> Self {
|
|
||||||
PackChild(data, PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Aggregate multiple entries 1:N
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PackAggregate<T: ReportType + PackType>(Vec<T>);
|
|
||||||
|
|
||||||
impl<In: ReportType + PackType> ReportType for PackAggregate<In> {
|
|
||||||
type Input = In;
|
|
||||||
|
|
||||||
fn report_type() -> PackMapType {
|
|
||||||
PackMapType::Aggregate(CommandPackAggregate {
|
|
||||||
inner: Arc::new(In::report_type()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<In: ReportType + PackType> PackType for PackAggregate<In> {
|
|
||||||
type Input = Vec<In>;
|
|
||||||
|
|
||||||
fn extract(_context: &PackingContext<Self>, data: <Self as PackType>::Input) -> Self {
|
|
||||||
PackAggregate(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// An item that may or may not be present
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PackMaybe<T: ReportType + PackType>(Option<T>);
|
|
||||||
|
|
||||||
impl<In: ReportType + PackType> ReportType for PackMaybe<In> {
|
|
||||||
type Input = In;
|
|
||||||
|
|
||||||
fn report_type() -> PackMapType {
|
|
||||||
In::report_type()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<In: ReportType + PackType> PackType for PackMaybe<In> {
|
|
||||||
type Input = Option<In>;
|
|
||||||
|
|
||||||
fn extract(_context: &PackingContext<Self>, data: <Self as PackType>::Input) -> Self {
|
|
||||||
PackMaybe(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multiple adjacent values
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PackMulti<T>(T);
|
|
||||||
|
|
||||||
macro_rules! impl_pack_multi {
|
|
||||||
($($a:ident as $b:ident),*) => {
|
|
||||||
impl<$($a,)*> ReportType for PackMulti<($($a,)*)>
|
|
||||||
where $($a: ReportType + PackType,)* {
|
|
||||||
type Input = ($(<$a as ReportType>::Input,)*);
|
|
||||||
|
|
||||||
fn report_type() -> PackMapType {
|
|
||||||
PackMapType::Multi(CommandPackMulti {
|
|
||||||
inner: vec![$(
|
|
||||||
$a::report_type(),
|
|
||||||
)*].into(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<$($a,)*> PackType for PackMulti<($($a,)*)>
|
|
||||||
where $($a: ReportType + PackType,)* {
|
|
||||||
type Input = ($(<$a as PackType>::Input,)*);
|
|
||||||
|
|
||||||
fn extract(context: &PackingContext<Self>, ($($b,)*): <Self as PackType>::Input) -> Self {
|
|
||||||
PackMulti(($(
|
|
||||||
<$a as PackType>::extract(&context.cloned(), $b),
|
|
||||||
)*))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_pack_multi!();
|
|
||||||
impl_pack_multi!(A as a);
|
|
||||||
impl_pack_multi!(A as a, B as b);
|
|
||||||
impl_pack_multi!(A as a, B as b, C as c);
|
|
||||||
impl_pack_multi!(A as a, B as b, C as c, D as d);
|
|
||||||
impl_pack_multi!(A as a, B as b, C as c, D as d, E as e);
|
|
||||||
impl_pack_multi!(A as a, B as b, C as c, D as d, E as e, F as f);
|
|
||||||
impl_pack_multi!(A as a, B as b, C as c, D as d, E as e, F as f, G as g);
|
|
||||||
impl_pack_multi!(A as a, B as b, C as c, D as d, E as e, F as f, G as g, H as h);
|
|
||||||
|
|
||||||
// API packed types
|
|
||||||
|
|
||||||
pub struct PackPacked<T: Packed, In = <T as Packed>::Input>(T, PhantomData<In>);
|
|
||||||
|
|
||||||
macro_rules! impl_pack_raw_tuple {
|
|
||||||
($($a:ident),*) => {
|
|
||||||
impl<$($a,)* P: Packed> ReportType for PackPacked<P, ($($a,)*)>
|
|
||||||
where $(PackWrapper<$a>: ReportType + PackType),* {
|
|
||||||
type Input = ($($a,)*);
|
|
||||||
|
|
||||||
fn report_type() -> PackMapType {
|
|
||||||
PackMapType::Multi(CommandPackMulti {
|
|
||||||
inner: vec![
|
|
||||||
$(
|
|
||||||
PackWrapper::<$a>::report_type()
|
|
||||||
),*
|
|
||||||
].into()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<$($a,)* P: Packed<Input = ($($a,)*)>> PackType for PackPacked<P, ($($a,)*)>
|
|
||||||
where $(PackWrapper<$a>: ReportType + PackType),* {
|
|
||||||
type Input = ($($a,)*);
|
|
||||||
|
|
||||||
fn extract(_context: &PackingContext<Self>, data: <Self as PackType>::Input) -> Self {
|
|
||||||
PackPacked(P::pack_from(data), PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_pack_raw_tuple!(A);
|
|
||||||
impl_pack_raw_tuple!(A, B);
|
|
||||||
impl_pack_raw_tuple!(A, B, C);
|
|
||||||
impl_pack_raw_tuple!(A, B, C, D);
|
|
||||||
impl_pack_raw_tuple!(A, B, C, D, E);
|
|
||||||
impl_pack_raw_tuple!(A, B, C, D, E, F);
|
|
||||||
impl_pack_raw_tuple!(A, B, C, D, E, F, G);
|
|
||||||
impl_pack_raw_tuple!(A, B, C, D, E, F, G, H);
|
|
||||||
|
|
||||||
// Helper wrapper for Multi components
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct PackWrapper<T>(T);
|
|
||||||
|
|
||||||
impl<In: 'static> ReportType for PackWrapper<Required<In>>
|
|
||||||
where
|
|
||||||
PackExtract<In>: ReportType + PackType,
|
|
||||||
{
|
|
||||||
type Input = <PackExtract<In> as ReportType>::Input;
|
|
||||||
|
|
||||||
fn report_type() -> PackMapType {
|
|
||||||
PackExtract::<In>::report_type()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<In: 'static> PackType for PackWrapper<Required<In>>
|
|
||||||
where
|
|
||||||
PackExtract<In>: ReportType + PackType,
|
|
||||||
{
|
|
||||||
type Input = <PackExtract<In> as PackType>::Input;
|
|
||||||
|
|
||||||
fn extract(context: &PackingContext<Self>, data: Self::Input) -> Self {
|
|
||||||
PackWrapper(Required(
|
|
||||||
PackExtract::<In>::extract(&context.cloned(), data).0,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<In: 'static> ReportType for PackWrapper<Option<In>>
|
|
||||||
where
|
|
||||||
PackExtract<In>: ReportType + PackType,
|
|
||||||
{
|
|
||||||
type Input = In;
|
|
||||||
|
|
||||||
fn report_type() -> PackMapType {
|
|
||||||
PackMapType::Maybe(CommandPackMaybe {
|
|
||||||
inner: Arc::new(PackExtract::<In>::report_type()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<In: 'static> PackType for PackWrapper<Option<In>>
|
|
||||||
where
|
|
||||||
PackExtract<In>: ReportType + PackType,
|
|
||||||
{
|
|
||||||
type Input = Option<In>;
|
|
||||||
|
|
||||||
fn extract(context: &PackingContext<Self>, data: Self::Input) -> Self {
|
|
||||||
PackWrapper(
|
|
||||||
PackMaybe::<PackExtract<In>>::extract(&context.cloned(), data.map(PackExtract))
|
|
||||||
.0
|
|
||||||
.map(|PackExtract(x)| x),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Concrete extractable types
|
|
||||||
impl PackType for PackExtract<Id> {
|
|
||||||
type Input = PackExt<Box<dyn BaseId>>;
|
|
||||||
|
|
||||||
fn extract(_context: &PackingContext<Self>, PackExt(data): <Self as PackType>::Input) -> Self {
|
|
||||||
PackExtract(Id {
|
|
||||||
id: data.get_id().to_owned(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Packing context that gets passed in
|
|
||||||
impl<T> PackingContext<T> {
|
|
||||||
fn self_user(&self) -> Option<&ck::user::Model> {
|
fn self_user(&self) -> Option<&ck::user::Model> {
|
||||||
self.context.self_user.as_deref()
|
self.self_user.as_deref()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_self(&self, user: &ck::user::Model) -> bool {
|
fn is_self(&self, user: &ck::user::Model) -> bool {
|
||||||
|
@ -358,37 +23,3 @@ impl<T> PackingContext<T> {
|
||||||
.is_some_and(|self_user| self_user.id == user.id)
|
.is_some_and(|self_user| self_user.id == user.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Packer<T> {
|
|
||||||
context: PackingContext<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pack_from_db<T: PackType + ReportType>(
|
|
||||||
self_user: Option<Arc<ck::user::Model>>,
|
|
||||||
) -> Packer<T> {
|
|
||||||
let types = T::report_type();
|
|
||||||
let context = PackingContext {
|
|
||||||
context: Arc::new(InternalContext { self_user }),
|
|
||||||
data: Arc::new(types),
|
|
||||||
_type: PhantomData,
|
|
||||||
};
|
|
||||||
|
|
||||||
Packer { context }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use magnetar_sdk::types::user::PackUserSelfMaybeAll;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_pack_type() {
|
|
||||||
let types = <PackPacked<PackUserSelfMaybeAll> as ReportType>::report_type();
|
|
||||||
println!("{:?}", types);
|
|
||||||
|
|
||||||
let edges = types.as_edges(&None, &None, 0);
|
|
||||||
for (from, to) in edges {
|
|
||||||
println!("{:?} --> {:?}", from, to);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue