Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[workspace]
members = [
"crates/client",
"crates/example",
]
resolver = "3"
17 changes: 13 additions & 4 deletions crates/client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "process-compose-client"
version = "1.64.1"
version = "1.73.0"
description = "Client for Process Compose via OpenAPI and/or project file"
license = "Apache-2.0"
edition = "2021"
Expand All @@ -19,16 +19,25 @@ crate-type = ["rlib"]
serde_json = { version = "^1.0", default-features = false }
openapiv3 = { version = "^2", default-features = false }

progenitor = { version = "^0.10", default-features = false, optional = true }
progenitor = { version = "^0.11", default-features = false, optional = true }
prettyplease = { version = "^0.2.24", optional = true }
syn = { version = "^2.0.80", optional = true }
schemars = { version = "^0.8.*" }
schemars = { version = "^0.8.*" } # update to 1.0 blocked by typify

typify = { version = "^0.4", default-features = false, optional = true }

process-wrap = { version = "9.0", default-features = false, optional = true, features = ["tokio1", "kill-on-drop", ]}


clap = {version = "4.5", default-features = false, features = ["derive", "std", "env", "help", "usage", "error-context", "suggestions"], optional = true}
bon = { version = "3.7", default-features = false, optional = true}
struct_field_names = { version = "0.2", default-features = false, optional = true}

[features]
default = ["progenitor", "typify"]
default = ["progenitor", "typify", "cli"]

typify = ["dep:typify", "dep:prettyplease"]

progenitor = ["dep:progenitor", "dep:prettyplease", "dep:syn"]

cli = ["dep:process-wrap", "dep:bon", "dep:clap", "dep:struct_field_names"]
5 changes: 4 additions & 1 deletion crates/client/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@

Build time utility to get Rust Process Compose interface:
Compile time and runtime utility to get Rust Process Compose interface.

# Features

- as raw OpenAPI schema or with `progenitor` client.
- as raw project config JSON schema or with `typify` builder.
- as Rust "native" crate behind `cli` providing process-wrap handle produced from command line args builder
42 changes: 42 additions & 0 deletions crates/client/src/cli/cmd/flags.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//! Keep in sync with `flags.go`.
use std::time::Duration;

/// Default refresh interval
pub const DEFAULT_REFRESH_RATE: Duration = Duration::from_secs(1);

/// Default log level
pub const DEFAULT_LOG_LEVEL: &str = "info";

/// Default port number
pub const DEFAULT_PORT_NUM: u16 = 8080;

/// Default bind address (host)
pub const DEFAULT_ADDRESS: &str = "localhost";

/// Default log length (number of lines kept in memory)
pub const DEFAULT_LOG_LENGTH: usize = 1000;

/// Default sort column name
pub const DEFAULT_SORT_COLUMN: &str = "NAME";

/// Default theme name
pub const DEFAULT_THEME_NAME: &str = "Default";

/// Represents absence of a namespace selection
pub const NO_NAMESPACE: &str = "";

pub mod env {
pub const PORT_NUM: &str = "PC_PORT_NUM";
pub const DISABLE_TUI: &str = "PC_DISABLE_TUI";
pub const CONFIG_FILES: &str = "PC_CONFIG_FILES";
pub const SHORTCUTS_FILES: &str = "PC_SHORTCUTS_FILES";
pub const NO_SERVER: &str = "PC_NO_SERVER";
pub const SOCKET_PATH: &str = "PC_SOCKET_PATH";
pub const READ_ONLY: &str = "PC_READ_ONLY";
pub const DISABLE_DOTENV: &str = "PC_DISABLE_DOTENV";
pub const TUI_FULL_SCREEN: &str = "PC_TUI_FULL_SCREEN";
pub const HIDE_DISABLED_PROC: &str = "PC_HIDE_DISABLED_PROC";
pub const ORDERED_SHUTDOWN: &str = "PC_ORDERED_SHUTDOWN";
pub const RECURSIVE_METRICS: &str = "PC_RECURSIVE_METRICS";
pub const LOG_FILE: &str = "PC_LOG_FILE";
}
14 changes: 14 additions & 0 deletions crates/client/src/cli/cmd/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Define macro before submodules so it's in scope for them
macro_rules! arg {
($ty:ty, $field:ident) => {{
<$ty as clap::CommandFactory>::command()
.get_arguments()
.find(|a| a.get_id() == <$ty>::FIELD_NAMES.$field)
.and_then(|a| a.get_long())
.expect("long argument name not found")
}};
}

