1//! Database-free event processing
2//!
3//! This module converts parsed events into `ProcessedEvent` structures
4//! containing all data needed for database operations, without touching
5//! the database itself. Workers become pure functions.
6
7use super::handlers;
8use crate::database_writer::{EventSource, ProcessedEvent};
9use crate::relay::types::RecordTypes;
10use ipld_core::cid::Cid;
11
12/// Process a record operation into database operations (no I/O)
13///
14/// This is the database-free equivalent of `index_op_impl`.
15/// Instead of writing to the database, it returns a list of operations to perform.
16///
17/// # Parameters
18/// - `actor_id`: The resolved actor ID for the repo (caller must resolve via ensure_actor_id)
19/// - `resolved_actor_ids`: All resolved actor IDs from the record (subject, parent, root, quoted, mentioned)
20///
21/// NOTE: With Tap filtering, all events are from allowlisted DIDs.
22/// We always enqueue related posts (replies/quotes) for fetching to ensure completeness.
23#[expect(clippy::too_many_arguments, reason = "Comprehensive record processing requires multiple resolved IDs and metadata")]
24pub fn process_record_to_operations(
25 repo: &str,
26 actor_id: i32,
27 resolved_actor_ids: super::ResolvedActorIds,
28 cid: Cid,
29 record: RecordTypes,
30 at_uri: String,
31 rkey: String,
32 source: EventSource,
33) -> ProcessedEvent {
34 let subject_actor_id = resolved_actor_ids.subject_actor_id;
35
36 // Create context for handlers that need it
37 let ctx = handlers::RecordContext {
38 repo: repo.to_string(),
39 actor_id,
40 subject_actor_id,
41 via_repost_key: resolved_actor_ids.via_repost_key,
42 service_actor_id: resolved_actor_ids.service_actor_id,
43 cid,
44 at_uri: at_uri.clone(),
45 rkey: rkey.clone(),
46 source,
47 };
48
49 let operations = match record {
50 RecordTypes::AppBskyActorProfile(record) => handlers::handle_profile(&ctx, record),
51 RecordTypes::AppBskyActorStatus(record) => handlers::handle_status(&ctx, record),
52 RecordTypes::AppBskyFeedGenerator(record) => handlers::handle_feedgen(&ctx, record),
53 RecordTypes::AppBskyFeedLike(record) => handlers::handle_like(&ctx, record),
54 RecordTypes::AppBskyFeedPost(record) => {
55 let post_ctx = handlers::PostContext {
56 ctx: ctx.clone(),
57 parent_author_actor_id: resolved_actor_ids.parent_author_actor_id,
58 root_author_actor_id: resolved_actor_ids.root_author_actor_id,
59 quoted_author_actor_id: resolved_actor_ids.quoted_author_actor_id,
60 mentioned_actor_ids: resolved_actor_ids.mentioned_actor_ids,
61 };
62 handlers::handle_post(&post_ctx, record)
63 },
64 RecordTypes::AppBskyFeedPostgate(record) => handlers::handle_postgate(&ctx, record),
65 RecordTypes::AppBskyFeedRepost(record) => handlers::handle_repost(&ctx, record),
66 RecordTypes::AppBskyFeedThreadgate(record) => handlers::handle_threadgate(&ctx, record),
67 RecordTypes::AppBskyGraphBlock(record) => handlers::handle_block(&ctx, record),
68 RecordTypes::AppBskyGraphFollow(record) => handlers::handle_follow(&ctx, record),
69 RecordTypes::AppBskyGraphList(record) => handlers::handle_list(&ctx, record),
70 RecordTypes::AppBskyGraphListBlock(record) => handlers::handle_list_block(&ctx, record),
71 RecordTypes::AppBskyGraphListItem(record) => handlers::handle_list_item(&ctx, record),
72 RecordTypes::AppBskyGraphStarterPack(record) => handlers::handle_starterpack(&ctx, record),
73 RecordTypes::AppBskyGraphVerification(record) => handlers::handle_verification(&ctx, record),
74 RecordTypes::AppBskyLabelerService(record) => handlers::handle_labeler(&ctx, record),
75 RecordTypes::AppBskyNotificationDeclaration(record) => {
76 handlers::handle_notification_declaration(&ctx, record)
77 }
78 RecordTypes::ChatBskyActorDeclaration(record) => {
79 handlers::handle_chat_declaration(&ctx, record)
80 }
81 RecordTypes::CommunityLexiconBookmark(record) => handlers::handle_bookmark(&ctx, record),
82 RecordTypes::FmTealAlpaActorStatus(record) => handlers::handle_alpa_status(&ctx, record),
83 RecordTypes::Unknown(_value) => {
84 // Unknown record types are skipped
85 vec![]
86 }
87 };
88
89 ProcessedEvent {
90 operations,
91 cursor: None,
92 source,
93 }
94}