Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 09:04:14 +08:00
commit 70c36b5eff
248 changed files with 47482 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
/// Basic Parser Template with Clap Derive Macros
///
/// This template demonstrates:
/// - Parser derive macro
/// - Argument attributes (short, long, default_value)
/// - PathBuf for file handling
/// - Boolean flags
/// - Doc comments as help text
use clap::Parser;
use std::path::PathBuf;
#[derive(Parser)]
#[command(name = "myapp")]
#[command(author = "Your Name <you@example.com>")]
#[command(version = "1.0.0")]
#[command(about = "A simple CLI application", long_about = None)]
struct Cli {
/// Input file to process
#[arg(short, long, value_name = "FILE")]
input: PathBuf,
/// Optional output file
#[arg(short, long)]
output: Option<PathBuf>,
/// Enable verbose output
#[arg(short, long)]
verbose: bool,
/// Number of items to process
#[arg(short = 'c', long, default_value_t = 10)]
count: usize,
/// Dry run mode (don't make changes)
#[arg(short = 'n', long)]
dry_run: bool,
}
fn main() {
let cli = Cli::parse();
if cli.verbose {
println!("Input file: {:?}", cli.input);
println!("Output file: {:?}", cli.output);
println!("Count: {}", cli.count);
println!("Dry run: {}", cli.dry_run);
}
// Check if input file exists
if !cli.input.exists() {
eprintln!("Error: Input file does not exist: {:?}", cli.input);
std::process::exit(1);
}
// Your processing logic here
println!("Processing {} with count {}...", cli.input.display(), cli.count);
if let Some(output) = cli.output {
if !cli.dry_run {
println!("Would write to: {}", output.display());
} else {
println!("Dry run: Skipping write to {}", output.display());
}
}
}

View File

@@ -0,0 +1,112 @@
/// Builder Pattern Template (Manual API)
///
/// This template demonstrates the builder API for advanced use cases:
/// - Dynamic CLI construction
/// - Runtime configuration
/// - Custom help templates
/// - Complex validation logic
///
/// Note: Prefer derive macros unless you need this level of control.
use clap::{Arg, ArgAction, ArgMatches, Command};
use std::path::PathBuf;
fn build_cli() -> Command {
Command::new("advanced-cli")
.version("1.0.0")
.author("Your Name <you@example.com>")
.about("Advanced CLI using builder pattern")
.arg(
Arg::new("input")
.short('i')
.long("input")
.value_name("FILE")
.help("Input file to process")
.required(true)
.value_parser(clap::value_parser!(PathBuf)),
)
.arg(
Arg::new("output")
.short('o')
.long("output")
.value_name("FILE")
.help("Output file (optional)")
.value_parser(clap::value_parser!(PathBuf)),
)
.arg(
Arg::new("verbose")
.short('v')
.long("verbose")
.help("Enable verbose output")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("count")
.short('c')
.long("count")
.value_name("NUM")
.help("Number of items to process")
.default_value("10")
.value_parser(clap::value_parser!(usize)),
)
.arg(
Arg::new("format")
.short('f')
.long("format")
.value_name("FORMAT")
.help("Output format")
.value_parser(["json", "yaml", "toml"])
.default_value("json"),
)
.arg(
Arg::new("tags")
.short('t')
.long("tag")
.value_name("TAG")
.help("Tags to apply (can be specified multiple times)")
.action(ArgAction::Append),
)
}
fn process_args(matches: &ArgMatches) {
let input = matches.get_one::<PathBuf>("input").unwrap();
let output = matches.get_one::<PathBuf>("output");
let verbose = matches.get_flag("verbose");
let count = *matches.get_one::<usize>("count").unwrap();
let format = matches.get_one::<String>("format").unwrap();
let tags: Vec<_> = matches
.get_many::<String>("tags")
.unwrap_or_default()
.map(|s| s.as_str())
.collect();
if verbose {
println!("Configuration:");
println!(" Input: {:?}", input);
println!(" Output: {:?}", output);
println!(" Count: {}", count);
println!(" Format: {}", format);
println!(" Tags: {:?}", tags);
}
// Your processing logic here
println!("Processing {} items from {}", count, input.display());
if !tags.is_empty() {
println!("Applying tags: {}", tags.join(", "));
}
if let Some(output_path) = output {
println!("Writing {} format to {}", format, output_path.display());
}
}
fn main() {
let matches = build_cli().get_matches();
process_args(&matches);
}
// Example usage:
//
// cargo run -- -i input.txt -o output.json -v -c 20 -f yaml -t alpha -t beta
// cargo run -- --input data.txt --format toml --tag important

