thread
Source: crates/lvcs-doc/src/kinds/thread/mod.rs.
/// A thread's id: the `HASH` of the canonical CBOR array (anchor, opener
/// key, open time): the anchor as a nested [`ThreadAnchor`] value (not a
/// byte string), the key as a byte string, and the open time as the opening
/// request's `Origin.time_ms`. Written in `ID_ENCODING`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct ThreadId(#[serde(with = "crate::cbor::fixed")] pub [u8; 32]);
/// A message's id: the `HASH` of the message's canonical CBOR encoding, so
/// an answer can name the ask it replies to. Written in `ID_ENCODING`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct MessageId(#[serde(with = "crate::cbor::fixed")] pub [u8; 32]);
/// What a thread is about (design §9). Fixed when the thread opens.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ThreadAnchor {
/// An address, `path#pointer` or a whole document (design §4).
Address(String),
/// A workspace.
Workspace(WorkspaceId),
/// A set of participants: a direct conversation.
Participants(BTreeSet<ParticipantKey>),
}
/// The kind of a stored message (design §9). The set is open: later kinds
/// (navigator `review`, `accept`, `dismiss`, §10) need no major version, so
/// a reader skips a stored message whose kind it doesn't know. `presence`
/// and `typing` are never stored.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum MessageKind {
Say,
/// Expects an `answer` and stays open until one arrives. Addressed to
/// the thread's `to` (see [`ThreadOpen`]), or to anyone subscribed when
/// `to` is empty.
Ask,
/// Replies to the ask `ask` in the same thread.
Answer {
ask: MessageId,
},
}
/// One message of a thread.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ThreadMessage {
pub author: ParticipantKey,
/// The sending request's `Origin.time_ms`.
pub time_ms: i64,
pub kind: MessageKind,
pub text: String,
}
/// Open a thread: write its anchor, its addressees, and its first message
/// (design §9), committed by `origin.author` at `origin.time_ms / 1000`
/// seconds: the first event a new thread document gets; a thread already
/// open, and a document of another kind, ignore it. `first.author` is the
/// opener of the thread id. A merge-back conflict thread is anchored on the
/// conflicting base document's address and addressed to both authors
/// (design §8).
///
/// Layout: the genesis operation puts `/messages` as a list, so every
/// participant appends to the same object. Opening puts `/anchor` as a byte
/// string holding the canonical CBOR encoding of `anchor` and `/to` as a
/// list of the addressees' keys in `ID_ENCODING`, sorted by key bytes (the
/// order of `to`, not that of the encoded text, whose digits sort before its
/// letters), then appends `first` like a [`ThreadAppend`], all in one
/// change. Each message is a map at the end of `/messages` with `id`,
/// `author`, `kind` (`say`, `ask`, or `answer`), and, for an answer, `ask`
/// as strings (ids and keys in `ID_ENCODING`), `time_ms` as an integer, and
/// `text` as a string.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ThreadOpen {
pub origin: Origin,
pub anchor: ThreadAnchor,
/// Who the thread is addressed to: the participants its asks expect an
/// answer from, and whose inboxes it reaches (M3). Fixed when the thread
/// opens.
pub to: BTreeSet<ParticipantKey>,
pub first: ThreadMessage,
}
/// Append a message to an open thread (design §9), committed by
/// `origin.author` at `origin.time_ms / 1000` seconds; a thread not yet
/// open, and a document of another kind, ignore it. Concurrent appends both
/// land, ordered by Automerge.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ThreadAppend {
pub origin: Origin,
pub message: ThreadMessage,
}