//! Database-free event processing //! //! This module converts parsed events into `ProcessedEvent` structures //! containing all data needed for database operations, without touching //! the database itself. Workers become pure functions. use super::handlers; use crate::database_writer::{EventSource, ProcessedEvent}; use crate::relay::types::RecordTypes; use ipld_core::cid::Cid; /// Process a record operation into database operations (no I/O) /// /// This is the database-free equivalent of `index_op_impl`. /// Instead of writing to the database, it returns a list of operations to perform. /// /// # Parameters /// - `actor_id`: The resolved actor ID for the repo (caller must resolve via ensure_actor_id) /// - `resolved_actor_ids`: All resolved actor IDs from the record (subject, parent, root, quoted, mentioned) /// /// NOTE: With Tap filtering, all events are from allowlisted DIDs. /// We always enqueue related posts (replies/quotes) for fetching to ensure completeness. #[expect(clippy::too_many_arguments, reason = "Comprehensive record processing requires multiple resolved IDs and metadata")] pub fn process_record_to_operations( repo: &str, actor_id: i32, resolved_actor_ids: super::ResolvedActorIds, cid: Cid, record: RecordTypes, at_uri: String, rkey: String, source: EventSource, ) -> ProcessedEvent { let subject_actor_id = resolved_actor_ids.subject_actor_id; // Create context for handlers that need it let ctx = handlers::RecordContext { repo: repo.to_string(), actor_id, subject_actor_id, via_repost_key: resolved_actor_ids.via_repost_key, service_actor_id: resolved_actor_ids.service_actor_id, cid, at_uri: at_uri.clone(), rkey: rkey.clone(), source, }; let operations = match record { RecordTypes::AppBskyActorProfile(record) => handlers::handle_profile(&ctx, record), RecordTypes::AppBskyActorStatus(record) => handlers::handle_status(&ctx, record), RecordTypes::AppBskyFeedGenerator(record) => handlers::handle_feedgen(&ctx, record), RecordTypes::AppBskyFeedLike(record) => handlers::handle_like(&ctx, record), RecordTypes::AppBskyFeedPost(record) => { let post_ctx = handlers::PostContext { ctx: ctx.clone(), parent_author_actor_id: resolved_actor_ids.parent_author_actor_id, root_author_actor_id: resolved_actor_ids.root_author_actor_id, quoted_author_actor_id: resolved_actor_ids.quoted_author_actor_id, mentioned_actor_ids: resolved_actor_ids.mentioned_actor_ids, }; handlers::handle_post(&post_ctx, record) }, RecordTypes::AppBskyFeedPostgate(record) => handlers::handle_postgate(&ctx, record), RecordTypes::AppBskyFeedRepost(record) => handlers::handle_repost(&ctx, record), RecordTypes::AppBskyFeedThreadgate(record) => handlers::handle_threadgate(&ctx, record), RecordTypes::AppBskyGraphBlock(record) => handlers::handle_block(&ctx, record), RecordTypes::AppBskyGraphFollow(record) => handlers::handle_follow(&ctx, record), RecordTypes::AppBskyGraphList(record) => handlers::handle_list(&ctx, record), RecordTypes::AppBskyGraphListBlock(record) => handlers::handle_list_block(&ctx, record), RecordTypes::AppBskyGraphListItem(record) => handlers::handle_list_item(&ctx, record), RecordTypes::AppBskyGraphStarterPack(record) => handlers::handle_starterpack(&ctx, record), RecordTypes::AppBskyGraphVerification(record) => handlers::handle_verification(&ctx, record), RecordTypes::AppBskyLabelerService(record) => handlers::handle_labeler(&ctx, record), RecordTypes::AppBskyNotificationDeclaration(record) => { handlers::handle_notification_declaration(&ctx, record) } RecordTypes::ChatBskyActorDeclaration(record) => { handlers::handle_chat_declaration(&ctx, record) } RecordTypes::CommunityLexiconBookmark(record) => handlers::handle_bookmark(&ctx, record), RecordTypes::FmTealAlpaActorStatus(record) => handlers::handle_alpa_status(&ctx, record), RecordTypes::Unknown(_value) => { // Unknown record types are skipped vec![] } }; ProcessedEvent { operations, cursor: None, source, } }