chore (backend-rs): rename modules to more comfortable names

This commit is contained in:
naskya 2024-06-09 21:09:11 +09:00
parent 132af64a6e
commit 679749a2cc
No known key found for this signature in database
GPG key ID: 712D413B3A9FED5C
11 changed files with 193 additions and 133 deletions

View file

@ -419,19 +419,13 @@ export interface ImageSize {
height: number
}
export function getImageSizeFromUrl(url: string): Promise<ImageSize>
export interface PartialNoteToSummarize {
fileIds: Array<string>
text: string | null
cw: string | null
hasPoll: boolean
}
export function getNoteSummary(note: PartialNoteToSummarize): string
export function isQuote(note: Note): boolean
export function isSafeUrl(url: string): boolean
/** Returns the latest Firefish version. */
export function latestVersion(): Promise<string>
export function toMastodonId(firefishId: string): string | null
export function fromMastodonId(mastodonId: string): string | null
export function getNoteSummary(fileIds: Array<string>, text: string | undefined | null, cw: string | undefined | null, hasPoll: boolean): string
/**
* Converts the given text into the cat language.
*

View file

@ -310,7 +310,7 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}
const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, fetchMeta, updateMetaCache, metaToPugArgs, loadConfig, stringToAcct, acctToString, fetchNodeinfo, nodeinfo_2_1, nodeinfo_2_0, updateNodeinfoCache, Protocol, Inbound, Outbound, greet, initializeRustLogger, showServerInfo, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, isQuote, isSafeUrl, latestVersion, toMastodonId, fromMastodonId, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, cpuInfo, cpuUsage, memoryUsage, storageUsage, AntennaSrc, DriveFileUsageHint, MutedNoteReason, NoteVisibility, NotificationType, PageVisibility, PollNoteVisibility, RelayStatus, UserEmojiModPerm, UserProfileFfvisibility, UserProfileMutingNotificationTypes, updateAntennasOnNewNote, updateAntennaCache, watchNote, unwatchNote, PushNotificationKind, sendPushNotification, publishToChannelStream, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToBroadcastStream, publishToGroupChatStream, publishToModerationStream, ChatEvent, getTimestamp, genId, genIdAt, generateSecureRandomString, generateUserToken } = nativeBinding
const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, fetchMeta, updateMetaCache, metaToPugArgs, loadConfig, stringToAcct, acctToString, fetchNodeinfo, nodeinfo_2_1, nodeinfo_2_0, updateNodeinfoCache, Protocol, Inbound, Outbound, greet, initializeRustLogger, showServerInfo, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, isQuote, isSafeUrl, latestVersion, toMastodonId, fromMastodonId, getNoteSummary, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, cpuInfo, cpuUsage, memoryUsage, storageUsage, AntennaSrc, DriveFileUsageHint, MutedNoteReason, NoteVisibility, NotificationType, PageVisibility, PollNoteVisibility, RelayStatus, UserEmojiModPerm, UserProfileFfvisibility, UserProfileMutingNotificationTypes, updateAntennasOnNewNote, updateAntennaCache, watchNote, unwatchNote, PushNotificationKind, sendPushNotification, publishToChannelStream, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToBroadcastStream, publishToGroupChatStream, publishToModerationStream, ChatEvent, getTimestamp, genId, genIdAt, generateSecureRandomString, generateUserToken } = nativeBinding
module.exports.SECOND = SECOND
module.exports.MINUTE = MINUTE
@ -349,12 +349,12 @@ module.exports.sqlLikeEscape = sqlLikeEscape
module.exports.safeForSql = safeForSql
module.exports.formatMilliseconds = formatMilliseconds
module.exports.getImageSizeFromUrl = getImageSizeFromUrl
module.exports.getNoteSummary = getNoteSummary
module.exports.isQuote = isQuote
module.exports.isSafeUrl = isSafeUrl
module.exports.latestVersion = latestVersion
module.exports.toMastodonId = toMastodonId
module.exports.fromMastodonId = fromMastodonId
module.exports.getNoteSummary = getNoteSummary
module.exports.nyaify = nyaify
module.exports.hashPassword = hashPassword
module.exports.verifyPassword = verifyPassword

View file

@ -1,4 +1,4 @@
use crate::misc::get_note_all_texts::all_texts;
use crate::misc::note::elaborate;
use once_cell::sync::Lazy;
use regex::Regex;
use sea_orm::DbErr;
@ -59,7 +59,7 @@ pub async fn check_word_mute(
Ok(false)
} else {
Ok(check_word_mute_impl(
&all_texts!(note, true).await?,
&elaborate!(note, true).await?,
muted_words,
muted_patterns,
))

View file

@ -1,93 +0,0 @@
use serde::Deserialize;
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
#[crate::export(object)]
pub struct PartialNoteToSummarize {
pub file_ids: Vec<String>,
pub text: Option<String>,
pub cw: Option<String>,
pub has_poll: bool,
}
#[crate::export]
pub fn get_note_summary(note: PartialNoteToSummarize) -> String {
let mut buf: Vec<String> = vec![];
if let Some(cw) = note.cw {
buf.push(cw)
} else if let Some(text) = note.text {
buf.push(text)
}
match note.file_ids.len() {
0 => (),
1 => buf.push("📎".to_string()),
n => buf.push(format!("📎 ({})", n)),
};
if note.has_poll {
buf.push("📊".to_string())
}
buf.join(" ")
}
#[cfg(test)]
mod unit_test {
use super::{get_note_summary, PartialNoteToSummarize};
use pretty_assertions::assert_eq;
#[test]
fn test_note_summary() {
let note = PartialNoteToSummarize {
file_ids: vec![],
text: Some("Hello world!".to_string()),
cw: None,
has_poll: false,
};
assert_eq!(get_note_summary(note), "Hello world!");
let note_with_cw = PartialNoteToSummarize {
file_ids: vec![],
text: Some("Hello world!".to_string()),
cw: Some("Content warning".to_string()),
has_poll: false,
};
assert_eq!(get_note_summary(note_with_cw), "Content warning");
let note_with_file_and_cw = PartialNoteToSummarize {
file_ids: vec!["9s7fmcqogiq4igin".to_string()],
text: None,
cw: Some("Selfie, no ec".to_string()),
has_poll: false,
};
assert_eq!(get_note_summary(note_with_file_and_cw), "Selfie, no ec 📎");
let note_with_files_only = PartialNoteToSummarize {
file_ids: vec![
"9s7fmcqogiq4igin".to_string(),
"9s7qrld5u14cey98".to_string(),
"9s7gebs5zgts4kca".to_string(),
"9s5z3e4vefqd29ee".to_string(),
],
text: None,
cw: None,
has_poll: false,
};
assert_eq!(get_note_summary(note_with_files_only), "📎 (4)");
let note_all = PartialNoteToSummarize {
file_ids: vec![
"9s7fmcqogiq4igin".to_string(),
"9s7qrld5u14cey98".to_string(),
"9s7gebs5zgts4kca".to_string(),
"9s5z3e4vefqd29ee".to_string(),
],
text: Some("Hello world!".to_string()),
cw: Some("Content warning".to_string()),
has_poll: true,
};
assert_eq!(get_note_summary(note_all), "Content warning 📎 (4) 📊");
}
}

View file

@ -7,12 +7,11 @@ pub mod emoji;
pub mod escape_sql;
pub mod format_milliseconds;
pub mod get_image_size;
pub mod get_note_all_texts;
pub mod get_note_summary;
pub mod is_quote;
pub mod is_safe_url;
pub mod latest_version;
pub mod mastodon_id;
pub mod note;
pub mod nyaify;
pub mod password;
pub mod reaction;

View file

@ -6,7 +6,7 @@ use sea_orm::{prelude::*, QuerySelect};
/// Returns [`Vec<String>`] containing the post text, content warning,
/// those of the "parent" (replied/quoted) posts, and alt texts of attached files.
/// Consider using [`all_texts`] macro instead
/// Consider using [`elaborate`] macro instead
/// when dealing with a note ([`note::Model`])-like instance.
///
/// # Arguments
@ -14,7 +14,7 @@ use sea_orm::{prelude::*, QuerySelect};
/// * `file_ids` : IDs of attached files ([`drive_file::Model`])
/// * `text`, `cw`, `renote_id`, `reply_id` : note ([`note::Model`]) fields
/// * `include_parent` : whether to take the reply-to post and quoted post into account
pub async fn all_texts_impl(
pub async fn elaborate_impl(
file_ids: &[String],
text: Option<String>,
cw: Option<String>,
@ -87,15 +87,15 @@ pub async fn all_texts_impl(
/// # Caveats
///
/// The `note_like` argument should not contain function calls
/// (e.g., `all_texts!(note.clone(), false)`)
/// (e.g., `elaborate!(note.clone(), false)`)
/// since the function will be called multiple times after macro expansion.
///
/// # Examples
///
/// ```
/// # use backend_rs::misc::get_note_all_texts::all_texts;
/// # use backend_rs::misc::note::elaborate;
/// // note-like struct
/// struct SomeNoteLikeStruct {
/// struct NoteLike {
/// // required fields
/// file_ids: Vec<String>,
/// text: Option<String>,
@ -107,18 +107,19 @@ pub async fn all_texts_impl(
/// extra_field_2: Vec<String>,
/// }
///
/// async fn all_texts_from_some_note_like_struct(
/// note_like: &SomeNoteLikeStruct,
/// include_parent: bool,
/// ) -> Result<Vec<String>, sea_orm::DbErr> {
/// all_texts!(note_like, include_parent).await
/// async fn print_all_related_texts(
/// note: &NoteLike
/// ) -> Result<(), sea_orm::DbErr> {
/// let all_texts = elaborate!(note, true).await?;
/// all_texts.iter().map(|text| println!("{}", text));
/// Ok(())
/// }
/// ```
#[doc(hidden)] // hide the macro in the top doc page
#[macro_export]
macro_rules! all_texts {
macro_rules! elaborate {
($note_like:expr, $include_parent:expr) => {
$crate::misc::get_note_all_texts::all_texts_impl(
$crate::misc::note::elaborate::elaborate_impl(
&$note_like.file_ids,
$note_like.text.clone(),
$note_like.cw.clone(),
@ -128,5 +129,6 @@ macro_rules! all_texts {
)
};
}
#[doc(inline)] // show the macro in the module doc page
pub use all_texts;
pub use elaborate;

View file

@ -0,0 +1,5 @@
pub use elaborate::elaborate;
pub use summarize::summarize;
pub mod elaborate;
pub mod summarize;

View file

@ -0,0 +1,145 @@
#[crate::export(js_name = "getNoteSummary")]
pub fn summarize_impl(
file_ids: &[String],
text: Option<String>,
cw: Option<String>,
has_poll: bool,
) -> String {
let mut buf: Vec<String> = vec![];
if let Some(cw) = cw {
buf.push(cw)
} else if let Some(text) = text {
buf.push(text)
}
match file_ids.len() {
0 => (),
1 => buf.push("📎".to_string()),
n => buf.push(format!("📎 ({})", n)),
};
if has_poll {
buf.push("📊".to_string())
}
buf.join(" ")
}
/// Returns the summary of a post, which can be used to display posts in small spaces
/// such as push notifications.
///
/// # Arguments
///
/// * `note_like` : a note ([`crate::model::entity::note::Model`])-like instance containing
/// `file_ids`, `text`, `cw`, `has_poll` fields
///
/// # Caveats
///
/// The `note_like` argument should not contain function calls
/// (e.g., `summarize!(note.clone())`)
/// since the function will be called multiple times after macro expansion.
///
/// # Examples
///
/// ```
/// # use backend_rs::misc::note::summarize;
/// // note-like struct
/// struct NoteLike {
/// // required fields
/// file_ids: Vec<String>,
/// text: Option<String>,
/// cw: Option<String>,
/// has_poll: bool,
/// // arbitrary extra fields
/// renote_id: Option<String>,
/// reply_id: Option<String>,
/// extra_field_1: u32,
/// extra_field_2: Vec<String>,
/// }
///
/// fn print_note_summary(note: &NoteLike) {
/// println!("{}", summarize!(note));
/// }
/// ```
#[doc(hidden)] // hide the macro in the top doc page
#[macro_export]
macro_rules! summarize {
($note_like:expr) => {
$crate::misc::note::summarize::summarize_impl(
&$note_like.file_ids,
$note_like.text.to_owned(),
$note_like.cw.to_owned(),
$note_like.has_poll.to_owned(),
)
};
}
#[doc(inline)] // show the macro in the module doc page
pub use summarize;
#[cfg(test)]
mod unit_test {
use super::summarize;
use pretty_assertions::assert_eq;
struct NoteLike {
file_ids: Vec<String>,
text: Option<String>,
cw: Option<String>,
has_poll: bool,
}
#[test]
fn summarize() {
let note = NoteLike {
file_ids: vec![],
text: Some("Hello world!".to_string()),
cw: None,
has_poll: false,
};
assert_eq!(summarize!(note), "Hello world!");
let note_with_cw = NoteLike {
file_ids: vec![],
text: Some("Hello world!".to_string()),
cw: Some("Content warning".to_string()),
has_poll: false,
};
assert_eq!(summarize!(note_with_cw), "Content warning");
let note_with_file_and_cw = NoteLike {
file_ids: vec!["9s7fmcqogiq4igin".to_string()],
text: None,
cw: Some("Selfie, no ec".to_string()),
has_poll: false,
};
assert_eq!(summarize!(note_with_file_and_cw), "Selfie, no ec 📎");
let note_with_files_only = NoteLike {
file_ids: vec![
"9s7fmcqogiq4igin".to_string(),
"9s7qrld5u14cey98".to_string(),
"9s7gebs5zgts4kca".to_string(),
"9s5z3e4vefqd29ee".to_string(),
],
text: None,
cw: None,
has_poll: false,
};
assert_eq!(summarize!(note_with_files_only), "📎 (4)");
let note_all = NoteLike {
file_ids: vec![
"9s7fmcqogiq4igin".to_string(),
"9s7qrld5u14cey98".to_string(),
"9s7gebs5zgts4kca".to_string(),
"9s5z3e4vefqd29ee".to_string(),
],
text: Some("Hello world!".to_string()),
cw: Some("Content warning".to_string()),
has_poll: true,
};
assert_eq!(summarize!(note_all), "Content warning 📎 (4) 📊");
}
}

View file

@ -1,7 +1,7 @@
use crate::{
database::{cache, redis_conn, redis_key, RedisConnError},
federation::acct::Acct,
misc::get_note_all_texts::all_texts,
misc::note::elaborate,
model::entity::note,
service::{
antenna,
@ -41,7 +41,7 @@ pub async fn update_antennas_on_new_note(
note_author: &Acct,
note_muted_users: &[String],
) -> Result<(), Error> {
let note_all_texts = all_texts!(note, false).await?;
let note_all_texts = elaborate!(note, false).await?;
// TODO: do this in parallel
for antenna in antenna::cache::get().await?.iter() {

View file

@ -1,12 +1,10 @@
use crate::{
config::local_server_info,
database::db_conn,
misc::get_note_summary::{get_note_summary, PartialNoteToSummarize},
model::entity::sw_subscription,
util::http_client,
config::local_server_info, database::db_conn, misc::note::summarize,
model::entity::sw_subscription, util::http_client,
};
use once_cell::sync::OnceCell;
use sea_orm::prelude::*;
use serde::Deserialize;
use web_push::*;
#[derive(thiserror::Error, Debug)]
@ -72,8 +70,18 @@ fn compact_content(mut content: serde_json::Value) -> Result<serde_json::Value,
));
}
let note_like: PartialNoteToSummarize = serde_json::from_value(note.clone())?;
let text = get_note_summary(note_like);
// TODO: get rid of this struct
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct PartialNote {
file_ids: Vec<String>,
text: Option<String>,
cw: Option<String>,
has_poll: bool,
}
let note_like: PartialNote = serde_json::from_value(note.clone())?;
let text = summarize!(note_like);
let note_object = note.as_object_mut().unwrap();

View file

@ -540,7 +540,7 @@ router.get("/notes/:note", async (ctx, next) => {
await Users.findOneByOrFail({ id: note.userId }),
),
// TODO: Let locale changeable by instance setting
summary: getNoteSummary(note),
summary: getNoteSummary(note.fileIds, note.text, note.cw, note.hasPoll),
});
ctx.set("Cache-Control", "public, max-age=15");
@ -562,19 +562,19 @@ router.get("/posts/:note", async (ctx, next) => {
visibility: In(["public", "home"]),
});
if (note) {
const _note = await Notes.pack(note);
if (note != null) {
const packedNote = await Notes.pack(note);
const profile = await UserProfiles.findOneByOrFail({ userId: note.userId });
const meta = await fetchMeta();
await ctx.render("note", {
...metaToPugArgs(meta),
note: _note,
note: packedNote,
profile,
avatarUrl: await Users.getAvatarUrl(
await Users.findOneByOrFail({ id: note.userId }),
),
// TODO: Let locale changeable by instance setting
summary: getNoteSummary(_note),
summary: getNoteSummary(note.fileIds, note.text, note.cw, note.hasPoll),
});
ctx.set("Cache-Control", "public, max-age=15");