conch-runtime-pshaw 0.1.6

A library for evaluating/executing programs written in the shell programming language.
Documentation
#![cfg_attr(not(feature = "conch-parser"), allow(dead_code, unused_imports))]

use conch_parser::ast::builder::ArcBuilder;
use conch_parser::lexer::Lexer;
use conch_parser::parse::Parser;
use conch_runtime::env::{DefaultEnvArc, DefaultEnvConfigArc};
use conch_runtime::spawn::sequence;
use conch_runtime::{ExitStatus, EXIT_ERROR};
use owned_chars::OwnedCharsExt;
use std::io::{stderr, stdin, BufRead, BufReader, Write};
use std::process::exit;

#[cfg(not(feature = "conch-parser"))]
fn main() {}

#[cfg(feature = "conch-parser")]
#[tokio::main]
async fn main() {
    let stdin = BufReader::new(stdin())
        .lines()
        .filter_map(|result| match result {
            Ok(line) => Some(line),
            Err(e) => {
                if e.kind() == ::std::io::ErrorKind::WouldBlock {
                    None
                } else {
                    panic!("stdin error: {}", e);
                }
            }
        })
        .flat_map(|mut line| {
            line.push_str("\n"); // BufRead::lines unfortunately strips \n and \r\n
            line.into_chars()
        });

    // Initialize our token lexer and shell parser with the program's input
    let lex = Lexer::new(stdin);
    let parser = Parser::with_builder(lex, ArcBuilder::new());

    let cmds = parser.into_iter().map(|result| {
        result.unwrap_or_else(|e| {
            let _ = writeln!(stderr(), "Parse error encountered: {}", e);
            exit_with_status(EXIT_ERROR);
        })
    });

    // Instantiate our environment for executing commands
    let mut env = DefaultEnvArc::with_config(DefaultEnvConfigArc {
        interactive: true,
        ..DefaultEnvConfigArc::new().expect("failed to create env config")
    });

    let env_future_result = sequence(cmds, &mut env).await;

    // Environment no longer needed. Dropping it here so that it can
    // free up any file handles or other resources which may be held
    // (and therefore block any progress).
    drop(env);

    let status = match env_future_result {
        Ok(future) => future.await,
        Err(e) => {
            eprintln!("encountered an error: {}", e);
            EXIT_ERROR
        }
    };

    exit_with_status(status);
}

fn exit_with_status(status: ExitStatus) -> ! {
    let status = match status {
        ExitStatus::Code(n) => n,
        ExitStatus::Signal(n) => n + 128,
    };

    // Have our shell exit with the result of the last command
    exit(status);
}