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: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ jobs:
with:
mono-dev: packages
rust: stable
- run: task exec -- check
- run: task check
test:
runs-on: ubuntu-latest
steps:
- uses: Pistonight/mono-dev/actions/setup@main
with:
mono-dev: packages
rust: stable
- run: task exec -- test
- run: task test
doc:
runs-on: ubuntu-latest
steps:
- uses: Pistonight/mono-dev/actions/setup@main
with:
mono-dev: packages
rust: nightly
- run: task exec -- doc
- run: task doc
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ members = [
"packages/copper",
"packages/copper-proc-macros",
"packages/promethium",
"packages/terminal-tests",
]
3 changes: 3 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ includes:
cu: {taskfile: ./packages/copper, dir: ./packages/copper, internal: true}
cu-proc-macros: {taskfile: ./packages/copper-proc-macros, dir: ./packages/copper-proc-macros, internal: true}
pm: {taskfile: ./packages/promethium, dir: ./packages/promethium, internal: true}
terminal-tests: {taskfile: ./packages/terminal-tests, dir: ./packages/terminal-tests, internal: true}

tasks:
install-cargo-extra-tools:
Expand All @@ -33,11 +34,13 @@ tasks:
- task: pm:check
- task: cu-proc-macros:check
- task: cu:check
- task: terminal-tests:check

test:
cmds:
- task: pm:test
- task: cu:test
- task: terminal-tests:run

dev-doc:
cmds:
Expand Down
4 changes: 2 additions & 2 deletions packages/copper-proc-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pistonite-cu-proc-macros"
version = "0.2.4"
version = "0.2.5"
edition = "2024"
description = "Proc-macros for Cu"
repository = "https://github.com/Pistonite/cu"
Expand All @@ -12,7 +12,7 @@ exclude = [

[dependencies.pm]
package = "pistonite-pm"
version = "0.2.3"
version = "0.2.4"
path = "../promethium"
features = ["full"]

Expand Down
8 changes: 6 additions & 2 deletions packages/copper-proc-macros/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,14 @@ pub fn expand(attr: TokenStream, input: TokenStream) -> pm::Result<TokenStream2>
}
};

item.sig.ident = generated_main_name;
let old_name = {
let mut new_name = generated_main_name;
std::mem::swap(&mut new_name, &mut item.sig.ident);
new_name
};

