use std::num::NonZeroU64;
use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Revision(NonZeroU64);
static NEXT_REVISION: AtomicU64 = AtomicU64::new(1);
impl Revision {
pub(crate) fn fresh() -> Self {
let n = NEXT_REVISION.fetch_add(1, Ordering::Relaxed);
Self(NonZeroU64::new(n).expect("revision counter wrapped"))
}
pub fn get(self) -> NonZeroU64 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Column(usize);
impl Column {
pub const ZERO: Column = Column(0);
pub fn new(chars: usize) -> Self {
Self(chars)
}
pub fn get(self) -> usize {
self.0
}
}
impl From<usize> for Column {
fn from(chars: usize) -> Self {
Self(chars)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Position {
byte: usize,
row: usize,
column: Column,
revision: Revision,
}
impl Position {
pub(crate) fn new(byte: usize, row: usize, column: Column, revision: Revision) -> Self {
Self {
byte,
row,
column,
revision,
}
}
pub fn byte(self) -> usize {
self.byte
}
pub fn row(self) -> usize {
self.row
}
pub fn column(self) -> Column {
self.column
}
pub fn revision(self) -> Revision {
self.revision
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Span {
start: Position,
end: Position,
}
impl Span {
pub(crate) fn new(start: Position, end: Position) -> Self {
debug_assert_eq!(start.revision(), end.revision());
debug_assert!(start.byte() <= end.byte());
Self { start, end }
}
pub fn start(self) -> Position {
self.start
}
pub fn end(self) -> Position {
self.end
}
pub fn revision(self) -> Revision {
self.start.revision()
}
pub fn is_empty(self) -> bool {
self.start.byte() == self.end.byte()
}
pub fn byte_range(self) -> std::ops::Range<usize> {
self.start.byte()..self.end.byte()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fresh_revisions_never_repeat() {
let a = Revision::fresh();
let b = Revision::fresh();
assert_ne!(a, b);
}
#[test]
fn column_round_trips() {
assert_eq!(Column::from(7).get(), 7);
assert_eq!(Column::ZERO.get(), 0);
}
}