View File

@@ -0,0 +1,99 @@
/// Environment Variable Integration Template
///
/// This template demonstrates:
/// - Reading from environment variables
/// - Fallback to CLI arguments
/// - Default values
/// - Sensitive data handling (API keys, tokens)
use clap::Parser;
use std::path::PathBuf;
#[derive(Parser)]
#[command(name = "envapp")]
#[command(about = "CLI with environment variable support")]
struct Cli {
/// API key (or set API_KEY env var)
///
/// Sensitive data like API keys should preferably be set via environment
/// variables to avoid exposing them in shell history or process lists.
#[arg(long, env = "API_KEY", hide_env_values = true)]
api_key: String,
/// Database URL (or set DATABASE_URL env var)
#[arg(long, env = "DATABASE_URL")]
database_url: String,
/// Log level: debug, info, warn, error
///
/// Defaults to "info" if not provided via CLI or LOG_LEVEL env var.
#[arg(long, env = "LOG_LEVEL", default_value = "info")]
log_level: String,
/// Configuration file path
///
/// Reads from CONFIG_FILE env var, or uses default if not specified.
#[arg(long, env = "CONFIG_FILE", default_value = "config.toml")]
config: PathBuf,
/// Number of workers (default from env or 4)
#[arg(long, env = "WORKER_COUNT", default_value_t = 4)]
workers: usize,
/// Enable debug mode
///
/// Can be set via DEBUG=1 or --debug flag
#[arg(long, env = "DEBUG", value_parser = clap::value_parser!(bool))]
debug: bool,
/// Host to bind to
#[arg(long, env = "HOST", default_value = "127.0.0.1")]
host: String,
/// Port to listen on
#[arg(short, long, env = "PORT", default_value_t = 8080)]
port: u16,
}
fn main() {
let cli = Cli::parse();
println!("Configuration loaded:");
println!(" Database URL: {}", cli.database_url);
println!(" API Key: {}...", &cli.api_key[..4.min(cli.api_key.len())]);
println!(" Log level: {}", cli.log_level);
println!(" Config file: {}", cli.config.display());
println!(" Workers: {}", cli.workers);
println!(" Debug mode: {}", cli.debug);
println!(" Host: {}", cli.host);
println!(" Port: {}", cli.port);
// Initialize logging based on log_level
match cli.log_level.to_lowercase().as_str() {
"debug" => println!("Log level set to DEBUG"),
"info" => println!("Log level set to INFO"),
"warn" => println!("Log level set to WARN"),
"error" => println!("Log level set to ERROR"),
_ => println!("Unknown log level: {}", cli.log_level),
}
// Your application logic here
println!("\nStarting application...");
println!("Listening on {}:{}", cli.host, cli.port);
}
// Example usage:
//
// 1. Set environment variables:
// export API_KEY="sk-1234567890abcdef"
// export DATABASE_URL="postgres://localhost/mydb"
// export LOG_LEVEL="debug"
// export WORKER_COUNT="8"
// cargo run
//
// 2. Override with CLI arguments:
// cargo run -- --api-key "other-key" --workers 16
//
// 3. Mix environment and CLI:
// export DATABASE_URL="postgres://localhost/mydb"
// cargo run -- --api-key "sk-1234" --debug

View File

