#![cfg_attr(
not(any(
feature = "tokio_lib",
feature = "async_std_lib",
feature = "static_output"
)),
allow(unused_imports),
allow(dead_code)
)]
#![deny(clippy::all)]
#![warn(clippy::pedantic)]
mod error;
#[cfg(any(feature = "tokio_lib", feature = "async_std_lib"))]
mod rt_wrappers;
mod search;
#[cfg(feature = "static_output")]
mod static_pager;
mod utils;
#[cfg(any(feature = "tokio_lib", feature = "async_std_lib"))]
pub use rt_wrappers::*;
#[cfg(feature = "static_output")]
pub use static_pager::page_all;
pub use error::*;
use std::cell::UnsafeCell;
#[cfg(any(feature = "tokio_lib", feature = "async_std_lib"))]
use std::sync::Arc;
use std::{
ops::{Deref, DerefMut},
sync::atomic::{AtomicBool, Ordering},
};
pub use utils::LineNumbers;
mod init;
pub struct PagerMutex {
pager: UnsafeCell<Pager>,
is_locked: AtomicBool,
}
impl<'a> PagerMutex {
pub async fn lock(&'a self) -> PagerGuard<'a> {
loop {
if self.is_locked.swap(true, Ordering::AcqRel) {
self.is_locked.store(true, Ordering::Relaxed);
return PagerGuard(self);
}
std::sync::atomic::spin_loop_hint();
}
}
#[must_use]
pub fn new(p: Pager) -> PagerMutex {
PagerMutex {
pager: UnsafeCell::new(p),
is_locked: AtomicBool::new(false),
}
}
}
pub struct PagerGuard<'a>(&'a PagerMutex);
impl<'a> DerefMut for PagerGuard<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.0.pager.get() }
}
}
impl<'a> Deref for PagerGuard<'a> {
type Target = Pager;
fn deref(&self) -> &Self::Target {
unsafe { &*self.0.pager.get() }
}
}
impl<'a> Drop for PagerGuard<'a> {
fn drop(&mut self) {
self.0.is_locked.store(false, Ordering::Relaxed);
}
}
unsafe impl<'a> Send for PagerGuard<'a> {}
unsafe impl<'a> Sync for PagerGuard<'a> {}
unsafe impl<'a> Send for PagerMutex {}
unsafe impl<'a> Sync for PagerMutex {}
#[derive(Clone)]
pub struct Pager {
pub lines: String,
line_numbers: LineNumbers,
pub prompt: String,
exit_strategy: ExitStrategy,
upper_mark: usize,
searchable: bool,
#[cfg(feature = "search")]
search_term: String,
#[cfg(feature = "search")]
search_lines: String,
}
impl Pager {
#[must_use]
pub fn new() -> Self {
Pager {
lines: String::new(),
line_numbers: LineNumbers::Disabled,
upper_mark: 0,
prompt: "minus".to_string(),
exit_strategy: ExitStrategy::ProcessQuit,
searchable: true,
#[cfg(feature = "search")]
search_term: String::new(),
#[cfg(feature = "search")]
search_lines: String::new(),
}
}
pub fn set_text(mut self, t: impl Into<String>) -> Self {
self.lines = t.into();
self
}
#[must_use]
pub fn set_line_numbers(mut self, l: LineNumbers) -> Self {
self.line_numbers = l;
self
}
pub fn set_prompt(mut self, t: impl Into<String>) -> Self {
self.prompt = t.into();
self
}
#[must_use]
pub fn set_searchable(mut self, s: bool) -> Self {
self.searchable = s;
self
}
#[must_use]
#[cfg(any(feature = "tokio_lib", feature = "async_std_lib"))]
pub fn finish(self) -> Arc<PagerMutex> {
Arc::new(PagerMutex::new(self))
}
#[must_use]
pub fn set_exit_strategy(mut self, strategy: ExitStrategy) -> Self {
self.exit_strategy = strategy;
self
}
pub(crate) fn get_lines(&self) -> String {
#[cfg(feature = "search")]
if self.search_term.is_empty() {
self.lines.clone()
} else {
self.search_lines.clone()
}
#[cfg(not(feature = "search"))]
self.lines.clone()
}
}
impl std::default::Default for Pager {
fn default() -> Self {
Pager::new()
}
}
#[derive(PartialEq, Clone)]
pub enum ExitStrategy {
ProcessQuit,
PagerQuit,
}