rust-pager 0.2.4

Yet another pager in Rust
mod reader;
mod shared;
mod writer;

use bumpalo::Bump;
use crossbeam_queue::ArrayQueue;
use crossbeam_utils::thread::scope;
use crossterm::{tty::IsTty, Result};
use std::fs::File;
use std::path::PathBuf;
use std::sync::{atomic, Arc};

static RUN: atomic::AtomicBool = atomic::AtomicBool::new(true);

struct Args {
    path: Option<PathBuf>,
}

impl Args {
    pub fn parse() -> Option<Self> {
        let mut args = pico_args::Arguments::from_env();

        if args.contains(["-h", "--help"]) {
            println!("rp 0.1.0");
            println!("USAGE: `<command> | rp` or `rp <path>`");
            return None;
        }

        Some(Self {
            path: args.free_from_str().ok(),
        })
    }
}

fn get_input(args: &crate::Args) -> Result<File> {
    if !std::io::stdin().is_tty() {
        unsafe {
            use std::os::unix::prelude::FromRawFd;
            let stdin = File::from_raw_fd(libc::STDIN_FILENO);
            Ok(stdin)
        }
    } else {
        Ok(File::open(args.path.as_deref().expect("No given path"))?)
    }
}

fn main() -> Result<()> {
    #[cfg(feature = "logging")]
    {
        use simplelog::*;
        WriteLogger::init(
            LevelFilter::Trace,
            ConfigBuilder::new().build(),
            File::create("rp.log")?,
        )
        .unwrap();
        log_panics::init();
    }

    ctrlc::set_handler(|| {
        crate::RUN.store(false, atomic::Ordering::Release);
    })
    .expect("Set ctrlc handler");

    let args = match Args::parse() {
        Some(args) => args,
        None => return Ok(()),
    };
    let stdin = get_input(&args)?;
    let rx = Arc::new(ArrayQueue::new(1024 * 16));
    let mut b = Bump::with_capacity(1024 * 1024);

    scope(|s| {
        let tx = rx.clone();
        s.builder()
            .name("stdin".into())
            .spawn(|_| reader::read_from_stdin(stdin, &mut b, tx))?;

        writer::UiContext::new(rx)?.run()?;

        Ok(())
    })
    .unwrap()
}