@@ -0,0 +1,290 @@
/// Full-Featured CLI Template
///
/// This template combines all patterns:
/// - Parser derive with subcommands
/// - ValueEnum for type-safe options
/// - Environment variable support
/// - Custom value parsers
/// - Global arguments
/// - Comprehensive help text
use clap::{Parser, Subcommand, ValueEnum};
use std::path::PathBuf;
#[derive(Parser)]
#[command(name = "myapp")]
#[command(author = "Your Name <you@example.com>")]
#[command(version = "1.0.0")]
#[command(about = "A full-featured CLI application", long_about = None)]
#[command(propagate_version = true)]
struct Cli {
/// Configuration file path
#[arg(short, long, env = "CONFIG_FILE", global = true)]
config: Option<PathBuf>,
/// Enable verbose output
#[arg(short, long, global = true)]
verbose: bool,
/// Output format
#[arg(short, long, value_enum, global = true, default_value_t = Format::Text)]
format: Format,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Initialize a new project
Init {
/// Project directory
#[arg(default_value = ".")]
path: PathBuf,
/// Project template
#[arg(short, long, value_enum, default_value_t = Template::Basic)]
template: Template,
/// Skip interactive prompts
#[arg(short = 'y', long)]
yes: bool,
},
/// Build the project
Build {
/// Build mode
#[arg(short, long, value_enum, default_value_t = BuildMode::Debug)]
mode: BuildMode,
/// Number of parallel jobs
#[arg(short, long, value_parser = clap::value_parser!(u8).range(1..=32), default_value_t = 4)]
jobs: u8,
/// Target directory
#[arg(short, long, default_value = "target")]
target_dir: PathBuf,
/// Clean before building
#[arg(long)]
clean: bool,
},
/// Test the project
Test {
/// Test name pattern
pattern: Option<String>,
/// Run ignored tests
#[arg(long)]
ignored: bool,
/// Number of test threads
#[arg(long, value_parser = clap::value_parser!(usize).range(1..))]
test_threads: Option<usize>,
/// Show output for passing tests
#[arg(long)]
nocapture: bool,
},
/// Deploy the project
Deploy {
/// Deployment environment
#[arg(value_enum)]
environment: Environment,
/// Skip pre-deployment checks
#[arg(long)]
skip_checks: bool,
/// Deployment tag/version
#[arg(short, long)]
tag: Option<String>,
/// Deployment configuration
#[command(subcommand)]
config: Option<DeployConfig>,
},
}
#[derive(Subcommand)]
enum DeployConfig {
/// Configure database settings
Database {
/// Database URL
#[arg(long, env = "DATABASE_URL")]
url: String,
/// Run migrations
#[arg(long)]
migrate: bool,
},
/// Configure server settings
Server {
/// Server host
#[arg(long, default_value = "0.0.0.0")]
host: String,
/// Server port
#[arg(long, default_value_t = 8080, value_parser = port_in_range)]
port: u16,
/// Number of workers
#[arg(long, default_value_t = 4)]
workers: usize,
},
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
enum Format {
/// Human-readable text
Text,
/// JSON output
Json,
/// YAML output
Yaml,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
enum Template {
/// Basic template
Basic,
/// Full-featured template
Full,
/// Minimal template
Minimal,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
enum BuildMode {
/// Debug build with symbols
Debug,
/// Release build with optimizations
Release,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
enum Environment {
/// Development environment
Dev,
/// Staging environment
Staging,
/// Production environment
Prod,
}
use std::ops::RangeInclusive;
const PORT_RANGE: RangeInclusive<usize> = 1..=65535;
fn port_in_range(s: &str) -> Result<u16, String> {
let port: usize = s
.parse()
.map_err(|_| format!("`{}` isn't a valid port number", s))?;
if PORT_RANGE.contains(&port) {
Ok(port as u16)
} else {
Err(format!(
"port not in range {}-{}",
PORT_RANGE.start(),
PORT_RANGE.end()
))
}
}
fn main() {
let cli = Cli::parse();
if cli.verbose {
println!("Verbose mode enabled");
if let Some(config) = &cli.config {
println!("Using config: {}", config.display());
}
println!("Output format: {:?}", cli.format);
}
match &cli.command {
Commands::Init { path, template, yes } => {
println!("Initializing project at {}", path.display());
println!("Template: {:?}", template);
if *yes {
println!("Skipping prompts");
}
}
Commands::Build {
mode,
jobs,
target_dir,
clean,
} => {
if *clean {
println!("Cleaning target directory");
}
println!("Building in {:?} mode", mode);
println!("Using {} parallel jobs", jobs);
println!("Target directory: {}", target_dir.display());
}
Commands::Test {
pattern,
ignored,
test_threads,
nocapture,
} => {
println!("Running tests");
if let Some(pat) = pattern {
println!("Pattern: {}", pat);
}
if *ignored {
println!("Including ignored tests");
}
if let Some(threads) = test_threads {
println!("Test threads: {}", threads);
}
if *nocapture {
println!("Showing test output");
}
}
Commands::Deploy {
environment,
skip_checks,
tag,
config,
} => {
println!("Deploying to {:?}", environment);
if *skip_checks {
println!("⚠️ Skipping pre-deployment checks");
}
if let Some(version) = tag {
println!("Version: {}", version);
}
if let Some(deploy_config) = config {
match deploy_config {
DeployConfig::Database { url, migrate } => {
println!("Database URL: {}", url);
if *migrate {
println!("Running migrations");
}
}
DeployConfig::Server { host, port, workers } => {
println!("Server: {}:{}", host, port);
println!("Workers: {}", workers);
}
}
}
}
}
}
// Example usage:
//
// myapp init --template full
// myapp build --mode release --jobs 8 --clean
// myapp test integration --test-threads 4
// myapp deploy prod --tag v1.0.0 server --host 0.0.0.0 --port 443 --workers 16

View File

@@ -0,0 +1,139 @@
/// Subcommand Template with Clap
///
/// This template demonstrates:
/// - Subcommand derive macro
/// - Nested command structure
/// - Per-subcommand arguments
/// - Enum-based command routing
use clap::{Parser, Subcommand};
use std::path::PathBuf;
#[derive(Parser)]
#[command(name = "git-like")]
#[command(author, version, about, long_about = None)]
#[command(propagate_version = true)]
struct Cli {
/// Enable verbose output
#[arg(global = true, short, long)]
verbose: bool,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Initialize a new repository
Init {
/// Directory to initialize
#[arg(value_name = "DIR", default_value = ".")]
path: PathBuf,
/// Create a bare repository
#[arg(long)]
bare: bool,
},
/// Add files to staging area
Add {
/// Files to add
#[arg(value_name = "FILE", required = true)]
files: Vec<PathBuf>,
/// Add all files
#[arg(short = 'A', long)]
all: bool,
},
/// Commit staged changes
Commit {
/// Commit message
#[arg(short, long)]
message: String,
/// Amend previous commit
#[arg(long)]
amend: bool,
},
/// Remote repository operations
Remote {
#[command(subcommand)]
command: RemoteCommands,
},
}
#[derive(Subcommand)]
enum RemoteCommands {
/// Add a new remote
Add {
/// Remote name
name: String,
/// Remote URL
url: String,
},
/// Remove a remote
Remove {
/// Remote name
name: String,
},
/// List all remotes
List {
/// Show URLs
#[arg(short, long)]
verbose: bool,
},
}
fn main() {
let cli = Cli::parse();
match &cli.command {
Commands::Init { path, bare } => {
if cli.verbose {
println!("Initializing repository at {:?}", path);
}
println!(
"Initialized {} repository in {}",
if *bare { "bare" } else { "normal" },
path.display()
);
}
Commands::Add { files, all } => {
if *all {
println!("Adding all files");
} else {
println!("Adding {} file(s)", files.len());
if cli.verbose {
for file in files {
println!(" - {}", file.display());
}
}
}
}
Commands::Commit { message, amend } => {
if *amend {
println!("Amending previous commit");
}
println!("Committing with message: {}", message);
}
Commands::Remote { command } => match command {
RemoteCommands::Add { name, url } => {
println!("Adding remote '{}' -> {}", name, url);
}
RemoteCommands::Remove { name } => {
println!("Removing remote '{}'", name);
}
RemoteCommands::List { verbose } => {
println!("Listing remotes{}", if *verbose { " (verbose)" } else { "" });
}
},
}
}

View File

@@ -0,0 +1,143 @@
/// ValueEnum Template for Type-Safe Options
///
/// This template demonstrates:
/// - ValueEnum trait for constrained choices
/// - Type-safe option selection
/// - Automatic validation and help text
/// - Pattern matching on enums
use clap::{Parser, ValueEnum};
/// Output format options
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
enum Format {
/// JavaScript Object Notation
Json,
/// YAML Ain't Markup Language
Yaml,
/// Tom's Obvious, Minimal Language
Toml,
/// Comma-Separated Values
Csv,
}
/// Log level options
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
enum LogLevel {
/// Detailed debug information
Debug,
/// General information
Info,
/// Warning messages
Warn,
/// Error messages only
Error,
}
/// Color output mode
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
enum ColorMode {
/// Always use colors
Always,
/// Never use colors
Never,
/// Automatically detect (default)
Auto,
}
#[derive(Parser)]
#[command(name = "converter")]
#[command(about = "Convert data between formats with type-safe options")]
struct Cli {
/// Input file
input: std::path::PathBuf,
/// Output format
#[arg(short, long, value_enum, default_value_t = Format::Json)]
format: Format,
/// Log level
#[arg(short, long, value_enum, default_value_t = LogLevel::Info)]
log_level: LogLevel,
/// Color mode for output
#[arg(long, value_enum, default_value_t = ColorMode::Auto)]
color: ColorMode,
/// Pretty print output (for supported formats)
#[arg(short, long)]
pretty: bool,
}
fn main() {
let cli = Cli::parse();
// Configure logging based on log level
match cli.log_level {
LogLevel::Debug => println!("🔍 Debug logging enabled"),
LogLevel::Info => println!(" Info logging enabled"),
LogLevel::Warn => println!("⚠️ Warning logging enabled"),
LogLevel::Error => println!("❌ Error logging only"),
}
// Check color mode
let use_colors = match cli.color {
ColorMode::Always => true,
ColorMode::Never => false,
ColorMode::Auto => atty::is(atty::Stream::Stdout),
};
if use_colors {
println!("🎨 Color output enabled");
}
// Process based on format
println!("Converting {} to {:?}", cli.input.display(), cli.format);
match cli.format {
Format::Json => {
println!("Converting to JSON{}", if cli.pretty { " (pretty)" } else { "" });
// JSON conversion logic here
}
Format::Yaml => {
println!("Converting to YAML");
// YAML conversion logic here
}
Format::Toml => {
println!("Converting to TOML");
// TOML conversion logic here
}
Format::Csv => {
println!("Converting to CSV");
// CSV conversion logic here
}
}
println!("✓ Conversion complete");
}
// Helper function to check if stdout is a terminal (for color auto-detection)
mod atty {
pub enum Stream {
Stdout,
}
pub fn is(_stream: Stream) -> bool {
// Simple implementation - checks if stdout is a TTY
#[cfg(unix)]
{
unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
}
#[cfg(not(unix))]
{
false
}
}
}
// Example usage:
//
// cargo run -- input.txt --format json --log-level debug
// cargo run -- data.yml --format toml --color always --pretty
// cargo run -- config.json --format yaml --log-level warn

View File

@@ -0,0 +1,109 @@
/// Value Parser Template with Custom Validation
///
/// This template demonstrates:
/// - Custom value parsers
/// - Range validation
/// - Format validation (regex)
/// - Error handling with helpful messages
use clap::Parser;
use std::ops::RangeInclusive;
const PORT_RANGE: RangeInclusive<usize> = 1..=65535;
/// Parse and validate port number
fn port_in_range(s: &str) -> Result<u16, String> {
let port: usize = s
.parse()
.map_err(|_| format!("`{}` isn't a valid port number", s))?;
if PORT_RANGE.contains(&port) {
Ok(port as u16)
} else {
Err(format!(
"port not in range {}-{}",
PORT_RANGE.start(),
PORT_RANGE.end()
))
}
}
/// Validate email format (basic validation)
fn validate_email(s: &str) -> Result<String, String> {
if s.contains('@') && s.contains('.') && s.len() > 5 {
Ok(s.to_string())
} else {
Err(format!("`{}` is not a valid email address", s))
}
}
/// Parse percentage (0-100)
fn parse_percentage(s: &str) -> Result<u8, String> {
let value: u8 = s
.parse()
.map_err(|_| format!("`{}` isn't a valid number", s))?;
if value <= 100 {
Ok(value)
} else {
Err("percentage must be between 0 and 100".to_string())
}
}
/// Validate directory exists
fn validate_directory(s: &str) -> Result<std::path::PathBuf, String> {
let path = std::path::PathBuf::from(s);
if path.exists() && path.is_dir() {
Ok(path)
} else {
Err(format!("directory does not exist: {}", s))
}
}
#[derive(Parser)]
#[command(name = "validator")]
#[command(about = "CLI with custom value parsers and validation")]
struct Cli {
/// Port number (1-65535)
#[arg(short, long, value_parser = port_in_range)]
port: u16,
/// Email address
#[arg(short, long, value_parser = validate_email)]
email: String,
/// Success threshold percentage (0-100)
#[arg(short, long, value_parser = parse_percentage, default_value = "80")]
threshold: u8,
/// Working directory (must exist)
#[arg(short, long, value_parser = validate_directory)]
workdir: Option<std::path::PathBuf>,
/// Number of retries (1-10)
#[arg(
short,
long,
default_value = "3",
value_parser = clap::value_parser!(u8).range(1..=10)
)]
retries: u8,
}
fn main() {
let cli = Cli::parse();
println!("Configuration:");
println!(" Port: {}", cli.port);
println!(" Email: {}", cli.email);
println!(" Threshold: {}%", cli.threshold);
println!(" Retries: {}", cli.retries);
if let Some(workdir) = cli.workdir {
println!(" Working directory: {}", workdir.display());
}
// Your application logic here
println!("\nValidation passed! All inputs are valid.");
}