clankerdiff-markdown 0.1.1

Portable semantic Markdown parsing and review state
Documentation
use std::{
    ops::Range,
    sync::atomic::{AtomicU64, Ordering},
};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MarkdownStreamUpdate {
    pub revision: u64,
    pub changed_source: Range<usize>,
    pub reset: bool,
    pub finished: bool,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MarkdownStreamIdentity(u64);

impl Default for MarkdownStreamIdentity {
    fn default() -> Self {
        static NEXT: AtomicU64 = AtomicU64::new(0);
        Self(NEXT.fetch_add(1, Ordering::Relaxed))
    }
}

#[derive(Debug, Default)]
pub struct MarkdownStream {
    source: String,
    revision: u64,
    identity: MarkdownStreamIdentity,
    finished: bool,
}

impl Clone for MarkdownStream {
    fn clone(&self) -> Self {
        Self {
            source: self.source.clone(),
            revision: self.revision,
            identity: MarkdownStreamIdentity::default(),
            finished: self.finished,
        }
    }
}

impl MarkdownStream {
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }

    pub fn push(&mut self, chunk: &str) -> MarkdownStreamUpdate {
        if chunk.is_empty() && !self.finished {
            return self.unchanged();
        }
        let start = self.source.len();
        let reset = std::mem::take(&mut self.finished);
        self.source.push_str(chunk);
        self.update(start..self.source.len(), reset)
    }

    pub fn replace(&mut self, source: impl Into<String>) -> MarkdownStreamUpdate {
        self.source = source.into();
        self.finished = false;
        self.update(0..self.source.len(), true)
    }

    pub fn finish(&mut self) -> MarkdownStreamUpdate {
        if self.finished {
            return self.unchanged();
        }
        self.finished = true;
        self.update(self.source.len()..self.source.len(), false)
    }

    #[must_use]
    pub fn source(&self) -> &str {
        &self.source
    }

    #[must_use]
    pub const fn revision(&self) -> u64 {
        self.revision
    }

    #[must_use]
    pub const fn identity(&self) -> MarkdownStreamIdentity {
        self.identity
    }

    #[must_use]
    pub const fn is_finished(&self) -> bool {
        self.finished
    }

    fn update(&mut self, changed_source: Range<usize>, reset: bool) -> MarkdownStreamUpdate {
        self.revision = self.revision.wrapping_add(1);
        MarkdownStreamUpdate {
            revision: self.revision,
            changed_source,
            reset,
            finished: self.finished,
        }
    }

    fn unchanged(&self) -> MarkdownStreamUpdate {
        MarkdownStreamUpdate {
            revision: self.revision,
            changed_source: self.source.len()..self.source.len(),
            reset: false,
            finished: self.finished,
        }
    }
}