process-wrap

Wrap a Command, to spawn processes in a group or session or job object etc (successor to command-group)

https://github.com/watchexec/process-wrap

Science Score: 44.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
    Found CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
    Found .zenodo.json file
  • DOI references
  • Academic publication links
  • Committers with academic emails
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (10.2%) to scientific vocabulary

Keywords from Contributors

argument-parser interactive network-simulation hacking embedded optim standardization projection generic sequences
Last synced: 7 months ago · JSON representation ·

Repository

Wrap a Command, to spawn processes in a group or session or job object etc (successor to command-group)

Basic Info
Statistics
  • Stars: 26
  • Watchers: 1
  • Forks: 9
  • Open Issues: 4
  • Releases: 0
Created about 2 years ago · Last pushed 8 months ago
Metadata Files
Readme Changelog License Citation

README.md

Crate release version Crate license: Apache 2.0 or MIT CI status

process-wrap

  • API documentation.
  • Dual-licensed with Apache 2.0 and MIT.
  • Successor to command-group.
  • Minimum Supported Rust Version: 1.86.0.
    • Only the latest stable rustc version is supported.
    • MSRV increases will not incur major version bumps.

Unlike command-group, process-wrap doesn't implement a single cross-platform API. Instead, it provides composable wrappers which implement a single concern each. It is left to the developer to use the appropriate wrapper(s) for their use-case and platform.

As the successor to (and containing a lot of the code of) command-group, versioning starts at 6.0.0. You can think of it as a breaking change to command-group, though the paradigm is quite different. The full test suite from command-group was retained: process-wrap has parity on functionality as a starting point.

Quick start

toml [dependencies] process-wrap = { version = "9.0.0", features = ["tokio1"] }

By default, the crate does nothing, you need to enable either the std or Tokio "frontend". A default set of wrappers are enabled; you may choose to only compile those you need, see the features list.

```rust use tokio::process::Command; use process_wrap::tokio::*;

let mut child = TokioCommandWrap::withnew("watch", |command| { command.arg("ls"); }) .wrap(ProcessGroup::leader()) .spawn()?; let status = Box::intopin(child.wait()).await?; dbg!(status); ```

or on Windows

```rust use tokio::process::Command; use process_wrap::tokio::*;

let mut child = TokioCommandWrap::withnew("watch", |command| { command.arg("ls"); }) .wrap(JobObject::new()) .spawn()?; let status = Box::intopin(child.wait()).await?; dbg!(status); ```

or with sessions

```rust use tokio::process::Command; use process_wrap::tokio::*;

let mut child = TokioCommandWrap::withnew("watch", |command| { command.arg("ls"); }) .wrap(ProcessSession) .spawn()?; let status = Box::intopin(child.wait()).await?; dbg!(status); ```

or with multiple wrappers

```rust use tokio::process::Command; use process_wrap::tokio::*;

let mut child = TokioCommandWrap::withnew("watch", |command| { command.arg("ls"); }) .wrap(ProcessSession) .wrap(KillOnDrop) .spawn()?; let status = Box::intopin(child.wait()).await?; dbg!(status); ```

or with std

toml [dependencies] process-wrap = { version = "9.0.0", features = ["std"] }

```rust use std::process::Command; use process_wrap::std::*;

let mut child = StdCommandWrap::with_new("watch", |command| { command.arg("ls"); }) .wrap(ProcessGroup::leader()) .spawn()?; let status = child.wait()?; dbg!(status); ```

Wrappers

Job object

  • Platforms: Windows
  • Like command-group.
  • Feature: job-object (default)

rust TokioCommandWrap::with_new("watch", |command| { command.arg("ls"); }) .wrap(JobObject) .spawn()?;

When both CreationFlags and JobObject are used together, either: - CreationFlags must come first, or - CreationFlags must include CREATE_SUSPENDED

Process group

  • Platforms: POSIX (Linux, Mac, BSDs...)
  • Like command-group >=5.0.0.
  • Feature: process-group (default)

rust TokioCommandWrap::with_new("watch", |command| { command.arg("ls"); }) .wrap(ProcessGroup::leader()) .spawn()?;

Or join a different group instead:

rust TokioCommandWrap::with_new("watch", |command| { command.arg("ls"); }) .wrap(ProcessGroup::attach_to(pgid)) .spawn()?;

For Windows process groups, use CreationFlags::NEW_PROCESS_GROUP and/or JobObject::new().

Process session

  • Platforms: POSIX (Linux, Mac, BSDs...)
  • Like command-group <5.0.0.
  • Feature: process-session (default)

This combines creating a new session and a new group, and setting this process as leader. To join the session from another process, use ProcessGroup::attach_to() instead.

rust TokioCommandWrap::with_new("watch", |command| { command.arg("ls"); }) .wrap(ProcessSession) .spawn()?;

