Skip to main content

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}