Skip to main content

rig_core/conversation/
lineage.rs

1//! Which saved log a conversation continues.
2
3use serde::{Deserialize, Serialize};
4
5/// The saved log a [`Conversation`](super::Conversation) continues: a token
6/// a store chose when it started that log, carried by every value loaded
7/// from it or saved to it. A store appends a value's unsaved changes to a
8/// log only when the value holds what that log holds
9/// ([`Conversation::continues`](super::Conversation::continues)): its
10/// lineage, and as many of its changes. The count alone cannot tell two
11/// conversations apart.
12#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
13#[serde(transparent)]
14pub struct Lineage(u64);
15
16impl Lineage {
17    /// A conversation begun here from nothing and never saved: it continues
18    /// any empty log.
19    pub const EMPTY: Self = Self(0);
20
21    /// A conversation read back from its serde form, which does not carry
22    /// its lineage: it continues no log, so saving it writes it whole.
23    pub const DETACHED: Self = Self(u64::MAX);
24
25    /// The lineage of a log just started: neither [`EMPTY`](Self::EMPTY)
26    /// nor [`DETACHED`](Self::DETACHED), and unlike any other log's but by
27    /// a one in 2^64 chance.
28    pub fn fresh() -> Self {
29        let now = web_time::SystemTime::now()
30            .duration_since(web_time::UNIX_EPOCH)
31            .unwrap_or_default();
32        let clock = now.as_secs().rotate_left(32) ^ u64::from(now.subsec_nanos());
33        Self((fastrand::u64(..) ^ clock).clamp(1, u64::MAX - 1))
34    }
35
36    /// Whether this is a log's lineage: neither [`EMPTY`](Self::EMPTY) nor
37    /// [`DETACHED`](Self::DETACHED).
38    pub fn is_log(self) -> bool {
39        self != Self::EMPTY && self != Self::DETACHED
40    }
41}