refactor (backend-rs): remove strum derives

This commit is contained in:
naskya 2024-06-06 17:10:39 +09:00
parent f479ffb0e5
commit 642c4cb2c7
No known key found for this signature in database
GPG key ID: 712D413B3A9FED5C
14 changed files with 124 additions and 134 deletions

31
Cargo.lock generated
View file

@ -226,7 +226,6 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"serde_yaml", "serde_yaml",
"strum 0.26.2",
"sysinfo", "sysinfo",
"thiserror", "thiserror",
"tokio", "tokio",
@ -2644,12 +2643,6 @@ dependencies = [
"untrusted", "untrusted",
] ]
[[package]]
name = "rustversion"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.18" version = "1.0.18"
@ -2712,7 +2705,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"sqlx", "sqlx",
"strum 0.25.0", "strum",
"thiserror", "thiserror",
"time", "time",
"tracing", "tracing",
@ -3240,28 +3233,6 @@ version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
[[package]]
name = "strum"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
dependencies = [
"heck 0.4.1",
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.66",
]
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.5.0" version = "2.5.0"

View file

@ -35,7 +35,6 @@ sea-orm = { version = "0.12.15", default-features = false }
serde = { version = "1.0.203", default-features = false } serde = { version = "1.0.203", default-features = false }
serde_json = { version = "1.0.117", default-features = false } serde_json = { version = "1.0.117", default-features = false }
serde_yaml = { version = "0.9.34", default-features = false } serde_yaml = { version = "0.9.34", default-features = false }
strum = { version = "0.26.2", default-features = false }
syn = { version = "2.0.66", default-features = false } syn = { version = "2.0.66", default-features = false }
sysinfo = { version = "0.30.12", default-features = false } sysinfo = { version = "0.30.12", default-features = false }
thiserror = { version = "1.0.61", default-features = false } thiserror = { version = "1.0.61", default-features = false }

View file

@ -39,7 +39,6 @@ sea-orm = { workspace = true, features = ["macros", "runtime-tokio-rustls", "sql
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true } serde_json = { workspace = true }
serde_yaml = { workspace = true } serde_yaml = { workspace = true }
strum = { workspace = true, features = ["derive"] }
sysinfo = { workspace = true } sysinfo = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
tokio = { workspace = true, features = ["fs", "io-std", "io-util", "macros", "process", "rt-multi-thread", "signal", "sync", "time"] } tokio = { workspace = true, features = ["fs", "io-std", "io-util", "macros", "process", "rt-multi-thread", "signal", "sync", "time"] }

View file

@ -1365,13 +1365,13 @@ export function updateAntennasOnNewNote(note: Note, noteAuthor: Acct, noteMutedU
export function watchNote(watcherId: string, noteAuthorId: string, noteId: string): Promise<void> export function watchNote(watcherId: string, noteAuthorId: string, noteId: string): Promise<void>
export function unwatchNote(watcherId: string, noteId: string): Promise<void> export function unwatchNote(watcherId: string, noteId: string): Promise<void>
export enum PushNotificationKind { export enum PushNotificationKind {
Generic = 'generic', Generic = 0,
Chat = 'chat', Chat = 1,
ReadAllChats = 'readAllChats', ReadAllChats = 2,
ReadAllChatsInTheRoom = 'readAllChatsInTheRoom', ReadAllChatsInTheRoom = 3,
ReadNotifications = 'readNotifications', ReadNotifications = 4,
ReadAllNotifications = 'readAllNotifications', ReadAllNotifications = 5,
Mastodon = 'mastodon' Mastodon = 6
} }
export function sendPushNotification(receiverUserId: string, kind: PushNotificationKind, content: any): Promise<void> export function sendPushNotification(receiverUserId: string, kind: PushNotificationKind, content: any): Promise<void>
export function publishToChannelStream(channelId: string, userId: string): Promise<void> export function publishToChannelStream(channelId: string, userId: string): Promise<void>

View file

@ -4,16 +4,12 @@ use crate::database::{redis_conn, redis_key, RedisConnError};
use redis::{AsyncCommands, RedisError}; use redis::{AsyncCommands, RedisError};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(strum::Display, Debug)] #[cfg_attr(test, derive(Debug))]
pub enum Category { pub enum Category {
#[strum(serialize = "fetchUrl")]
FetchUrl, FetchUrl,
#[strum(serialize = "blocking")]
Block, Block,
#[strum(serialize = "following")]
Follow, Follow,
#[cfg(test)] #[cfg(test)]
#[strum(serialize = "usedOnlyForTesting")]
Test, Test,
} }
@ -32,9 +28,15 @@ fn prefix_key(key: &str) -> String {
redis_key(format!("cache:{}", key)) redis_key(format!("cache:{}", key))
} }
#[inline]
fn categorize(category: Category, key: &str) -> String { fn categorize(category: Category, key: &str) -> String {
format!("{}:{}", category, key) let prefix = match category {
Category::FetchUrl => "fetchUrl",
Category::Block => "blocking",
Category::Follow => "following",
#[cfg(test)]
Category::Test => "usedOnlyForTesting",
};
format!("{}:{}", prefix, key)
} }
#[inline] #[inline]

