Skip to main content

strop_core/
diagnostics.rs

1//! Mutation diagnostics at the mechanics boundary, including open undo groups.
2use crate::Buffer;
3use serde::Serialize;
4use std::sync::atomic::{AtomicU64, Ordering};
5use strop_trace::{capture_content, enabled, record, EventKind};
6
7static NEXT_BUFFER: AtomicU64 = AtomicU64::new(1);
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
10#[serde(transparent)]
11pub struct BufferTraceId(u64);
12impl BufferTraceId {
13    pub(crate) fn next() -> Self {
14        Self(NEXT_BUFFER.fetch_add(1, Ordering::Relaxed))
15    }
16}
17
18#[derive(Serialize)]
19struct Mutation<'a> {
20    buffer: BufferTraceId,
21    source: crate::ChangeOrigin,
22    revision: u64,
23    start_byte: usize,
24    removed_bytes: usize,
25    inserted_bytes: usize,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    inserted_text: Option<&'a str>,
28}
29
30impl Buffer {
31    pub fn trace_id(&self) -> BufferTraceId {
32        self.trace_identity
33    }
34
35    pub(crate) fn trace_edit(
36        &self,
37        source: crate::ChangeOrigin,
38        start_byte: usize,
39        removed_bytes: usize,
40        inserted: &str,
41    ) {
42        if !enabled() {
43            return;
44        }
45        record(
46            EventKind::Mutation,
47            &Mutation {
48                buffer: self.trace_id(),
49                source,
50                revision: self.revision().get(),
51                start_byte,
52                removed_bytes,
53                inserted_bytes: inserted.len(),
54                inserted_text: capture_content().then_some(inserted),
55            },
56        );
57    }
58
59    pub(crate) fn trace_history(&self, edits: &[crate::history::Edit]) {
60        if !enabled() {
61            return;
62        }
63        #[derive(Serialize)]
64        struct HistoryEdit<'a> {
65            start_byte: usize,
66            bytes: usize,
67            kind: crate::history::EditKind,
68            #[serde(skip_serializing_if = "Option::is_none")]
69            text: Option<&'a str>,
70        }
71        #[derive(Serialize)]
72        struct AppliedHistory<'a> {
73            buffer: BufferTraceId,
74            revision: u64,
75            edits: Vec<HistoryEdit<'a>>,
76        }
77        record(
78            EventKind::History,
79            &AppliedHistory {
80                buffer: self.trace_id(),
81                revision: self.revision().get(),
82                edits: edits
83                    .iter()
84                    .map(|edit| HistoryEdit {
85                        start_byte: edit.at,
86                        bytes: edit.text.len(),
87                        kind: edit.kind,
88                        text: capture_content().then_some(edit.text.as_str()),
89                    })
90                    .collect(),
91            },
92        );
93    }
94}