use crate::error::{self, Error};
use crate::sink;
const PLANE: &str = "lines";
const FIRST_GUESS: usize = 128;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Line {
number: u64,
start: u64,
content_end: u64,
term_end: u64,
}
impl Line {
#[must_use]
pub fn number(&self) -> u64 {
self.number
}
#[must_use]
pub fn content<'t>(&self, text: &'t [u8]) -> &'t [u8] {
&text[self.start as usize..self.content_end as usize]
}
#[must_use]
pub fn with_terminator<'t>(&self, text: &'t [u8]) -> &'t [u8] {
&text[self.start as usize..self.term_end as usize]
}
#[must_use]
pub fn range(&self) -> std::ops::Range<usize> {
self.start as usize..self.content_end as usize
}
#[must_use]
pub fn holds(&self, at: usize) -> bool {
let at = at as u64;
at >= self.start && (at < self.term_end || at == self.term_end && self.is_unterminated())
}
#[must_use]
pub fn is_unterminated(&self) -> bool {
self.content_end == self.term_end
}
#[must_use]
pub fn column(&self, at: usize) -> usize {
(at as u64).saturating_sub(self.start) as usize + 1
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Band {
rows: Vec<Line>,
center: usize,
}
impl Band {
#[must_use]
pub fn rows(&self) -> &[Line] {
&self.rows
}
#[must_use]
pub fn center(&self) -> usize {
self.center
}
#[must_use]
pub fn focus(&self) -> Option<&Line> {
self.rows.get(self.center)
}
#[must_use]
pub fn around(&self) -> (&[Line], Option<&Line>, &[Line]) {
let (before, rest) = self.rows.split_at(self.center.min(self.rows.len()));
match rest.split_first() {
Some((focus, after)) => (before, Some(focus), after),
None => (before, None, &[]),
}
}
}
impl std::ops::Deref for Band {
type Target = [Line];
fn deref(&self) -> &Self::Target {
&self.rows
}
}
pub fn count(text: &[u8]) -> Result<u64, Error> {
let mut out = 0u64;
let status = unsafe { ffi::irgx_lines_count(text.as_ptr(), text.len(), &raw mut out) };
if status < 0 {
return Err(error::plane_fault(status, PLANE));
}
Ok(out)
}
pub fn split(text: &[u8]) -> Result<Vec<Line>, Error> {
sink::reap_all(PLANE, FIRST_GUESS, |out, cap, written| {
unsafe { ffi::irgx_lines_split(text.as_ptr(), text.len(), out, cap, written) }
})
}
pub fn context(text: &[u8], at: usize, before: usize, after: usize) -> Result<Band, Error> {
let mut center = 0usize;
let rows = sink::reap_all(PLANE, before + after + 1, |out, cap, written| {
unsafe {
ffi::irgx_lines_context(
text.as_ptr(),
text.len(),
at,
before,
after,
out,
cap,
written,
&raw mut center,
)
}
})?;
Ok(Band { rows, center })
}
mod ffi {
use super::Line;
unsafe extern "C" {
pub fn irgx_lines_count(text: *const u8, len: usize, out: *mut u64) -> i32;
pub fn irgx_lines_context(
text: *const u8,
len: usize,
at: usize,
before: usize,
after: usize,
out: *mut Line,
cap: usize,
written: *mut usize,
center: *mut usize,
) -> i32;
pub fn irgx_lines_split(
text: *const u8,
len: usize,
out: *mut Line,
cap: usize,
written: *mut usize,
) -> i32;
}
}