View file

@ -32,32 +32,18 @@ fn get_client() -> Result<IsahcWebPushClient, Error> {
.cloned()?) .cloned()?)
} }
#[derive(strum::Display, PartialEq)] #[crate::export]
#[crate::export(string_enum = "camelCase")]
pub enum PushNotificationKind { pub enum PushNotificationKind {
#[strum(serialize = "notification")]
Generic, Generic,
#[strum(serialize = "unreadMessagingMessage")]
Chat, Chat,
#[strum(serialize = "readAllMessagingMessages")]
ReadAllChats, ReadAllChats,
#[strum(serialize = "readAllMessagingMessagesOfARoom")]
ReadAllChatsInTheRoom, ReadAllChatsInTheRoom,
#[strum(serialize = "readNotifications")]
ReadNotifications, ReadNotifications,
#[strum(serialize = "readAllNotifications")]
ReadAllNotifications, ReadAllNotifications,
Mastodon, Mastodon,
} }
fn compact_content( fn compact_content(mut content: serde_json::Value) -> Result<serde_json::Value, Error> {
kind: &PushNotificationKind,
mut content: serde_json::Value,
) -> Result<serde_json::Value, Error> {
if kind != &PushNotificationKind::Generic {
return Ok(content);
}
if !content.is_object() { if !content.is_object() {
return Err(Error::InvalidContent("not a JSON object".to_string())); return Err(Error::InvalidContent("not a JSON object".to_string()));
} }
@ -159,24 +145,40 @@ pub async fn send_push_notification(
.all(db) .all(db)
.await?; .await?;
let use_mastodon_api = matches!(kind, PushNotificationKind::Mastodon);
// TODO: refactoring // TODO: refactoring
let payload = if kind == PushNotificationKind::Mastodon { let payload = if use_mastodon_api {
// Leave the `content` as it is // Leave the `content` as it is
serde_json::to_string(content)? serde_json::to_string(content)?
} else { } else {
// Format the `content` passed from the TypeScript backend // Format the `content` passed from the TypeScript backend
// for Firefish push notifications // for Firefish push notifications
let label = match kind {
PushNotificationKind::Generic => "notification",
PushNotificationKind::Chat => "unreadMessagingMessage",
PushNotificationKind::ReadAllChats => "readAllMessagingMessages",
PushNotificationKind::ReadAllChatsInTheRoom => "readAllMessagingMessagesOfARoom",
PushNotificationKind::ReadNotifications => "readNotifications",
PushNotificationKind::ReadAllNotifications => "readAllNotifications",
// unreachable
_ => "unknown",
};
format!( format!(
"{{\"type\":\"{}\",\"userId\":\"{}\",\"dateTime\":{},\"body\":{}}}", "{{\"type\":\"{}\",\"userId\":\"{}\",\"dateTime\":{},\"body\":{}}}",
kind, label,
receiver_user_id, receiver_user_id,
chrono::Utc::now().timestamp_millis(), chrono::Utc::now().timestamp_millis(),
serde_json::to_string(&compact_content(&kind, content.clone())?)? match kind {
PushNotificationKind::Generic =>
serde_json::to_string(&compact_content(content.to_owned())?)?,
_ => serde_json::to_string(&content)?,
}
) )
}; };
tracing::trace!("payload: {}", payload); tracing::trace!("payload: {}", payload);
let encoding = if kind == PushNotificationKind::Mastodon { let encoding = if use_mastodon_api {
ContentEncoding::AesGcm ContentEncoding::AesGcm
} else { } else {
ContentEncoding::Aes128Gcm ContentEncoding::Aes128Gcm
@ -184,13 +186,13 @@ pub async fn send_push_notification(
for subscription in subscriptions.iter() { for subscription in subscriptions.iter() {
if !subscription.send_read_message if !subscription.send_read_message
&& [ && matches!(
PushNotificationKind::ReadAllChats, kind,
PushNotificationKind::ReadAllChatsInTheRoom, PushNotificationKind::ReadAllChats
PushNotificationKind::ReadAllNotifications, | PushNotificationKind::ReadAllChatsInTheRoom
PushNotificationKind::ReadNotifications, | PushNotificationKind::ReadAllNotifications
] | PushNotificationKind::ReadNotifications
.contains(&kind) )
{ {
continue; continue;
} }

View file

@ -10,39 +10,44 @@ use crate::config::CONFIG;
use crate::database::{redis_conn, RedisConnError}; use crate::database::{redis_conn, RedisConnError};
use redis::{AsyncCommands, RedisError}; use redis::{AsyncCommands, RedisError};
#[derive(strum::Display)]
pub enum Stream { pub enum Stream {
#[strum(serialize = "internal")]
Internal, Internal,
#[strum(serialize = "broadcast")]
CustomEmoji, CustomEmoji,
#[strum(to_string = "adminStream:{moderator_id}")] Moderation {
Moderation { moderator_id: String }, moderator_id: String,
#[strum(to_string = "user:{user_id}")] },
User { user_id: String }, User {
#[strum(to_string = "channelStream:{channel_id}")] user_id: String,
Channel { channel_id: String }, },
#[strum(to_string = "noteStream:{note_id}")] Channel {
Note { note_id: String }, channel_id: String,
#[strum(serialize = "notesStream")] },
Note {
note_id: String,
},
Notes, Notes,
#[strum(to_string = "userListStream:{list_id}")] UserList {
UserList { list_id: String }, list_id: String,
#[strum(to_string = "mainStream:{user_id}")] },
Main { user_id: String }, Main {
#[strum(to_string = "driveStream:{user_id}")] user_id: String,
Drive { user_id: String }, },
#[strum(to_string = "antennaStream:{antenna_id}")] Drive {
Antenna { antenna_id: String }, user_id: String,
#[strum(to_string = "messagingStream:{sender_user_id}-{receiver_user_id}")] },
Antenna {
antenna_id: String,
},
Chat { Chat {
sender_user_id: String, sender_user_id: String,
receiver_user_id: String, receiver_user_id: String,
}, },
#[strum(to_string = "messagingStream:{group_id}")] GroupChat {
GroupChat { group_id: String }, group_id: String,
#[strum(to_string = "messagingIndexStream:{user_id}")] },
ChatIndex { user_id: String }, ChatIndex {
user_id: String,
},
} }
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
@ -59,9 +64,29 @@ pub enum Error {
pub async fn publish_to_stream( pub async fn publish_to_stream(
stream: &Stream, stream: &Stream,
kind: Option<String>, kind: Option<&str>,
value: Option<String>, value: Option<String>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let channel = match stream {
Stream::Internal => "internal".to_string(),
Stream::CustomEmoji => "broadcast".to_string(),
Stream::Moderation { moderator_id } => format!("adminStream:{moderator_id}"),
Stream::User { user_id } => format!("user:{user_id}"),
Stream::Channel { channel_id } => format!("channelStream:{channel_id}"),
Stream::Note { note_id } => format!("noteStream:{note_id}"),
Stream::Notes => "notesStream".to_string(),
Stream::UserList { list_id } => format!("userListStream:{list_id}"),
Stream::Main { user_id } => format!("mainStream:{user_id}"),
Stream::Drive { user_id } => format!("driveStream:{user_id}"),
Stream::Antenna { antenna_id } => format!("antennaStream:{antenna_id}"),
Stream::Chat {
sender_user_id,
receiver_user_id,
} => format!("messagingStream:{sender_user_id}-{receiver_user_id}"),
Stream::GroupChat { group_id } => format!("messagingStream:{group_id}"),
Stream::ChatIndex { user_id } => format!("messagingIndexStream:{user_id}"),
};
let message = if let Some(kind) = kind { let message = if let Some(kind) = kind {
format!( format!(
"{{\"type\":\"{}\",\"body\":{}}}", "{{\"type\":\"{}\",\"body\":{}}}",
@ -76,28 +101,9 @@ pub async fn publish_to_stream(
.await? .await?
.publish( .publish(
&CONFIG.host, &CONFIG.host,
format!("{{\"channel\":\"{}\",\"message\":{}}}", stream, message), format!("{{\"channel\":\"{}\",\"message\":{}}}", channel, message),
) )
.await?; .await?;
Ok(()) Ok(())
} }
#[cfg(test)]
mod unit_test {
use super::Stream;
use pretty_assertions::assert_eq;
#[test]
fn channel_to_string() {
assert_eq!(Stream::Internal.to_string(), "internal");
assert_eq!(Stream::CustomEmoji.to_string(), "broadcast");
assert_eq!(
Stream::Moderation {
moderator_id: "9tb42br63g5apjcq".to_string()
}
.to_string(),
"adminStream:9tb42br63g5apjcq"
);
}
}

View file

@ -4,7 +4,7 @@ use crate::service::stream::{publish_to_stream, Error, Stream};
pub async fn publish(antenna_id: String, note: &note::Model) -> Result<(), Error> { pub async fn publish(antenna_id: String, note: &note::Model) -> Result<(), Error> {
publish_to_stream( publish_to_stream(
&Stream::Antenna { antenna_id }, &Stream::Antenna { antenna_id },
Some("note".to_string()), Some("note"),
Some(serde_json::to_string(note)?), Some(serde_json::to_string(note)?),
) )
.await .await

View file

@ -4,7 +4,7 @@ use crate::service::stream::{publish_to_stream, Error, Stream};
pub async fn publish(channel_id: String, user_id: String) -> Result<(), Error> { pub async fn publish(channel_id: String, user_id: String) -> Result<(), Error> {
publish_to_stream( publish_to_stream(
&Stream::Channel { channel_id }, &Stream::Channel { channel_id },
Some("typing".to_string()), Some("typing"),
Some(format!("\"{}\"", user_id)), Some(format!("\"{}\"", user_id)),
) )
.await .await

View file

@ -1,15 +1,10 @@
use crate::service::stream::{publish_to_stream, Error, Stream}; use crate::service::stream::{publish_to_stream, Error, Stream};
#[derive(strum::Display)]
#[crate::export(string_enum = "camelCase")] #[crate::export(string_enum = "camelCase")]
pub enum ChatEvent { pub enum ChatEvent {
#[strum(serialize = "message")]
Message, Message,
#[strum(serialize = "read")]
Read, Read,
#[strum(serialize = "deleted")]
Deleted, Deleted,
#[strum(serialize = "typing")]
Typing, Typing,
} }
@ -23,12 +18,19 @@ pub async fn publish(
kind: ChatEvent, kind: ChatEvent,
object: &serde_json::Value, object: &serde_json::Value,
) -> Result<(), Error> { ) -> Result<(), Error> {
let kind = match kind {
ChatEvent::Message => "message",
ChatEvent::Read => "read",
ChatEvent::Deleted => "deleted",
ChatEvent::Typing => "typing",
};
publish_to_stream( publish_to_stream(
&Stream::Chat { &Stream::Chat {
sender_user_id, sender_user_id,
receiver_user_id, receiver_user_id,
}, },
Some(kind.to_string()), Some(kind),
Some(serde_json::to_string(object)?), Some(serde_json::to_string(object)?),
) )
.await .await

View file

@ -1,11 +1,8 @@
use crate::service::stream::{publish_to_stream, Error, Stream}; use crate::service::stream::{publish_to_stream, Error, Stream};
#[derive(strum::Display)]
#[crate::export(string_enum = "camelCase")] #[crate::export(string_enum = "camelCase")]
pub enum ChatIndexEvent { pub enum ChatIndexEvent {
#[strum(serialize = "message")]
Message, Message,
#[strum(serialize = "read")]
Read, Read,
} }
@ -18,9 +15,14 @@ pub async fn publish(
kind: ChatIndexEvent, kind: ChatIndexEvent,
object: &serde_json::Value, object: &serde_json::Value,
) -> Result<(), Error> { ) -> Result<(), Error> {
let kind = match kind {
ChatIndexEvent::Message => "message",
ChatIndexEvent::Read => "read",
};
publish_to_stream( publish_to_stream(
&Stream::ChatIndex { user_id }, &Stream::ChatIndex { user_id },
Some(kind.to_string()), Some(kind),
Some(serde_json::to_string(object)?), Some(serde_json::to_string(object)?),
) )
.await .await

View file

@ -21,7 +21,7 @@ pub struct PackedEmoji {
pub async fn publish(emoji: &PackedEmoji) -> Result<(), Error> { pub async fn publish(emoji: &PackedEmoji) -> Result<(), Error> {
publish_to_stream( publish_to_stream(
&Stream::CustomEmoji, &Stream::CustomEmoji,
Some("emojiAdded".to_string()), Some("emojiAdded"),
Some(format!("{{\"emoji\":{}}}", serde_json::to_string(emoji)?)), Some(format!("{{\"emoji\":{}}}", serde_json::to_string(emoji)?)),
) )
.await .await

View file

@ -9,9 +9,16 @@ pub async fn publish(
kind: ChatEvent, kind: ChatEvent,
object: &serde_json::Value, object: &serde_json::Value,
) -> Result<(), Error> { ) -> Result<(), Error> {
let kind = match kind {
ChatEvent::Message => "message",
ChatEvent::Read => "read",
ChatEvent::Deleted => "deleted",
ChatEvent::Typing => "typing",
};
publish_to_stream( publish_to_stream(
&Stream::GroupChat { group_id }, &Stream::GroupChat { group_id },
Some(kind.to_string()), Some(kind),
Some(serde_json::to_string(object)?), Some(serde_json::to_string(object)?),
) )
.await .await

View file

@ -15,7 +15,7 @@ pub struct AbuseUserReportLike {
pub async fn publish(moderator_id: String, report: &AbuseUserReportLike) -> Result<(), Error> { pub async fn publish(moderator_id: String, report: &AbuseUserReportLike) -> Result<(), Error> {
publish_to_stream( publish_to_stream(
&Stream::Moderation { moderator_id }, &Stream::Moderation { moderator_id },
Some("newAbuseUserReport".to_string()), Some("newAbuseUserReport"),
Some(serde_json::to_string(report)?), Some(serde_json::to_string(report)?),
) )
.await .await