pub mod flags;
pub mod parent;
pub mod up;
116 changes: 116 additions & 0 deletions crates/client/src/cli/cmd/parent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
use clap::{Parser, Subcommand};
use std::path::PathBuf;

use crate::cli::cmd::flags::env;
use crate::cli::cmd::flags::DEFAULT_PORT_NUM;
use crate::cli::cmd::up::ProcessComposeFlagsUp;
/// Process Compose startup flags
#[derive(Debug, Clone, Parser, struct_field_names::StructFieldNames, bon::Builder)]
pub struct ProcessComposeFlags {
/// Specify the log file path (env: PC_LOG_FILE)
/// Default "/tmp/process-compose-<user>.log"
#[arg(long = "log-file", env = "PC_LOG_FILE")]
pub log_file: Option<PathBuf>,

/// Disable HTTP server (env: PC_NO_SERVER)
#[arg(long = "no-server", env = env::NO_SERVER)]
#[builder(default = false)]
pub no_server: bool,

/// Shut down processes in reverse dependency order (env: PC_ORDERED_SHUTDOWN)
#[arg(long = "ordered-shutdown", env = env::ORDERED_SHUTDOWN)]
#[builder(default = false)]
pub ordered_shutdown: bool,

/// Port number (env: PC_PORT_NUM)
#[arg(long = "port", env = env::PORT_NUM, default_value_t = DEFAULT_PORT_NUM)]
#[builder(default = DEFAULT_PORT_NUM)]
pub port: u16,

/// Enable read-only mode (env: PC_READ_ONLY)
#[arg(long = "read-only", env = env::READ_ONLY)]
#[builder(default = false)]
pub read_only: bool,

/// Path to unix socket (env: PC_SOCKET_PATH)
/// Default "/tmp/process-compose-<pid>.sock"
#[arg(long = "unix-socket", env = env::SOCKET_PATH)]
pub unix_socket: Option<PathBuf>,

/// Use unix domain sockets instead of TCP
#[arg(long = "use-uds", default_value_t = false)]
#[builder(default = false)]
pub use_uds: bool,

#[command(subcommand)]
pub subcommand: Option<ProcessComposeCommand>,
}

#[derive(Debug, Clone, Subcommand, struct_field_names::EnumVariantNames)]
pub enum ProcessComposeCommand {
/// Run process compose project
Up(ProcessComposeFlagsUp),
}

impl ProcessComposeCommand {
pub fn up(value: ProcessComposeFlagsUp) -> Self {
Self::Up(value)
}
}

impl TryInto<Vec<String>> for ProcessComposeCommand {
type Error = String;

fn try_into(self) -> Result<Vec<String>, Self::Error> {
let mut args = Vec::new();
match self {
ProcessComposeCommand::Up(up) => {
args.push("up".to_string());
let up_args: Vec<String> = up.try_into()?;
args.extend(up_args);
}
}
Ok(args)
}
}

impl TryInto<Vec<String>> for ProcessComposeFlags {
type Error = String;

fn try_into(self) -> Result<Vec<String>, Self::Error> {
let mut args = Vec::new();

if let Some(path) = self.log_file {
args.push(format!("--{}", arg!(ProcessComposeFlags, log_file)));
args.push(path.to_string_lossy().to_string());
}
if self.no_server {
args.push(format!("--{}", arg!(ProcessComposeFlags, no_server)));
}
if self.ordered_shutdown {
args.push(format!("--{}", arg!(ProcessComposeFlags, ordered_shutdown)));
}

// Always include port to be explicit
args.push(format!("--{}", arg!(ProcessComposeFlags, port)));
args.push(self.port.to_string());

if self.read_only {
args.push(format!("--{}", arg!(ProcessComposeFlags, read_only)));
}
if let Some(sock) = self.unix_socket {
args.push(format!("--{}", arg!(ProcessComposeFlags, unix_socket)));
args.push(sock.to_string_lossy().to_string());
}
if self.use_uds {
args.push(format!("--{}", arg!(ProcessComposeFlags, use_uds)));
}

if let Some(sub) = self.subcommand {
let mut sub_args: Vec<String> = sub.try_into()?;
args.append(&mut sub_args);
}

Ok(args)
}
}
Loading