refactor (backend-rs): create all_texts macro

This commit is contained in:
sup39 2024-06-09 16:34:58 +08:00
parent 09d975bdce
commit 132af64a6e
No known key found for this signature in database
GPG key ID: 111C00916C1641E5
3 changed files with 87 additions and 59 deletions

View file

@ -59,15 +59,7 @@ pub async fn check_word_mute(
Ok(false) Ok(false)
} else { } else {
Ok(check_word_mute_impl( Ok(check_word_mute_impl(
&all_texts( &all_texts!(note, true).await?,
note.file_ids,
note.text,
note.cw,
note.renote_id,
note.reply_id,
true,
)
.await?,
muted_words, muted_words,
muted_patterns, muted_patterns,
)) ))

View file

@ -6,18 +6,20 @@ use sea_orm::{prelude::*, QuerySelect};
/// Returns [`Vec<String>`] containing the post text, content warning, /// Returns [`Vec<String>`] containing the post text, content warning,
/// those of the "parent" (replied/quoted) posts, and alt texts of attached files. /// those of the "parent" (replied/quoted) posts, and alt texts of attached files.
/// Consider using [`all_texts`] macro instead
/// when dealing with a note ([`note::Model`])-like instance.
/// ///
/// # Arguments /// # Arguments
/// ///
/// * `file_ids` : IDs of attached files ([`drive_file::Model`]) /// * `file_ids` : IDs of attached files ([`drive_file::Model`])
/// * `text`, `cw`, `renote_id`, `reply_id` : note ([`note::Model`]) fields /// * `text`, `cw`, `renote_id`, `reply_id` : note ([`note::Model`]) fields
/// * `include_parent` : whether to take the reply-to post and quoted post into account /// * `include_parent` : whether to take the reply-to post and quoted post into account
pub async fn all_texts( pub async fn all_texts_impl(
file_ids: Vec<String>, file_ids: &[String],
text: Option<String>, text: Option<String>,
cw: Option<String>, cw: Option<String>,
renote_id: Option<String>, renote_id: &Option<String>,
reply_id: Option<String>, reply_id: &Option<String>,
include_parent: bool, include_parent: bool,
) -> Result<Vec<String>, DbErr> { ) -> Result<Vec<String>, DbErr> {
let db = db_conn().await?; let db = db_conn().await?;
@ -26,10 +28,10 @@ pub async fn all_texts(
let is_renote = text.is_none(); let is_renote = text.is_none();
if let Some(text) = text { if let Some(text) = text {
texts.push(text) texts.push(text);
} }
if let Some(cw) = cw { if let Some(cw) = cw {
texts.push(cw) texts.push(cw);
} }
texts.extend( texts.extend(
@ -44,45 +46,87 @@ pub async fn all_texts(
.flatten(), .flatten(),
); );
if renote_id.is_some() && (include_parent || is_renote) { let mut query_note_ids = Vec::<&str>::with_capacity(2);
let renote_id = renote_id.unwrap(); if let Some(renote_id) = renote_id {
if include_parent || is_renote {
if let Some((text, cw)) = note::Entity::find_by_id(&renote_id) query_note_ids.push(renote_id);
.select_only()
.columns([note::Column::Text, note::Column::Cw])
.into_tuple::<(Option<String>, Option<String>)>()
.one(db)
.await?
{
if let Some(t) = text {
texts.push(t);
}
if let Some(c) = cw {
texts.push(c);
}
} else {
tracing::warn!("nonexistent renote id: {}", renote_id);
} }
} }
if let Some(reply_id) = reply_id {
if include_parent && reply_id.is_some() { if include_parent {
if let Some((text, cw)) = note::Entity::find_by_id(reply_id.as_ref().unwrap()) query_note_ids.push(reply_id);
.select_only()
.columns([note::Column::Text, note::Column::Cw])
.into_tuple::<(Option<String>, Option<String>)>()
.one(db)
.await?
{
if let Some(t) = text {
texts.push(t);
}
if let Some(c) = cw {
texts.push(c);
}
} else {
tracing::warn!("nonexistent reply id: {}", reply_id.unwrap());
} }
} }
if !query_note_ids.is_empty() {
texts.extend(
note::Entity::find()
.filter(note::Column::Id.is_in(query_note_ids))
.select_only()
.columns([note::Column::Text, note::Column::Cw])
.into_tuple::<(Option<String>, Option<String>)>()
.one(db)
.await?
.into_iter()
.flat_map(|(text, cw)| [text, cw])
.flatten(),
);
}
Ok(texts) Ok(texts)
} }
/// Returns [`Vec<String>`] containing the post text, content warning,
/// those of the "parent" (replied/quoted) posts, and alt texts of attached files.
///
/// # Arguments
///
/// * `note_like` : a note ([`note::Model`])-like instance containing
/// `file_ids`, `text`, `cw`, `renote_id`, `reply_id` fields
/// * `include_parent` ([bool]) : whether to take the reply-to post and quoted post into account
///
/// # Caveats
///
/// The `note_like` argument should not contain function calls
/// (e.g., `all_texts!(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;
/// // note-like struct
/// struct SomeNoteLikeStruct {
/// // required fields
/// file_ids: Vec<String>,
/// text: Option<String>,
/// cw: Option<String>,
/// renote_id: Option<String>,
/// reply_id: Option<String>,
/// // arbitrary extra fields
/// extra_field_1: u32,
/// 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
/// }
/// ```
#[doc(hidden)] // hide the macro in the top doc page
#[macro_export]
macro_rules! all_texts {
($note_like:expr, $include_parent:expr) => {
$crate::misc::get_note_all_texts::all_texts_impl(
&$note_like.file_ids,
$note_like.text.clone(),
$note_like.cw.clone(),
&$note_like.renote_id,
&$note_like.reply_id,
$include_parent,
)
};
}
#[doc(inline)] // show the macro in the module doc page
pub use all_texts;

View file

@ -41,15 +41,7 @@ pub async fn update_antennas_on_new_note(
note_author: &Acct, note_author: &Acct,
note_muted_users: &[String], note_muted_users: &[String],
) -> Result<(), Error> { ) -> Result<(), Error> {
let note_all_texts = all_texts( let note_all_texts = all_texts!(note, false).await?;
note.file_ids.to_owned(),
note.text.to_owned(),
note.cw.to_owned(),
note.renote_id.to_owned(),
note.reply_id.to_owned(),
false,
)
.await?;
// TODO: do this in parallel // TODO: do this in parallel
for antenna in antenna::cache::get().await?.iter() { for antenna in antenna::cache::get().await?.iter() {