// 20_cli_apps.ruchy - Building command-line applications
import std::env
import std::process
fn main() {
println("=== CLI Application Examples ===\n")
// Command-line argument parsing
let args = env::args()
println("=== Basic Argument Parsing ===")
println(f"Program: {args[0]}")
println(f"Arguments: {args.slice(1)}")
// Simple CLI parser
fn parse_args(args) {
let mut options = {
verbose: false,
output: None,
input: None,
help: false
}
let mut positional = []
let mut i = 1 // Skip program name
while i < args.len() {
let arg = args[i]
if arg.starts_with("--") {
// Long option
let option = arg.slice(2)
if option == "verbose" {
options.verbose = true
} else if option == "help" {
options.help = true
} else if option.starts_with("output=") {
options.output = Some(option.split("=")[1])
}
} else if arg.starts_with("-") {
// Short option
let flags = arg.slice(1)
for flag in flags.chars() {
match flag {
'v' => options.verbose = true,
'h' => options.help = true,
'o' => {
if i + 1 < args.len() {
i += 1
options.output = Some(args[i])
}
},
_ => println(f"Unknown flag: -{flag}")
}
}
} else {
// Positional argument
positional.append(arg)
}
i += 1
}
{ options: options, args: positional }
}
// Help text generation
fn show_help(program_name) {
let help_text = f"
{program_name} - A sample CLI application
USAGE:
{program_name} [OPTIONS] [ARGS]
OPTIONS:
-h, --help Show this help message
-v, --verbose Enable verbose output
-o, --output FILE Specify output file
ARGUMENTS:
<input> Input file or data
EXAMPLES:
{program_name} input.txt
{program_name} -v --output=result.txt data.csv
{program_name} -vo output.log input.json
"
println(help_text)
}
// Interactive prompt
fn prompt(message, default = None) {
print(f"{message}")
if default != None {
print(f" [{default}]")
}
print(": ")
let input = readline()
if input.trim() == "" && default != None {
default
} else {
input.trim()
}
}
// Confirmation prompt
fn confirm(message) {
let response = prompt(f"{message} (y/n)", "n")
response.lower() == "y" || response.lower() == "yes"
}
// Progress bar
fn show_progress(current, total, width = 50) {
let percent = current / total
let filled = (width * percent).to_int()
let empty = width - filled
let bar = "█".repeat(filled) + "░".repeat(empty)
print(f"\r[{bar}] {(percent * 100).to_int()}%")
if current >= total {
println("") // New line when complete
}
}
// Spinner for long operations
fn spinner(message) {
let frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
let mut i = 0
{
update: || {
print(f"\r{frames[i]} {message}")
i = (i + 1) % frames.len()
},
stop: || {
print(f"\r✓ {message}\n")
}
}
}
// Table output
fn print_table(headers, rows) {
// Calculate column widths
let mut widths = headers.map(h => h.len())
for row in rows {
for (i, cell) in row.enumerate() {
widths[i] = max(widths[i], cell.to_string().len())
}
}
// Print header
let header_line = headers.enumerate().map((i, h) =>
h.pad_right(widths[i])
).join(" | ")
println(header_line)
println("-".repeat(header_line.len()))
// Print rows
for row in rows {
let row_line = row.enumerate().map((i, cell) =>
cell.to_string().pad_right(widths[i])
).join(" | ")
println(row_line)
}
}
// Color output
module colors {
pub fn red(text) { f"\x1b[31m{text}\x1b[0m" }
pub fn green(text) { f"\x1b[32m{text}\x1b[0m" }
pub fn yellow(text) { f"\x1b[33m{text}\x1b[0m" }
pub fn blue(text) { f"\x1b[34m{text}\x1b[0m" }
pub fn bold(text) { f"\x1b[1m{text}\x1b[0m" }
pub fn underline(text) { f"\x1b[4m{text}\x1b[0m" }
}
// Example CLI application
fn todo_app() {
println("\n=== TODO List CLI ===")
let mut todos = []
loop {
println("\n1. Add task")
println("2. List tasks")
println("3. Mark complete")
println("4. Remove task")
println("5. Exit")
let choice = prompt("Choose an option")
match choice {
"1" => {
let task = prompt("Enter task")
todos.append({ task: task, done: false })
println(colors::green("✓ Task added"))
},
"2" => {
if todos.len() == 0 {
println(colors::yellow("No tasks"))
} else {
for (i, todo) in todos.enumerate() {
let status = if todo.done { "✓" } else { "○" }
let text = if todo.done {
colors::green(todo.task)
} else {
todo.task
}
println(f"{i + 1}. {status} {text}")
}
}
},
"3" => {
let index = prompt("Task number").to_int() - 1
if index >= 0 && index < todos.len() {
todos[index].done = true
println(colors::green("✓ Marked complete"))
} else {
println(colors::red("Invalid task number"))
}
},
"4" => {
let index = prompt("Task number").to_int() - 1
if index >= 0 && index < todos.len() {
todos.remove(index)
println(colors::green("✓ Task removed"))
} else {
println(colors::red("Invalid task number"))
}
},
"5" => {
println("Goodbye!")
break
},
_ => println(colors::red("Invalid option"))
}
}
}
// File processor CLI
fn file_processor_cli() {
let parsed = parse_args(env::args())
if parsed.options.help {
show_help("file_processor")
return
}
if parsed.args.len() == 0 {
println(colors::red("Error: No input file specified"))
show_help("file_processor")
return
}
let input_file = parsed.args[0]
let output_file = parsed.options.output || f"{input_file}.processed"
println(f"Processing: {input_file}")
if parsed.options.verbose {
println(f"Output will be saved to: {output_file}")
}
// Simulate processing with progress bar
let total_steps = 100
for i in 0..=total_steps {
show_progress(i, total_steps)
sleep_ms(10) // Simulate work
}
println(colors::green(f"✓ Processing complete: {output_file}"))
}
// Configuration management
fn load_config() {
let config_paths = [
env::home() + "/.config/myapp/config.json",
"./config.json",
"/etc/myapp/config.json"
]
for path in config_paths {
if fs::exists(path) {
let config = parse_json(fs::read_to_string(path))
println(f"Loaded config from: {path}")
return config
}
}
// Default config
{
verbose: false,
output_dir: "./output",
max_threads: 4
}
}
// Environment variables
fn check_environment() {
println("\n=== Environment Variables ===")
let home = env::get("HOME") || "Not set"
let path = env::get("PATH") || "Not set"
let custom = env::get("MY_APP_CONFIG") || "Not set"
println(f"HOME: {home}")
println(f"PATH: {path.split(":").take(3).join(":")}")
println(f"MY_APP_CONFIG: {custom}")
// Set environment variable
env::set("MY_APP_VAR", "Hello from Ruchy!")
println(f"Set MY_APP_VAR: {env::get('MY_APP_VAR')}")
}
// Run examples
let data = [
["Name", "Age", "City"],
["Alice", "30", "NYC"],
["Bob", "25", "SF"],
["Charlie", "35", "LA"]
]
print_table(data[0], data.slice(1))
// Example usage of progress bar
println("\n=== Progress Bar Demo ===")
for i in 0..=50 {
show_progress(i, 50)
sleep_ms(20)
}
check_environment()
// Uncomment to run interactive todo app
// todo_app()
}