refactor(stitched): Move stitcher into its own crate

This commit is contained in:
Ginger 2026-01-22 13:44:25 -05:00
parent 9210ee2b42
commit 4da955438e
No known key found for this signature in database
35 changed files with 43 additions and 21 deletions

10
Cargo.lock generated
View file

@ -1216,7 +1216,6 @@ dependencies = [
"log",
"loole",
"lru-cache",
"peg",
"rand 0.8.5",
"recaptcha-verify",
"regex",
@ -5136,6 +5135,15 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "stitcher"
version = "0.5.3"
dependencies = [
"indexmap",
"itertools 0.14.0",
"peg",
]
[[package]]
name = "strict"
version = "0.2.0"

View file

@ -126,6 +126,3 @@ sd-notify.optional = true
[lints]
workspace = true
[dev-dependencies]
peg = "0.8.5"

View file

@ -4,7 +4,6 @@ mod build;
mod create;
mod data;
mod redact;
mod stitcher;
use std::{fmt::Write, sync::Arc};

18
src/stitcher/Cargo.toml Normal file
View file

@ -0,0 +1,18 @@
[package]
name = "stitcher"
description = "An implementation of stitched ordering (https://codeberg.org/andybalaam/stitched-order)"
edition.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
version.workspace = true
[lib]
path = "mod.rs"
[dependencies]
indexmap.workspace = true
itertools.workspace = true
[dev-dependencies]
peg = "0.8.5"

View file

@ -7,7 +7,7 @@ use super::{Batch, Gap, OrderKey, StitchedItem, StitcherBackend};
/// Updates to a gap in the stitched order.
#[derive(Debug)]
pub(super) struct GapUpdate<'id, K: OrderKey> {
pub struct GapUpdate<'id, K: OrderKey> {
/// The opaque key of the gap to update.
pub key: K,
/// The new contents of the gap. If this is empty, the gap should be
@ -20,7 +20,7 @@ pub(super) struct GapUpdate<'id, K: OrderKey> {
/// Updates to the stitched order.
#[derive(Debug)]
pub(super) struct OrderUpdates<'id, K: OrderKey> {
pub struct OrderUpdates<'id, K: OrderKey> {
/// Updates to individual gaps. The items inserted by these updates _should
/// not_ be synchronized to clients.
pub gap_updates: Vec<GapUpdate<'id, K>>,
@ -34,18 +34,18 @@ pub(super) struct OrderUpdates<'id, K: OrderKey> {
/// The stitcher, which implements the stitched ordering algorithm.
/// Its primary method is [`Stitcher::stitch`].
pub(super) struct Stitcher<'backend, B: StitcherBackend> {
pub struct Stitcher<'backend, B: StitcherBackend> {
backend: &'backend B,
}
impl<B: StitcherBackend> Stitcher<'_, B> {
/// Create a new [`Stitcher`] given a [`StitcherBackend`].
pub(super) fn new(backend: &B) -> Stitcher<'_, B> { Stitcher { backend } }
pub fn new(backend: &B) -> Stitcher<'_, B> { Stitcher { backend } }
/// Given a [`Batch`], compute the [`OrderUpdates`] which should be made to
/// the stitched order to incorporate that batch. It is the responsibility
/// of the caller to apply the updates.
pub(super) fn stitch<'id>(&self, batch: &Batch<'id>) -> OrderUpdates<'id, B::Key> {
pub fn stitch<'id>(&self, batch: &Batch<'id>) -> OrderUpdates<'id, B::Key> {
let mut gap_updates = Vec::new();
let mut events_added_to_gaps: HashSet<&'id str> = HashSet::new();

View file

@ -2,16 +2,17 @@ use std::{cmp::Ordering, collections::HashSet};
use indexmap::IndexMap;
pub(super) mod algorithm;
pub mod algorithm;
#[cfg(test)]
mod test;
pub use algorithm::*;
/// A gap in the stitched order.
pub(super) type Gap = HashSet<String>;
pub type Gap = HashSet<String>;
/// An item in the stitched order.
#[derive(Debug)]
pub(super) enum StitchedItem<'id> {
pub enum StitchedItem<'id> {
/// A single event.
Event(&'id str),
/// A gap representing one or more missing events.
@ -20,12 +21,12 @@ pub(super) enum StitchedItem<'id> {
/// An opaque key returned by a [`StitcherBackend`] to identify an item in its
/// order.
pub(super) trait OrderKey: Eq + Clone {}
pub trait OrderKey: Eq + Clone {}
impl<T: Eq + Clone> OrderKey for T {}
/// A trait providing read-only access to an existing stitched order.
pub(super) trait StitcherBackend {
pub trait StitcherBackend {
type Key: OrderKey;
/// Return all gaps containing one or more events listed in `events`.
@ -39,7 +40,7 @@ pub(super) trait StitcherBackend {
}
/// An ordered map from an event ID to its `prev_events`.
pub(super) type EventEdges<'id> = IndexMap<&'id str, HashSet<&'id str>>;
pub type EventEdges<'id> = IndexMap<&'id str, HashSet<&'id str>>;
/// Information about the `prev_events` of an event.
/// This struct does not store the ID of the event itself.
@ -56,17 +57,17 @@ struct EventPredecessors<'id> {
/// A batch of events to be inserted into the stitched order.
#[derive(Debug)]
pub(super) struct Batch<'id> {
pub struct Batch<'id> {
events: IndexMap<&'id str, EventPredecessors<'id>>,
}
impl<'id> Batch<'id> {
/// Create a new [`Batch`] from an [`EventEdges`].
pub(super) fn from_edges<'edges>(edges: &EventEdges<'edges>) -> Batch<'edges> {
pub fn from_edges<'edges>(edges: &EventEdges<'edges>) -> Batch<'edges> {
let mut events = IndexMap::new();
for (event, prev_events) in edges {
let predecessor_set = Self::find_predecessor_set(event, &edges);
let predecessor_set = Self::find_predecessor_set(event, edges);
events.insert(*event, EventPredecessors {
prev_events: prev_events.clone(),

View file

@ -3,7 +3,6 @@ use std::sync::atomic::{AtomicU64, Ordering};
use itertools::Itertools;
use super::{algorithm::*, *};
use crate::rooms::timeline::stitcher::algorithm::Stitcher;
mod parser;

View file

@ -2,7 +2,7 @@ use std::collections::HashSet;
use indexmap::IndexMap;
use crate::rooms::timeline::stitcher::StitchedItem;
use super::StitchedItem;
pub(super) type TestEventId<'id> = &'id str;