perforce-cli 0.1.0-alpha.1

A type-safe builder library for spawning Perforce (p4) commands, with compile-time option state isolation and multi-version support.
Documentation
  • Coverage
  • 88.48%
    622 out of 703 items documented0 out of 638 items with examples
  • Size
  • Source code size: 465.4 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 7.8 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 10s Average build duration of successful builds.
  • all releases: 6s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • araraloren/perforce-cli
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • araraloren

perforce-cli

A type-safe Rust builder library for spawning Perforce (p4) commands.

Instead of concatenating raw argument strings, you construct commands with a fluent builder API. Mutually exclusive option groups and command forms are encoded in the type system, so invalid combinations are rejected at compile time rather than failing at runtime.

Features

  • Compile-time option isolation — mutually exclusive flags are modeled as type-state markers. Once a mode is selected, incompatible options simply do not exist on the resulting type.
  • Fluent builders — every option has a consuming builder (x(true)) and a &mut self setter (set_x(true)), paired with getters (get_x()).
  • Generic spawn/output traits — run any command asynchronously with spawn or to completion with output, through the ParameterizedSpawn trait and its extension traits.
  • Multi-version support — select your target Helix Core server version with a single Cargo feature; options introduced or removed in specific releases are gated automatically.
  • Zero dependencies.

Requirements

  • A p4 executable available on PATH (or a custom path passed to P4Cli::new).
  • Rust 1.85+ (edition 2024).

Usage

[dependencies]
perforce-cli = "0.1.0-alpha.1"

Quick start

use std::ffi::OsStr;

use perforce_cli::spawn::ParameterizedOutput;
use perforce_cli::P4Cli;

fn main() -> std::io::Result<()> {
    let p4 = P4Cli::default();

    // `p4 print -q //depot/project/README.md`, output captured.
    let mut print = p4.print().quiet_mode(true);
    let output = print.output_with(&[OsStr::new("//depot/project/README.md")])?;
    println!("{}", String::from_utf8_lossy(&output.stdout));

    Ok(())
}

Global options (the g-opts accepted by every p4 command) are configured on GlobalOpts:

use perforce_cli::global::GlobalOpts;
use perforce_cli::P4Cli;

let global_opts = GlobalOpts::new()
    .port("ssl:helix.example.com:1666")
    .client("my-workspace")
    .user("alice");

let p4 = P4Cli::new("p4", global_opts);

Type-state: incompatible modes cannot be combined

sync demonstrates compile-time mode isolation. -f/-k/-r force a full sync, while -s (safe check) and -p (populate) are mutually exclusive with them:

use std::ffi::OsStr;

use perforce_cli::spawn::ParameterizedSpawn;
use perforce_cli::P4Cli;

let p4 = P4Cli::default();

// `p4 sync -f -q`
let mut force_sync = p4.sync().force(true).quiet_mode(true);
force_sync.spawn_with(&[])?;

// `p4 sync -s`
let mut safe_sync = p4.sync().enable_safe_check();
safe_sync.spawn_with(&[])?;

// `p4 sync -p //depot/project/...`
let mut populate = p4.sync().populate_client_workspace();
populate.spawn_with(&[OsStr::new("//depot/project/...")])?;

Calling enable_safe_check() after force(true) does not compile — the method is not defined for the force mode type, so the invalid command can never be built.

Commands without file arguments

Some commands take no positional arguments. Use the spawn / output methods from SpawnExt / OutputExt instead of spawn_with:

use perforce_cli::spawn::OutputExt;
use perforce_cli::P4Cli;

let p4 = P4Cli::default();

// `p4 admin stop`
let output = p4.admin().stop().output()?;

Spawning traits

Every command implements ParameterizedSpawn, which fixes a single input shape via a GAT:

Trait Arguments Typical Input<'a>
SpawnExt / OutputExt none ()
SpawnExt1 / OutputExt1 one &'a OsStr
SpawnExt2 / OutputExt2SpawnExt8 / OutputExt8 many (T1, …, TN)
ParameterizedSpawn / ParameterizedOutput raw input spawn_with(input) / output_with(input)
  • spawn / spawn_with return a std::process::Child with stdout and stderr piped.
  • output / output_with wait for the child and return a std::process::Output.

Import the trait matching the call you make; or bring everything into scope with the prelude:

use perforce_cli::prelude::*;

Targeting a Helix Core version

The crate carries per-version knowledge of every command's options. Enable exactly one version feature for the oldest server version you need to support; options that did not yet exist (or have been removed) are gated out of the API.

[dependencies]
# Defaults to the newest supported release.
perforce-cli = "0.1.0-alpha.1"

# Or pin an older server line.
perforce-cli = { version = "0.1.0-alpha.1", default-features = false, features = ["v2022_2"] }

Supported versions: v2014_1, v2014_2, v2015_1, v2015_2, v2016_1, v2016_2, v2017_1, v2017_2, v2018_1, v2018_2, v2019_1, v2019_2, v2020_1, v2020_2, v2021_1, v2021_2, v2022_1, v2022_2, v2023_1, v2023_2, v2024_1, v2024_2, v2025_1, v2025_2, v2026_1 (default).

Supported commands

add, admin, aliases, annotate, archive, attribute, changes, describe, edit, filelog, print, sync, where.

More commands are being added.

Examples

See examples/print.rs, which covers capturing output, chaining builders, and spawning a streaming child process.

cargo run --example print

License

Licensed under the Mozilla Public License, Version 2.0 (LICENSE).