let expanded = pm::quote! {
fn main() -> std::process::ExitCode {
fn #old_name() -> std::process::ExitCode {
unsafe { #main_impl }
}
#item
Expand Down
147 changes: 142 additions & 5 deletions packages/copper-proc-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,143 @@
use pm::pre::*;

/// See documentation for [`cu::cli`](../pistonite-cu/cli/index.html) module
/// **For the Command Line Interface feature set,
/// please refer to the [`cu::cli`](../pistonite-cu/cli/index.html) module.**
///
/// This is the documentation for the `#[cu::cli]` macro.
///
/// By annotating the main function, this macro generates
/// a shim that will reference the `cu::cli::Flags` command line
/// arguments and initialize logging, printing, and prompting
/// systems accordingly.
///
/// The main function can be async or sync. It should
/// return a `cu::Result`
/// ```rust,ignore
/// #[cu::cli]
/// fn main(flags: cu::cli::Flags) -> cu::Result<()> {
/// cu::debug!("flags are {flags:?}");
/// Ok(())
/// }
/// ```
///
/// To also define your own flags using the [`clap`](https://docs.rs/clap)
/// crate, define a CLI struct that derives `clap::Parser`.
/// Note the prelude import (`cu::pre::*`) automatically
/// brings `clap` into scope. You don't even need to add it
/// to `Cargo.toml`!
///
/// Make sure to `#[clap(flatten)]` the flags into your struct.
///
/// ```rust,ignore
/// # use pistonite_cu as cu;
/// use cu::pre::*;
/// /// My program
/// ///
/// /// This is my program, it is very good.
/// #[derive(clap::Parser, Clone)]
/// struct Args {
/// /// Input of the program
/// #[clap(short, long)]
/// input: String,
/// /// Output of the program
/// #[clap(short, long)]
/// output: Option<String>,
/// #[clap(flatten)]
/// inner: cu::cli::Flags,
/// }
/// ```
/// Now, to tell `cu` where to look for the flags,
/// specify the name of the field with `flags = "field"`
/// ```rust,ignore
/// // use the flags attribute to refer to the cu::cli::Flags field inside the Args struct
/// #[cu::cli(flags = "inner")]
/// fn main(args: Args) -> cu::Result<()> {
/// cu::info!("input is {}", args.input);
/// cu::info!("output is {:?}", args.output);
/// Ok(())
/// }
/// ```
///
/// Alternatively, implement `AsRef<cu::cli::Flag>` for your struct.
///
/// ```rust,ignore
/// # use pistonite_cu as cu;
/// use cu::pre::*;
/// #[derive(clap::Parser, Clone)]
/// struct Args {
/// input: String,
/// #[clap(flatten)]
/// inner: cu::cli::Flags,
/// }
/// impl AsRef<cu::cli::Flags> for Args {
/// fn as_ref(&self) -> cu::cli::Flags {
/// &self.inner
/// }
/// }
/// #[cu::cli]
/// fn main(_: Args) -> cu::Result<()> {
/// Ok(())
/// }
/// ```
///
/// Or enable the `derive` feature and derive `AsRef` (via [`derive_more`](https://docs.rs/derive_more)).
/// ```rust,ignore
/// # use pistonite_cu as cu;
/// use cu::pre::*;
/// #[derive(clap::Parser, Clone, AsRef)]
/// struct Args {
/// input: String,
/// #[clap(flatten)]
/// #[as_ref]
/// inner: cu::cli::Flags,
/// }
/// #[cu::cli]
/// fn main(_: Args) -> cu::Result<()> {
/// Ok(())
/// }
/// ```
///
/// The attribute can also take a `preprocess` function
/// to process flags before initializing the CLI system.
/// This can be useful to merge multiple Flags instance
/// in the CLI. Note that the logging/printing system
/// will not work during the preprocess.
///
/// ```rust,ignore
/// # use pistonite_cu as cu;
/// use cu::pre::*;
///
/// #[derive(clap::Parser)]
/// struct Args {
/// #[clap(subcommand)]
/// subcommand: Option<Command>,
/// #[clap(flatten)]
/// inner: cu::cli::Flags,
/// }
/// impl Args {
/// fn preprocess(&mut self) {
/// // merge subcommand flags into top level flags
/// // this way, both `-v foo` and `foo -v` will work
/// if let Some(Command::Foo(c)) = &self.subcommand {
/// self.inner.merge(c);
/// }
/// }
/// }
/// impl AsRef<cu::cli::Flags> for Args {
/// fn as_ref(&self) -> &cu::cli::Flags {
/// &self.inner
/// }
/// }
/// #[derive(clap::Subcommand)]
/// enum Command {
/// Foo(cu::cli::Flags),
/// }
/// #[cu::cli(preprocess = Args::preprocess)]
/// fn main(args: Args) -> cu::Result<()> {
/// Ok(())
/// }
/// ```
///
#[proc_macro_attribute]
pub fn cli(attr: TokenStream, input: TokenStream) -> TokenStream {
pm::flatten(cli::expand(attr, input))
Expand All @@ -16,10 +153,10 @@ mod derive_parse;

/// Attribute macro for wrapping a function with an error context
///
/// See the [tests](https://github.com/Pistonite/cu/blob/main/packages/copper/tests/error_ctx.rs)
/// See the [tests](https://github.com/Pistonite/cu/blob/main/packages/copper/tests/context.rs)
/// for examples
#[proc_macro_attribute]
pub fn error_ctx(attr: TokenStream, input: TokenStream) -> TokenStream {
pm::flatten(error_ctx::expand(attr, input))
pub fn context(attr: TokenStream, input: TokenStream) -> TokenStream {
pm::flatten(context::expand(attr, input))
}
mod error_ctx;
mod context;
67 changes: 34 additions & 33 deletions packages/copper/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pistonite-cu"
version = "0.6.10"
version = "0.7.0"
edition = "2024"
description = "Battery-included common utils to speed up development of rust tools"
repository = "https://github.com/Pistonite/cu"
Expand All @@ -11,58 +11,60 @@ exclude = [
]

[dependencies]
pistonite-cu-proc-macros = { version = "0.2.4", path = "../copper-proc-macros" }
pistonite-cu-proc-macros = { version = "0.2.5", path = "../copper-proc-macros" }

# --- Always enabled ---
anyhow = "1.0.100"
log = "0.4.29"
oneshot = "0.1.11"

# printing/cli
# --- Command Line Interface ---
oneshot = { version = "0.1.11", optional = true }
env_filter = { version = "0.1.4", optional = true }
terminal_size = { version = "0.4.3", optional = true }
unicode-width = { version = "0.2.2", features = ["cjk"], optional = true }
clap = { version = "4.5.54", features = ["derive"], optional = true }
regex = { version = "1.12.2", optional = true }

# fs
# --- Coroutine ---
num_cpus = { version = "1.17.0", optional = true }
tokio = { version = "1.49.0", optional = true, features = [
"macros", "rt-multi-thread"
] }

# --- File System ---
dunce = {version="1.0.5", optional = true}
which = {version = "8.0.0", optional = true }
pathdiff = {version = "0.2.3", optional=true}
spin = {version = "0.10.0", optional = true}
filetime = { version = "0.2.26", optional = true}
glob = { version = "0.3.3", optional = true }
spin = {version = "0.10.0", optional = true} # for PIO

# serde
serde = { version = "1.0.228", features = ["derive"], optional = true }
serde_json = { version = "1.0.148", optional = true }
serde_json = { version = "1.0.149", optional = true }
serde_yaml_ng = { version = "0.10.0", optional = true }
toml = { version = "0.9.10", optional = true }
toml = { version = "0.9.11", optional = true }

# derive
derive_more = { version = "2.1.1", features = ["full"], optional = true }

# coroutine
num_cpus = { version = "1.17.0", optional = true }

[target.'cfg(unix)'.dependencies]
libc = "0.2.179"
libc = "0.2.180"

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.61.2", features = ["Win32_Foundation", "Win32_System_Console", "Win32_Storage_FileSystem", "Win32_Security", "Win32_System_SystemServices"] }

[dependencies.tokio]
version = "1.49.0"
features = [ "macros", "rt-multi-thread" ]
optional = true
[dev-dependencies]

[dev-dependencies.tokio]
version = "1.49.0"
features = [ "macros", "rt-multi-thread", "time" ]

[features]
default = []
full = [
"coroutine-heavy",
"cli",
"coroutine-heavy",
"prompt-password",
"process",
"json",
Expand All @@ -71,31 +73,31 @@ full = [
"derive",
]

# Command Line Interface (enables integration with `clap` and command line entry points)
# --- Command Line Interface ---
print = ["dep:oneshot", "dep:regex", "dep:env_filter", "dep:terminal_size", "dep:unicode-width"]
cli = ["dep:clap", "print"]
print = ["dep:regex", "dep:env_filter", "dep:terminal_size", "dep:unicode-width"]
# Utils to show prompt for user input in terminal
prompt = ["print"]
prompt-password = ["prompt"]
# Enable coroutine drivers, which allow interop with async

# --- Coroutine ---
coroutine = [
"dep:tokio", "dep:num_cpus",
"tokio/sync", "tokio/io-util", "tokio/io-std"
]
# Enable heavy coroutine drived by multi-threaded tokio runtime
coroutine-heavy = ["coroutine"]
# Enable spawning child processes
process = [
coroutine-heavy = ["coroutine"] # enable heavy coroutine drived by multi-threaded tokio runtime

# --- File System ---
process = [ # enable spawning child processes
"coroutine", "fs",
"dep:spin",
"tokio/process",
"tokio/time",
]
# Enable file system and path util
fs = [
fs = [ # enable file system and path util
"dep:which", "dep:pathdiff", "dep:dunce", "dep:filetime", "dep:glob",
"tokio?/fs"
]

# Enable parsing utils
parse = []
serde = ["dep:serde"]
Expand All @@ -114,9 +116,12 @@ release-nolog= ["log/release_max_level_off"]
release-nodebuglog = ["log/release_max_level_info"]
derive = ["dep:derive_more"]

# Internally used to enable test features
__test = []

[[example]]
name = "print"
required-features = ["prompt", "cli"]
name = "prompt_password"
required-features = ["prompt-password", "cli", "parse"]

[[example]]
name = "process"
Expand All @@ -129,7 +134,3 @@ required-features = ["fs", "cli"]
[[example]]
name = "cargo"
required-features = ["process", "cli", "json"]

[[example]]
name = "prompt_password"
required-features = ["cli", "prompt-password"]
Loading