Reset signal mask

  • Platforms: POSIX (Linux, Mac, BSDs...)
  • Feature: reset-sigmask

This resets the signal mask of the process instead of inheriting it from the parent.

rust TokioCommandWrap::with_new("watch", |command| { command.arg("ls"); }) .wrap(ResetSigmask) .spawn()?;

Creation flags

  • Platforms: Windows
  • Like command-group.
  • Feature: creation-flags (default)

This is a shim to allow setting Windows process creation flags with this API, as otherwise they'd be overwritten.

rust use windows::Win32::System::Threading::*; TokioCommandWrap::with_new("watch", |command| { command.arg("ls"); }) .wrap(CreationFlags(CREATE_NO_WINDOW | CREATE_DETACHED)) .wrap(JobObject) .spawn()?;

When both CreationFlags and JobObject are used together, either: - CreationFlags must come first, or - CreationFlags must include CREATE_SUSPENDED

Kill on drop

  • Platforms: all (Tokio-only)
  • Like command-group.
  • Feature: kill-on-drop (default)

This is a shim to allow wrappers to handle the kill-on-drop flag, as it can't be read from Command.

rust let child = TokioCommandWrap::with_new("watch", |command| { command.arg("ls"); }) .wrap(KillOnDrop) .wrap(ProcessGroup::leader()) .spawn()?; drop(child);

Your own

Implementing a wrapper is done via a set of traits. The std and Tokio sides are completely separate, due to the different underlying APIs. Of course you can (and should) re-use/share code wherever possible if implementing both.

At minimum, you must implement StdCommandWrapper and/or TokioCommandWrapper. These provide the same functionality, but differ in the exact types specified. Here's the most basic impl (shown for Tokio):

```rust

[derive(Debug)]

pub struct YourWrapper; impl TokioCommandWrapper for YourWrapper {} ```

That's right, all member methods are optional. The trait provides extension or hook points into the lifecycle of a Command:

  • fn extend(&mut self, other: Box<dyn TokioCommandWrapper>) is called if .wrap(YourWrapper) is done twice. Only one of a wrapper type can exist, so this gives the opportunity to incorporate all or part of the second wrapper instance into the first. By default, this does nothing (ie only the first registered wrapper instance of a type does anything).

  • fn pre_spawn(&mut self, command: &mut Command, core: &TokioCommandWrap) is called before the command is spawned, and gives mutable access to it. It also gives mutable access to the wrapper instance, so state can be stored if needed. The core reference gives access to data from other wrappers; for example, that's how CreationFlags on Windows works along with JobObject. Noop by default.

  • fn post_spawn(&mut self, child: &mut tokio::process::Child, core: &TokioCommandWrap) is called after spawn, and should be used for any necessary cleanups. It is offered for completedness but is expected to be less used than wrap_child(). Noop by default.

  • fn wrap_child(&mut self, child: Box<dyn TokioChildWrapper>, core: &TokioCommandWrap) is called after all post_spawn()s have run. If your wrapper needs to override the methods on Child, then it should create an instance of its own type implementing TokioChildWrapper and return it here. Child wraps are in order: you may end up with a Foo(Bar(Child)) or a Bar(Foo(Child)) depending on if .wrap(Foo).wrap(Bar) or .wrap(Bar).wrap(Foo) was called. If your functionality is order-dependent, make sure to specify so in your documentation! Default is noop: no wrapping is performed and the input child is returned as-is.

Refer to the API documentation for more detail and the specifics of child wrapper traits.

Features

Frontends

  • std: enables the std-based API.
  • tokio1: enables the Tokio-based API.

Both can exist at the same time, but generally you should use one or the other.

Wrappers

Owner

  • Name: watchexec
  • Login: watchexec
  • Kind: organization

Citation (CITATION.cff)

cff-version: 1.2.0
message: If you use this software, please cite it using these metadata.
title: "process-wrap: extensible wrappers for Rust Command APIs"

version: "9.0.0"
date-released: 2025-08-09

repository-code: https://github.com/watchexec/process-wrap
license: Apache-2.0 OR MIT

authors:
  - family-names: Saparelli
    given-names: Félix
    orcid: https://orcid.org/0000-0002-2010-630X

GitHub Events

Total
  • Issues event: 12
  • Watch event: 15
  • Delete event: 4
  • Issue comment event: 36
  • Push event: 33
  • Pull request event: 13
  • Fork event: 4
  • Create event: 6
Last Year
  • Issues event: 12
  • Watch event: 15
  • Delete event: 4
  • Issue comment event: 36
  • Push event: 33
  • Pull request event: 13
  • Fork event: 4
  • Create event: 6

Committers

Last synced: 10 months ago

All Time
  • Total Commits: 231
  • Total Committers: 12
  • Avg Commits per committer: 19.25
  • Development Distribution Score (DDS): 0.065
