Skip to main content

rustledger_lsp/
snapshot.rs

1//! Immutable world snapshot for request handling.
2//!
3//! Each LSP request receives an immutable snapshot of the world state.
4//! This allows requests to be processed concurrently without locks.
5
6use std::sync::Arc;
7use std::sync::atomic::{AtomicU64, Ordering};
8
9/// Global revision counter used by [`Snapshot`].
10///
11/// Note: the LSP main loop maintains its OWN per-instance revision
12/// counter on `MainLoopState`. This global is kept only because the
13/// `Snapshot` API exposes `is_current` / `is_cancelled`, which today
14/// has no production callers and is exercised only by the test below.
15/// New cancellation-detection logic should use the per-instance
16/// counter on `MainLoopState` so multiple LSP instances in one
17/// process (e.g., integration tests) don't clobber each other.
18static REVISION: AtomicU64 = AtomicU64::new(0);
19
20/// Bump the global revision counter. See [`REVISION`] note about
21/// preferring the per-instance counter on `MainLoopState`.
22///
23/// Kept `#[allow(dead_code)]` because production no longer calls it
24/// (the per-instance counter on `MainLoopState` replaced it); the
25/// remaining caller is `test_snapshot_cancellation` below, which
26/// pins the `Snapshot::is_cancelled` contract even though no
27/// production handler reads `Snapshot` today.
28#[allow(dead_code)]
29pub fn bump_revision() -> u64 {
30    REVISION.fetch_add(1, Ordering::SeqCst) + 1
31}
32
33/// Get the current revision. See [`REVISION`] note about preferring
34/// the per-instance counter on `MainLoopState`.
35pub fn current_revision() -> u64 {
36    REVISION.load(Ordering::SeqCst)
37}
38
39/// An immutable snapshot of the world state.
40///
41/// Snapshots capture the revision at creation time, allowing
42/// handlers to detect if they should cancel (revision changed).
43#[derive(Debug)]
44pub struct Snapshot {
45    /// The revision at snapshot creation time.
46    revision: u64,
47    /// Parsed directives (TODO: replace with actual data)
48    _data: Arc<()>,
49}
50
51impl Snapshot {
52    /// Create a new snapshot at the current revision.
53    pub fn new() -> Self {
54        Self {
55            revision: current_revision(),
56            _data: Arc::new(()),
57        }
58    }
59
60    /// Check if this snapshot is still current (not cancelled).
61    pub fn is_current(&self) -> bool {
62        self.revision == current_revision()
63    }
64
65    /// Check if this snapshot has been cancelled.
66    pub fn is_cancelled(&self) -> bool {
67        !self.is_current()
68    }
69}
70
71impl Default for Snapshot {
72    fn default() -> Self {
73        Self::new()
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn test_snapshot_cancellation() {
83        let snap = Snapshot::new();
84        assert!(snap.is_current());
85
86        bump_revision();
87        assert!(snap.is_cancelled());
88    }
89}