Skip to content

Commit

Permalink
Split process building into smaller mods
Browse files Browse the repository at this point in the history
  • Loading branch information
jpikl committed Apr 30, 2024
1 parent 9cd8464 commit c0959d5
Show file tree
Hide file tree
Showing 11 changed files with 185 additions and 194 deletions.
41 changes: 3 additions & 38 deletions src/args.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use clap::crate_name;
use crate::env::ENV_BUF_MODE;
use crate::env::ENV_BUF_SIZE;
use crate::env::ENV_NULL;
use clap::Args;
use clap::ValueEnum;
use derive_more::Display;
use derive_more::IsVariant;
use std::env;
use std::io::stdout;
use std::io::IsTerminal;

Expand All @@ -12,42 +13,6 @@ use std::io::IsTerminal;
// Also used internally by the `linereader` library https://github.com/Freaky/rust-linereader.
const DEFAULT_BUF_SIZE: usize = 32 * 1024;

// Publicly known env variables:
pub const ENV_NULL: &str = "REW_NULL";
pub const ENV_BUF_MODE: &str = "REW_BUF_MODE";
pub const ENV_BUF_SIZE: &str = "REW_BUF_SIZE";

// Internal env variables:
//
// When `rew` is spawned as a child of some parent `rew` process,
// it recieves the parent's name through this environment variable.
pub const ENV_SPAWNED_BY: &str = "_REW_SPAWNED_BY";

pub fn get_spawned_by(cmd_name: &str) -> String {
format!("{} {cmd_name}", get_bin_name())
}

pub fn get_bin_name() -> String {
if let Some(spawned_by) = env::var_os(ENV_SPAWNED_BY) {
let mut spawned_by = spawned_by.to_string_lossy().to_string();

if let Some(bin_name_len) = spawned_by.rfind(' ') {
spawned_by.truncate(bin_name_len); // Trim rew subcommand name
}

// Parent `rew` process `argv[0]`
return spawned_by;
}

if let Some(bin_name) = env::args_os().next() {
// Current `rew` process `argv[0]`
return bin_name.to_string_lossy().to_string();
}

// exec syscall did not receive `argv0`?
crate_name!().to_owned()
}