Past Year
  • Commits: 14
  • Committers: 2
  • Avg Commits per committer: 7.0
  • Development Distribution Score (DDS): 0.071
Top Committers
Name Email Commits
Félix Saparelli f****x@p****e 216
Brian Caswell b****l@m****m 3
dependabot[bot] 4****] 2
Jiahao XU J****U@o****m 2
Yuyi Wang S****r@h****m 1
Ryan Zoeller R****r@n****m 1
Rebecca Turner r****t@s****s 1
Michael Alyn Miller m****n@s****m 1
Lennard l****r@g****t 1
Henry Chen c****7@a****o 1
Arthur D'Andréa a****r@g****m 1
Alexander Lyon a****n@m****m 1
Committer Domains (Top 20 + Academic)

Issues and Pull Requests

Last synced: 7 months ago

All Time
  • Total issues: 8
  • Total pull requests: 24
  • Average time to close issues: 18 days
  • Average time to close pull requests: 29 days
  • Total issue authors: 6
  • Total pull request authors: 7
  • Average comments per issue: 2.38
  • Average comments per pull request: 1.54
  • Merged pull requests: 9
  • Bot issues: 0
  • Bot pull requests: 15
Past Year
  • Issues: 7
  • Pull requests: 12
  • Average time to close issues: 20 days
  • Average time to close pull requests: 19 days
  • Issue authors: 5
  • Pull request authors: 4
  • Average comments per issue: 2.57
  • Average comments per pull request: 1.75
  • Merged pull requests: 4
  • Bot issues: 0
  • Bot pull requests: 6
Top Authors
Issue Authors
  • TheLostLambda (3)
  • devnought (1)
  • xobs (1)
  • Yohe-Am (1)
  • agallardol (1)
  • nathanwhit (1)
Pull Request Authors
  • dependabot[bot] (21)
  • chenx97 (2)
  • TheLostLambda (2)
  • passcod (2)
  • Berrysoft (2)
  • arthurdandrea (2)
  • 9999years (1)
Top Labels
Issue Labels
documentation (1)
Pull Request Labels
dependencies (21) rust (21)

Packages

  • Total packages: 1
  • Total downloads:
    • cargo 648,379 total
  • Total dependent packages: 2
  • Total dependent repositories: 0
  • Total versions: 15
  • Total maintainers: 1
crates.io: process-wrap

Wrap a Command, to spawn processes in a group or session or job etc

  • Versions: 15
  • Dependent Packages: 2
  • Dependent Repositories: 0
  • Downloads: 648,379 Total
Rankings
Dependent repos count: 28.8%
Dependent packages count: 33.9%
Average: 53.1%
Downloads: 96.6%
Maintainers (1)
Last synced: 8 months ago

Dependencies

.github/workflows/test.yml actions
  • actions/cache v3 composite
  • actions/checkout v3 composite
Cargo.lock cargo
  • addr2line 0.21.0
  • adler 1.0.2
  • backtrace 0.3.69
  • bitflags 2.4.1
  • bytes 1.1.0
  • cc 1.0.83
  • cfg-if 1.0.0
  • equivalent 1.0.1
  • gimli 0.28.0
  • hashbrown 0.14.3
  • hermit-abi 0.1.19
  • indexmap 2.2.5
  • libc 0.2.149
  • memchr 2.4.1
  • miniz_oxide 0.7.1
  • mio 0.8.9
  • nix 0.27.1
  • num_cpus 1.13.0
  • object 0.32.1
  • once_cell 1.19.0
  • pin-project-lite 0.2.13
  • proc-macro2 1.0.69
  • quote 1.0.33
  • rustc-demangle 0.1.23
  • signal-hook-registry 1.4.0
  • syn 2.0.38
  • tokio 1.33.0
  • tokio-macros 2.1.0
  • tracing 0.1.40
  • tracing-attributes 0.1.27
  • tracing-core 0.1.32
  • unicode-ident 1.0.12
  • wasi 0.11.0+wasi-snapshot-preview1
  • winapi 0.3.9
  • winapi-i686-pc-windows-gnu 0.4.0
  • winapi-x86_64-pc-windows-gnu 0.4.0
  • windows-sys 0.48.0
  • windows-targets 0.48.5
  • windows_aarch64_gnullvm 0.48.5
  • windows_aarch64_msvc 0.48.5
  • windows_i686_gnu 0.48.5
  • windows_i686_msvc 0.48.5
  • windows_x86_64_gnu 0.48.5
  • windows_x86_64_gnullvm 0.48.5
  • windows_x86_64_msvc 0.48.5
Cargo.toml cargo
  • tokio 1.10.0 development
  • indexmap 2.2.5
  • tokio 1.33.0
  • tracing 0.1.40