tailspin 6.0.0

A log file highlighter
Documentation
use crate::cli::get_config;
use crate::config::{Source, Target};
use crate::io::initial_read::{InitialReadCompleteReceiver, InitialReadCompleteSender, initial_read_complete_channel};
use crate::io::presenter::pager::{CustomPagerOptions, LessPagerOptions, Pager, PagerOptions};
use crate::io::presenter::stdout::StdoutPresenter;
use crate::io::reader::command::CommandReader;
use crate::io::reader::file_reader::FileReader;
use crate::io::reader::stdin::StdinReader;
use crate::io::writer::stdout::StdoutWriter;
use crate::io::writer::temp_file::TempFile;
use anyhow::{Context, Result};
use std::path::PathBuf;
use tailspin::Highlighter;
use tempfile::TempDir;
use tokio::fs::File;
use tokio::io::BufWriter;

pub enum Reader {
    File(FileReader),
    Stdin(StdinReader),
    Command(CommandReader),
}

pub enum Writer {
    TempFile(TempFile),
    Stdout(StdoutWriter),
}

pub enum Presenter {
    Pager(Pager),
    StdOut(StdoutPresenter),
}

pub async fn initialize_io() -> Result<(
    Reader,
    Writer,
    Presenter,
    Highlighter,
    InitialReadCompleteSender,
    InitialReadCompleteReceiver,
    Option<TempDir>,
)> {
    let config = get_config()?;
    let (read_complete_sender, read_complete_receiver) = initial_read_complete_channel();

    let reader = get_reader(config.source).await?;
    let (writer, presenter, temp_dir) = get_writer_presenter_and_temp_dir(config.target).await?;

    Ok((
        reader,
        writer,
        presenter,
        config.highlighter,
        read_complete_sender,
        read_complete_receiver,
        temp_dir,
    ))
}

async fn get_reader(input: Source) -> Result<Reader> {
    let reader = match input {
        Source::File(file) => Reader::File(FileReader::new(file.path, file.terminate_after_first_read).await?),
        Source::Stdin => Reader::Stdin(StdinReader::new()),
        Source::Command(cmd) => Reader::Command(CommandReader::new(cmd).await?),
    };

    Ok(reader)
}

async fn get_writer_presenter_and_temp_dir(output: Target) -> Result<(Writer, Presenter, Option<TempDir>)> {
    match output {
        Target::Less(opts) => {
            let less_pager_options = LessPagerOptions { follow: opts.follow };
            let pager_opts = PagerOptions::Less(less_pager_options);
            let (writer, presenter, temp_dir) = get_temp_file_and_pager(pager_opts).await?;

            Ok((writer, presenter, Some(temp_dir)))
        }
        Target::CustomPager(opts) => {
            let custom_pager_options = CustomPagerOptions {
                command: opts.command,
                args: opts.args,
            };
            let pager_options = PagerOptions::Custom(custom_pager_options);
            let (writer, presenter, temp_dir) = get_temp_file_and_pager(pager_options).await?;

            Ok((writer, presenter, Some(temp_dir)))
        }
        Target::Stdout => Ok((
            Writer::Stdout(StdoutWriter::new()),
            Presenter::StdOut(StdoutPresenter::new()),
            None,
        )),
    }
}

async fn get_temp_file_and_pager(pager_opts: PagerOptions) -> Result<(Writer, Presenter, TempDir)> {
    let (temp_dir, path_buf, buf_writer) = create_temp_file().await?;
    let temp_file = TempFile::new(buf_writer).await;
    let pager = Pager::new(path_buf, pager_opts);

    Ok((Writer::TempFile(temp_file), Presenter::Pager(pager), temp_dir))
}

async fn create_temp_file() -> Result<(TempDir, PathBuf, BufWriter<File>)> {
    let filename = "tailspin.temp";

    let temp_dir = tempfile::tempdir().context("Could not locate temporary directory")?;

    let temp_file_path = temp_dir.path().join(filename);
    let output_file = File::create(&temp_file_path)
        .await
        .context("Could not create temporary file")?;

    let buf_writer = BufWriter::new(output_file);

    Ok((temp_dir, temp_file_path, buf_writer))
}