nmp_threading/resolver.rs
1//! `ParentResolver` — per-NIP plug for the kind-agnostic [`crate::Grouper`].
2//!
3//! `nmp-nip01` impls it over NIP-10 markers (`Nip10Refs`). The grouper
4//! never sees kind numbers or tag conventions — it only asks "what is this
5//! event's parent / root / parent-author / supersession-target?".
6
7use nmp_core::substrate::{EventId, KernelEvent};
8
9use crate::pointer::ThreadPointer;
10
11/// Resolve thread relationships from a `KernelEvent`. Implementors are
12/// per-NIP and stateless — the grouper owns its own state.
13pub trait ParentResolver: Send + Sync + 'static {
14 /// Direct parent — the thing this event replies to. `None` for top-level
15 /// events that aren't part of a thread.
16 fn parent(&self, event: &KernelEvent) -> Option<ThreadPointer>;
17
18 /// Thread root — the original anchor (article, note, URI). For top-level
19 /// replies this may equal `parent`. `None` when the event is itself a
20 /// root or when no root marker is decodable.
21 fn root(&self, event: &KernelEvent) -> Option<ThreadPointer>;
22
23 /// Pubkey of the parent's author, when recoverable from the event's `p`
24 /// tags. Optional — used by UI for "X replied to Y" stitching; the
25 /// grouper itself does not consult this.
26 fn parent_author(&self, event: &KernelEvent) -> Option<String>;
27
28 /// Event id this event supersedes in the block layout, if any.
29 ///
30 /// Used for feed-composition rules where one event should *replace* (not
31 /// extend) another in the displayed block list — the canonical case is
32 /// a NIP-18 repost whose target note is already in the feed. The
33 /// grouper removes the named block before placing this event, so the
34 /// reposted note bumps to the new event's position and renders once.
35 ///
36 /// Default `None`: parent edges, not supersession, is the common case.
37 fn supersedes(&self, _event: &KernelEvent) -> Option<EventId> {
38 None
39 }
40}