#[derive(Clone, Copy, ValueEnum, Display, Debug, IsVariant, PartialEq, Eq)]
pub enum BufMode {
/// Writes to stdout after a line was processed or when the output buffer is full.
Expand Down
28 changes: 17 additions & 11 deletions src/command.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::args::BufMode;
use crate::args::GlobalArgs;
use crate::env::Env;
use crate::examples::Example;
use crate::io::ByteChunkReader;
use crate::io::CharChunkReader;
Expand Down Expand Up @@ -55,11 +56,11 @@ macro_rules! command_meta {
use clap::FromArgMatches;

if $crate::examples::is_arg_set(&matches) {
return $crate::examples::print(&meta.name, meta.examples);
return $crate::examples::print(meta.name, meta.examples);
}

let global_args = $crate::args::GlobalArgs::from_arg_matches(matches)?;
let context = $crate::command::Context::from(global_args);
let context = $crate::command::Context::new(meta.name, global_args);
let args = $args::from_arg_matches(matches)?;

$run(&context, &args)
Expand Down Expand Up @@ -129,15 +130,16 @@ impl Group {
}

#[derive(Clone)]
pub struct Context(GlobalArgs);

impl From<GlobalArgs> for Context {
fn from(args: GlobalArgs) -> Self {
Self(args)
}
pub struct Context {
command: &'static str,
args: GlobalArgs,
}

impl Context {
pub fn new(command: &'static str, args: GlobalArgs) -> Self {
Self { command, args }
}

#[allow(clippy::unused_self)]
pub fn raw_reader(&self) -> StdinLock<'_> {
stdin().lock()
Expand Down Expand Up @@ -182,18 +184,22 @@ impl Context {
}

pub fn buf_mode(&self) -> BufMode {
self.0.buf_mode
self.args.buf_mode
}

pub fn buf_size(&self) -> usize {
self.0.buf_size
self.args.buf_size
}

pub fn separator(&self) -> Separator {
if self.0.null {
if self.args.null {
Separator::Null
} else {
Separator::Newline
}
}

pub fn env(&self) -> Env {
Env::new(self.command, self.args.clone())
}
}
4 changes: 2 additions & 2 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ mod trim;
mod upper;
mod x;

pub const METAS: &[&Meta] = &[
pub const COMMANDS: &[&Meta] = &[
&ascii::META,
&cat::META,
&first::META,
Expand All @@ -33,5 +33,5 @@ pub const METAS: &[&Meta] = &[
];

pub fn get_meta(name: &str) -> Option<&'static Meta> {
METAS.iter().find(|meta| meta.name == name).copied()
COMMANDS.iter().find(|meta| meta.name == name).copied()
}
167 changes: 40 additions & 127 deletions src/commands/x.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
use super::get_meta;
use crate::args::get_spawned_by;
use crate::args::ENV_BUF_MODE;
use crate::args::ENV_BUF_SIZE;
use crate::args::ENV_NULL;
use crate::args::ENV_SPAWNED_BY;
use crate::colors::RESET;
use crate::colors::YELLOW;
use crate::command::Context;
Expand All @@ -12,32 +6,26 @@ use crate::command::Meta;
use crate::command_examples;
use crate::command_meta;
use crate::commands::cat;
use crate::env::Env;
use crate::examples::Example;
use crate::io::LineReader;
use crate::pattern;
use crate::pattern::Expression;
use crate::pattern::ExpressionValue;
use crate::pattern::Item;
use crate::pattern::Pattern;
use crate::pattern::SimpleItem;
use crate::pattern::SimplePattern;
use crate::process::Command;
use crate::process::Pipeline;
use crate::process::StdinMode;
use crate::shell::Shell;
use crate::spawn::Spawned;
use crate::stdbuf::StdBuf;
use anyhow::Context as _;
use anyhow::Result;
use bstr::ByteVec;
use clap::crate_name;
use clap::ArgAction;
use std::env;
use std::env::current_exe;
use std::panic::resume_unwind;
use std::process::Child;
use std::process::ChildStdin;
use std::process::ChildStdout;
use std::process::Command;
use std::thread;
use std::time::Duration;

Expand Down Expand Up @@ -211,7 +199,7 @@ fn eval_simple_pattern(context: &Context, pattern: &SimplePattern) -> Result<()>
}

fn eval_pattern(context: &Context, pattern: &Pattern, shell: &Shell) -> Result<()> {
let mut stdbuf = StdBuf::default();
let mut env = context.env();
let mut children = Vec::new();
let mut consumers = Vec::new();
let mut producers = Vec::new();
Expand All @@ -220,7 +208,7 @@ fn eval_pattern(context: &Context, pattern: &Pattern, shell: &Shell) -> Result<(
match &item {
Item::Constant(value) => producers.push(Producer::Constant(value.clone())),
Item::Expression(ref expr) => {
let pipeline = build_pipeline(context, &mut stdbuf, shell, expr)?;
let pipeline = build_pipeline(&mut env, shell, expr)?;

for child in pipeline.children {
children.push(child);
Expand Down Expand Up @@ -260,9 +248,37 @@ fn eval_pattern(context: &Context, pattern: &Pattern, shell: &Shell) -> Result<(
}
}

enum Producer {
Constant(String),
Child(Spawned<LineReader<ChildStdout>>),
fn build_pipeline(env: &mut Env, shell: &Shell, expr: &Expression) -> Result<Pipeline> {
let raw_expr = format!("{YELLOW}{}{RESET}", expr.raw_value);

match build_pipeline_internal(env, shell, expr) {
Ok(pipeline) => Ok(pipeline.context(format!("expression: {raw_expr}"))),
Err(err) => Err(err.context(format!("failed to initialize expression {raw_expr}"))),
}
}

fn build_pipeline_internal(env: &mut Env, shell: &Shell, expr: &Expression) -> Result<Pipeline> {
let mut pipeline = Pipeline::new(expr.stdin_mode);

match &expr.value {
ExpressionValue::RawShell(command) => {
let mut command = shell.build_command(command);
command.envs(env.external());
pipeline = pipeline.add_command(command, expr.stdin_mode)?;
}
ExpressionValue::Pipeline(commands) => {
for command in commands {
let command = Command::detect(&command.name, &command.args, command.external);
pipeline = pipeline.add_command(command.build(env)?, command.stdin_mode())?;
}
if pipeline.is_empty() {
let command = Command::internal(&cat::META);
pipeline = pipeline.add_command(command.build(env)?, command.stdin_mode())?;
}
}
};

Ok(pipeline)
}

fn forward_input(context: &Context, mut stdins: Vec<Option<Spawned<ChildStdin>>>) -> Result<()> {
Expand Down Expand Up @@ -291,6 +307,11 @@ fn forward_input(context: &Context, mut stdins: Vec<Option<Spawned<ChildStdin>>>
Ok(())
}

enum Producer {
Constant(String),
Child(Spawned<LineReader<ChildStdout>>),
}

fn collect_output(context: &Context, mut producers: Vec<Producer>) -> Result<()> {
let mut writer = context.writer();
let mut buffer = context.uninit_buf();
Expand Down Expand Up @@ -342,111 +363,3 @@ fn wait_children(mut children: Vec<Spawned<Child>>) -> Result<()> {

Ok(())
}

fn build_pipeline(
context: &Context,
stdbuf: &mut StdBuf,
shell: &Shell,
expr: &Expression,
) -> Result<Pipeline> {
let raw_expr = format!("{YELLOW}{}{RESET}", expr.raw_value);

match build_pipeline_internal(context, stdbuf, shell, expr) {
Ok(pipeline) => Ok(pipeline.context(format!("expression: {raw_expr}"))),
Err(err) => Err(err.context(format!("failed to initialize expression {raw_expr}"))),
}
}

fn build_pipeline_internal(
context: &Context,
stdbuf: &mut StdBuf,
shell: &Shell,
expr: &Expression,
) -> Result<Pipeline> {
let mut pipeline = Pipeline::new(expr.stdin_mode);

match &expr.value {
ExpressionValue::RawShell(command) => {
let command = shell.build_command(command);
pipeline = pipeline.command(command, expr.stdin_mode)?;
}
ExpressionValue::Pipeline(commands) => {
for command in commands {
let (command, stdin_mode) = build_command(context, stdbuf, command)?;
pipeline = pipeline.command(command, stdin_mode)?;
}
if pipeline.is_empty() {
let command = build_default_command(context)?;
pipeline = pipeline.command(command, StdinMode::Connected)?;
}
}
};

Ok(pipeline)
}

fn build_command(
context: &Context,
stdbuf: &mut StdBuf,
params: &pattern::Command,
) -> Result<(Command, StdinMode)> {
let pattern::Command {
name,
args,
external,
} = params;

if !external {
if let Some(meta) = get_meta(name) {
let command = build_internal_command(context, Some(name), args)?;
return Ok((command, meta.group.stdin_mode()));
}

if name == crate_name!() {
if let Some((name, args)) = args.split_first() {
if let Some(meta) = get_meta(name) {
let command = build_internal_command(context, Some(name), args)?;
return Ok((command, meta.group.stdin_mode()));
}
}

let command = build_internal_command(context, None, args)?;
return Ok((command, StdinMode::Connected));
}
}

let mut command = Command::new(name);
command.args(args);

if context.buf_mode().is_line() {
command.envs(stdbuf.line_buf_envs()); // libc based programs
command.env("PYTHONUNBUFFERED", "1"); // Python programs
}

Ok((command, StdinMode::Connected))
}

fn build_internal_command(
context: &Context,
name: Option<&str>,
args: &[String],
) -> Result<Command> {
let program = current_exe().context("could not detect current executable")?;
let mut command = Command::new(program);

command.env(ENV_NULL, context.separator().is_null().to_string());
command.env(ENV_BUF_MODE, context.buf_mode().to_string());
command.env(ENV_BUF_SIZE, context.buf_size().to_string());
command.env(ENV_SPAWNED_BY, get_spawned_by(META.name));

if let Some(name) = name {
command.arg(name);
}

command.args(args);
Ok(command)
}

fn build_default_command(context: &Context) -> Result<Command> {
build_internal_command(context, Some(cat::META.name), &[])
}
4 changes: 2 additions & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::args::get_bin_name;
use crate::args::ENV_SPAWNED_BY;
use crate::colors::Colorizer;
use crate::colors::BOLD;
use crate::colors::BOLD_RED;
use crate::colors::RESET;
use crate::env::get_bin_name;
use crate::env::ENV_SPAWNED_BY;
use anstream::eprint;
use anstream::eprintln;
use anstream::stdout;
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub mod command;
#[doc(hidden)]
pub mod commands;
#[doc(hidden)]
pub mod env;
#[doc(hidden)]
pub mod error;
#[doc(hidden)]
pub mod examples;
Expand Down
Loading

0 comments on commit c0959d5

Please sign in to comment.