☕ #cli - CLI Directive (Java)
#cli - CLI Directive (Java)
The #cli
directive provides enterprise-grade command-line interface capabilities for Java applications, enabling powerful CLI tools with Spring Boot integration and comprehensive command processing.
Basic Syntax
Basic CLI command
#cli command: "hello" {
@print("Hello, World!")
}CLI with arguments
#cli command: "greet" {
name: @args.name || "World"
@print("Hello, {name}!")
}CLI with options
#cli command: "process" {
input: @args.input
output: @args.output || "output.txt"
verbose: @args.verbose || false
@process_file(input, output, verbose)
}
Java Implementation
import org.tusklang.java.TuskLang;
import org.tusklang.java.directives.CliDirective;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;@Component
public class CliCommands implements CommandLineRunner {
private final TuskLang tuskLang;
private final CliDirective cliDirective;
private final FileProcessor fileProcessor;
public CliCommands(TuskLang tuskLang, FileProcessor fileProcessor) {
this.tuskLang = tuskLang;
this.cliDirective = new CliDirective();
this.fileProcessor = fileProcessor;
}
@Override
public void run(String... args) throws Exception {
CommandLine cli = new CommandLine(new CliApplication());
cli.execute(args);
}
}
@Command(name = "tusk-cli", mixinStandardHelpOptions = true, version = "1.0.0")
public class CliApplication {
@Command(name = "hello", description = "Print hello message")
public void hello() {
System.out.println("Hello, World!");
}
@Command(name = "greet", description = "Greet a person")
public void greet(@Option(names = {"-n", "--name"}, description = "Name to greet") String name) {
String greeting = "Hello, " + (name != null ? name : "World") + "!";
System.out.println(greeting);
}
@Command(name = "process", description = "Process a file")
public void process(@Option(names = {"-i", "--input"}, required = true, description = "Input file") String input,
@Option(names = {"-o", "--output"}, description = "Output file") String output,
@Option(names = {"-v", "--verbose"}, description = "Verbose output") boolean verbose) {
String outputFile = output != null ? output : "output.txt";
fileProcessor.processFile(input, outputFile, verbose);
}
}
CLI Configuration
Detailed CLI configuration
#cli {
command: "complex"
description: "Complex command with multiple options"
version: "1.0.0"
help: true
options: {
input: {required: true, type: "string", description: "Input file"}
output: {type: "string", default: "output.txt", description: "Output file"}
verbose: {type: "boolean", default: false, description: "Verbose output"}
format: {type: "string", choices: ["json", "xml", "csv"], description: "Output format"}
}
} {
@process_complex_command(@args.input, @args.output, @args.verbose, @args.format)
}CLI with subcommands
#cli {
command: "database"
subcommands: {
backup: {
description: "Backup database"
options: {
database: {required: true, description: "Database name"}
output: {description: "Backup file path"}
}
}
restore: {
description: "Restore database"
options: {
database: {required: true, description: "Database name"}
input: {required: true, description: "Backup file path"}
}
}
}
} {
@handle_database_command(@args.subcommand, @args)
}
Java CLI Configuration
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.List;@Component
@ConfigurationProperties(prefix = "tusk.cli")
public class CliConfig {
private String defaultCommand = "help";
private String version = "1.0.0";
private boolean helpEnabled = true;
private Map<String, CliCommandDefinition> commands;
private Map<String, String> aliases;
// Getters and setters
public String getDefaultCommand() { return defaultCommand; }
public void setDefaultCommand(String defaultCommand) { this.defaultCommand = defaultCommand; }
public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }
public boolean isHelpEnabled() { return helpEnabled; }
public void setHelpEnabled(boolean helpEnabled) { this.helpEnabled = helpEnabled; }
public Map<String, CliCommandDefinition> getCommands() { return commands; }
public void setCommands(Map<String, CliCommandDefinition> commands) { this.commands = commands; }
public Map<String, String> getAliases() { return aliases; }
public void setAliases(Map<String, String> aliases) { this.aliases = aliases; }
public static class CliCommandDefinition {
private String description;
private String version;
private boolean help;
private Map<String, CliOptionDefinition> options;
private Map<String, CliSubcommandDefinition> subcommands;
// Getters and setters
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }
public boolean isHelp() { return help; }
public void setHelp(boolean help) { this.help = help; }
public Map<String, CliOptionDefinition> getOptions() { return options; }
public void setOptions(Map<String, CliOptionDefinition> options) { this.options = options; }
public Map<String, CliSubcommandDefinition> getSubcommands() { return subcommands; }
public void setSubcommands(Map<String, CliSubcommandDefinition> subcommands) { this.subcommands = subcommands; }
}
public static class CliOptionDefinition {
private boolean required;
private String type;
private String defaultValue;
private String description;
private List<String> choices;
// Getters and setters
public boolean isRequired() { return required; }
public void setRequired(boolean required) { this.required = required; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public String getDefaultValue() { return defaultValue; }
public void setDefaultValue(String defaultValue) { this.defaultValue = defaultValue; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public List<String> getChoices() { return choices; }
public void setChoices(List<String> choices) { this.choices = choices; }
}
public static class CliSubcommandDefinition {
private String description;
private Map<String, CliOptionDefinition> options;
// Getters and setters
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public Map<String, CliOptionDefinition> options() { return options; }
public void setOptions(Map<String, CliOptionDefinition> options) { this.options = options; }
}
}
@Command(name = "tusk-cli", mixinStandardHelpOptions = true, version = "1.0.0")
public class ComplexCliApplication {
@Command(name = "complex", description = "Complex command with multiple options")
public void complex(@Option(names = {"-i", "--input"}, required = true, description = "Input file") String input,
@Option(names = {"-o", "--output"}, description = "Output file") String output,
@Option(names = {"-v", "--verbose"}, description = "Verbose output") boolean verbose,
@Option(names = {"-f", "--format"}, description = "Output format") String format) {
String outputFile = output != null ? output : "output.txt";
String outputFormat = format != null ? format : "json";
processComplexCommand(input, outputFile, verbose, outputFormat);
}
private void processComplexCommand(String input, String output, boolean verbose, String format) {
if (verbose) {
System.out.println("Processing input: " + input);
System.out.println("Output file: " + output);
System.out.println("Format: " + format);
}
// Process the command
fileProcessor.processFile(input, output, format);
if (verbose) {
System.out.println("Processing completed successfully");
}
}
}
@Command(name = "database", description = "Database operations")
public class DatabaseCli {
@Command(name = "backup", description = "Backup database")
public void backup(@Option(names = {"-d", "--database"}, required = true, description = "Database name") String database,
@Option(names = {"-o", "--output"}, description = "Backup file path") String output) {
String backupPath = output != null ? output : database + "_backup.sql";
databaseService.backupDatabase(database, backupPath);
System.out.println("Database backup completed: " + backupPath);
}
@Command(name = "restore", description = "Restore database")
public void restore(@Option(names = {"-d", "--database"}, required = true, description = "Database name") String database,
@Option(names = {"-i", "--input"}, required = true, description = "Backup file path") String input) {
databaseService.restoreDatabase(database, input);
System.out.println("Database restore completed: " + database);
}
}
File Processing Commands
File processing CLI
#cli command: "process-file" {
input: @args.input
output: @args.output
format: @args.format || "json"
@process_file_with_format(input, output, format)
}Batch file processing
#cli command: "batch-process" {
directory: @args.directory
pattern: @args.pattern || "*.txt"
output_dir: @args.output_dir || "./output"
@batch_process_files(directory, pattern, output_dir)
}File conversion
#cli command: "convert" {
input: @args.input
from_format: @args.from_format
to_format: @args.to_format
output: @args.output
@convert_file_format(input, from_format, to_format, output)
}
Java File Processing Commands
import org.springframework.stereotype.Component;
import java.nio.file.*;
import java.util.stream.Stream;@Component
public class FileProcessingCommands {
private final FileProcessor fileProcessor;
private final FileConverter fileConverter;
public FileProcessingCommands(FileProcessor fileProcessor, FileConverter fileConverter) {
this.fileProcessor = fileProcessor;
this.fileConverter = fileConverter;
}
@Command(name = "process-file", description = "Process a single file")
public void processFile(@Option(names = {"-i", "--input"}, required = true, description = "Input file") String input,
@Option(names = {"-o", "--output"}, description = "Output file") String output,
@Option(names = {"-f", "--format"}, description = "Output format") String format) {
String outputFile = output != null ? output : input + ".processed";
String outputFormat = format != null ? format : "json";
try {
fileProcessor.processFileWithFormat(input, outputFile, outputFormat);
System.out.println("File processed successfully: " + outputFile);
} catch (Exception e) {
System.err.println("Error processing file: " + e.getMessage());
System.exit(1);
}
}
@Command(name = "batch-process", description = "Process multiple files")
public void batchProcess(@Option(names = {"-d", "--directory"}, required = true, description = "Input directory") String directory,
@Option(names = {"-p", "--pattern"}, description = "File pattern") String pattern,
@Option(names = {"-o", "--output-dir"}, description = "Output directory") String outputDir) {
String filePattern = pattern != null ? pattern : "*.txt";
String outputDirectory = outputDir != null ? outputDir : "./output";
try {
Path inputPath = Paths.get(directory);
Path outputPath = Paths.get(outputDirectory);
if (!Files.exists(outputPath)) {
Files.createDirectories(outputPath);
}
try (Stream<Path> paths = Files.walk(inputPath)) {
paths.filter(Files::isRegularFile)
.filter(path -> path.toString().matches(filePattern.replace("", ".")))
.forEach(path -> {
try {
String relativePath = inputPath.relativize(path).toString();
Path outputFile = outputPath.resolve(relativePath + ".processed");
fileProcessor.processFile(path.toString(), outputFile.toString());
System.out.println("Processed: " + relativePath);
} catch (Exception e) {
System.err.println("Error processing " + path + ": " + e.getMessage());
}
});
}
System.out.println("Batch processing completed");
} catch (Exception e) {
System.err.println("Error during batch processing: " + e.getMessage());
System.exit(1);
}
}
@Command(name = "convert", description = "Convert file format")
public void convert(@Option(names = {"-i", "--input"}, required = true, description = "Input file") String input,
@Option(names = {"-f", "--from-format"}, required = true, description = "Source format") String fromFormat,
@Option(names = {"-t", "--to-format"}, required = true, description = "Target format") String toFormat,
@Option(names = {"-o", "--output"}, description = "Output file") String output) {
String outputFile = output != null ? output : input + "." + toFormat;
try {
fileConverter.convertFileFormat(input, fromFormat, toFormat, outputFile);
System.out.println("File converted successfully: " + outputFile);
} catch (Exception e) {
System.err.println("Error converting file: " + e.getMessage());
System.exit(1);
}
}
}
@Service
public class FileProcessor {
private final ObjectMapper objectMapper;
private final XmlMapper xmlMapper;
public FileProcessor(ObjectMapper objectMapper, XmlMapper xmlMapper) {
this.objectMapper = objectMapper;
this.xmlMapper = xmlMapper;
}
public void processFileWithFormat(String input, String output, String format) throws IOException {
// Read input file
String content = Files.readString(Paths.get(input));
// Process content based on format
String processedContent = processContent(content, format);
// Write output file
Files.writeString(Paths.get(output), processedContent);
}
public void processFile(String input, String output) throws IOException {
processFileWithFormat(input, output, "json");
}
private String processContent(String content, String format) throws IOException {
switch (format.toLowerCase()) {
case "json":
return processJsonContent(content);
case "xml":
return processXmlContent(content);
case "csv":
return processCsvContent(content);
default:
throw new IllegalArgumentException("Unsupported format: " + format);
}
}
private String processJsonContent(String content) throws IOException {
JsonNode jsonNode = objectMapper.readTree(content);
// Add processing timestamp
((ObjectNode) jsonNode).put("processed_at", LocalDateTime.now().toString());
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonNode);
}
private String processXmlContent(String content) throws IOException {
// Process XML content
return content; // Simplified
}
private String processCsvContent(String content) {
// Process CSV content
return content; // Simplified
}
}
@Service
public class FileConverter {
private final ObjectMapper objectMapper;
private final XmlMapper xmlMapper;
public FileConverter(ObjectMapper objectMapper, XmlMapper xmlMapper) {
this.objectMapper = objectMapper;
this.xmlMapper = xmlMapper;
}
public void convertFileFormat(String input, String fromFormat, String toFormat, String output) throws IOException {
// Read input file
String content = Files.readString(Paths.get(input));
// Convert content
String convertedContent = convertContent(content, fromFormat, toFormat);
// Write output file
Files.writeString(Paths.get(output), convertedContent);
}
private String convertContent(String content, String fromFormat, String toFormat) throws IOException {
// Convert between formats
if ("json".equals(fromFormat) && "xml".equals(toFormat)) {
JsonNode jsonNode = objectMapper.readTree(content);
return xmlMapper.writeValueAsString(jsonNode);
} else if ("xml".equals(fromFormat) && "json".equals(toFormat)) {
JsonNode jsonNode = xmlMapper.readTree(content);
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonNode);
} else {
throw new IllegalArgumentException("Unsupported conversion: " + fromFormat + " to " + toFormat);
}
}
}
Database Commands
Database CLI commands
#cli command: "db-backup" {
database: @args.database
output: @args.output || "{database}_backup.sql"
@backup_database(database, output)
}Database migration
#cli command: "db-migrate" {
direction: @args.direction || "up"
version: @args.version
@migrate_database(direction, version)
}Database query
#cli command: "db-query" {
query: @args.query
output: @args.output || "query_result.json"
format: @args.format || "json"
@execute_query(query, output, format)
}
Java Database Commands
import org.springframework.stereotype.Component;
import org.springframework.jdbc.core.JdbcTemplate;
import org.flywaydb.core.Flyway;@Component
public class DatabaseCommands {
private final DatabaseService databaseService;
private final JdbcTemplate jdbcTemplate;
private final Flyway flyway;
public DatabaseCommands(DatabaseService databaseService, JdbcTemplate jdbcTemplate, Flyway flyway) {
this.databaseService = databaseService;
this.jdbcTemplate = jdbcTemplate;
this.flyway = flyway;
}
@Command(name = "db-backup", description = "Backup database")
public void backupDatabase(@Option(names = {"-d", "--database"}, required = true, description = "Database name") String database,
@Option(names = {"-o", "--output"}, description = "Backup file path") String output) {
String backupPath = output != null ? output : database + "_backup.sql";
try {
databaseService.backupDatabase(database, backupPath);
System.out.println("Database backup completed: " + backupPath);
} catch (Exception e) {
System.err.println("Error backing up database: " + e.getMessage());
System.exit(1);
}
}
@Command(name = "db-migrate", description = "Run database migrations")
public void migrateDatabase(@Option(names = {"-d", "--direction"}, description = "Migration direction") String direction,
@Option(names = {"-v", "--version"}, description = "Target version") String version) {
String migrationDirection = direction != null ? direction : "up";
try {
if ("up".equals(migrationDirection)) {
if (version != null) {
flyway.migrateTo(version);
} else {
flyway.migrate();
}
System.out.println("Database migration completed");
} else if ("down".equals(migrationDirection)) {
if (version != null) {
flyway.undo(version);
} else {
flyway.undo();
}
System.out.println("Database migration rolled back");
} else {
throw new IllegalArgumentException("Invalid direction: " + migrationDirection);
}
} catch (Exception e) {
System.err.println("Error during migration: " + e.getMessage());
System.exit(1);
}
}
@Command(name = "db-query", description = "Execute database query")
public void executeQuery(@Option(names = {"-q", "--query"}, required = true, description = "SQL query") String query,
@Option(names = {"-o", "--output"}, description = "Output file") String output,
@Option(names = {"-f", "--format"}, description = "Output format") String format) {
String outputFile = output != null ? output : "query_result.json";
String outputFormat = format != null ? format : "json";
try {
List<Map<String, Object>> results = jdbcTemplate.queryForList(query);
String resultContent = formatResults(results, outputFormat);
Files.writeString(Paths.get(outputFile), resultContent);
System.out.println("Query executed successfully. Results saved to: " + outputFile);
} catch (Exception e) {
System.err.println("Error executing query: " + e.getMessage());
System.exit(1);
}
}
private String formatResults(List<Map<String, Object>> results, String format) throws IOException {
switch (format.toLowerCase()) {
case "json":
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(results);
case "csv":
return convertToCsv(results);
case "xml":
return xmlMapper.writeValueAsString(results);
default:
throw new IllegalArgumentException("Unsupported format: " + format);
}
}
private String convertToCsv(List<Map<String, Object>> results) {
if (results.isEmpty()) {
return "";
}
StringBuilder csv = new StringBuilder();
// Write headers
String[] headers = results.get(0).keySet().toArray(new String[0]);
csv.append(String.join(",", headers)).append("\n");
// Write data
for (Map<String, Object> row : results) {
String[] values = new String[headers.length];
for (int i = 0; i < headers.length; i++) {
Object value = row.get(headers[i]);
values[i] = value != null ? value.toString() : "";
}
csv.append(String.join(",", values)).append("\n");
}
return csv.toString();
}
}
@Service
public class DatabaseService {
private final DataSource dataSource;
public DatabaseService(DataSource dataSource) {
this.dataSource = dataSource;
}
public void backupDatabase(String database, String outputPath) throws IOException {
try {
// Create backup directory if it doesn't exist
Path outputDir = Paths.get(outputPath).getParent();
if (outputDir != null && !Files.exists(outputDir)) {
Files.createDirectories(outputDir);
}
// Perform database backup
ProcessBuilder pb = new ProcessBuilder(
"mysqldump",
"--host=" + getDatabaseHost(),
"--port=" + getDatabasePort(),
"--user=" + getDatabaseUser(),
"--password=" + getDatabasePassword(),
"--single-transaction",
"--routines",
"--triggers",
database
);
Process process = pb.start();
// Write backup to file
try (FileOutputStream fos = new FileOutputStream(outputPath)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = process.getInputStream().read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
}
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new RuntimeException("Database backup failed with exit code: " + exitCode);
}
} catch (Exception e) {
throw new IOException("Failed to backup database", e);
}
}
private String getDatabaseHost() {
// Extract host from datasource
return "localhost"; // Simplified
}
private String getDatabasePort() {
return "3306"; // Simplified
}
private String getDatabaseUser() {
return "root"; // Simplified
}
private String getDatabasePassword() {
return "password"; // Simplified
}
}
System Commands
System monitoring CLI
#cli command: "system-status" {
@print_system_status()
}Process management
#cli command: "process-list" {
@list_processes()
}System cleanup
#cli command: "cleanup" {
temp_files: @args.temp_files || true
logs: @args.logs || false
cache: @args.cache || false
@cleanup_system(temp_files, logs, cache)
}
Java System Commands
import org.springframework.stereotype.Component;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.OperatingSystemMXBean;@Component
public class SystemCommands {
private final SystemService systemService;
private final CleanupService cleanupService;
public SystemCommands(SystemService systemService, CleanupService cleanupService) {
this.systemService = systemService;
this.cleanupService = cleanupService;
}
@Command(name = "system-status", description = "Show system status")
public void systemStatus() {
try {
SystemStatus status = systemService.getSystemStatus();
System.out.println("=== System Status ===");
System.out.println("CPU Usage: " + String.format("%.1f%%", status.getCpuUsage()));
System.out.println("Memory Usage: " + String.format("%.1f%%", status.getMemoryUsage()));
System.out.println("Disk Usage: " + String.format("%.1f%%", status.getDiskUsage()));
System.out.println("Uptime: " + status.getUptime());
System.out.println("Active Connections: " + status.getActiveConnections());
} catch (Exception e) {
System.err.println("Error getting system status: " + e.getMessage());
System.exit(1);
}
}
@Command(name = "process-list", description = "List running processes")
public void processList() {
try {
List<ProcessInfo> processes = systemService.getRunningProcesses();
System.out.println("=== Running Processes ===");
System.out.printf("%-10s %-20s %-10s %-10s%n", "PID", "Name", "CPU%", "Memory%");
System.out.println("----------------------------------------");
for (ProcessInfo process : processes) {
System.out.printf("%-10s %-20s %-10.1f %-10.1f%n",
process.getPid(),
process.getName(),
process.getCpuUsage(),
process.getMemoryUsage());
}
} catch (Exception e) {
System.err.println("Error listing processes: " + e.getMessage());
System.exit(1);
}
}
@Command(name = "cleanup", description = "Clean up system resources")
public void cleanup(@Option(names = {"-t", "--temp-files"}, description = "Clean temp files") boolean tempFiles,
@Option(names = {"-l", "--logs"}, description = "Clean old logs") boolean logs,
@Option(names = {"-c", "--cache"}, description = "Clean cache") boolean cache) {
try {
CleanupResult result = cleanupService.cleanup(tempFiles, logs, cache);
System.out.println("=== Cleanup Results ===");
if (tempFiles) {
System.out.println("Temp files cleaned: " + result.getTempFilesCleaned() + " files");
}
if (logs) {
System.out.println("Log files cleaned: " + result.getLogFilesCleaned() + " files");
}
if (cache) {
System.out.println("Cache cleaned: " + result.getCacheCleaned() + " entries");
}
System.out.println("Total space freed: " + result.getSpaceFreed() + " bytes");
} catch (Exception e) {
System.err.println("Error during cleanup: " + e.getMessage());
System.exit(1);
}
}
}
@Service
public class SystemService {
private final OperatingSystemMXBean osBean;
private final MemoryMXBean memoryBean;
public SystemService() {
this.osBean = ManagementFactory.getOperatingSystemMXBean();
this.memoryBean = ManagementFactory.getMemoryMXBean();
}
public SystemStatus getSystemStatus() {
SystemStatus status = new SystemStatus();
// Get CPU usage
if (osBean instanceof com.sun.management.OperatingSystemMXBean) {
com.sun.management.OperatingSystemMXBean sunOsBean = (com.sun.management.OperatingSystemMXBean) osBean;
status.setCpuUsage(sunOsBean.getCpuLoad() * 100);
}
// Get memory usage
long totalMemory = memoryBean.getHeapMemoryUsage().getMax();
long usedMemory = memoryBean.getHeapMemoryUsage().getUsed();
status.setMemoryUsage((double) usedMemory / totalMemory * 100);
// Get disk usage
File root = new File("/");
long totalSpace = root.getTotalSpace();
long freeSpace = root.getFreeSpace();
status.setDiskUsage((double) (totalSpace - freeSpace) / totalSpace * 100);
// Get uptime
long uptime = ManagementFactory.getRuntimeMXBean().getUptime();
status.setUptime(formatUptime(uptime));
// Get active connections (simplified)
status.setActiveConnections(100); // Placeholder
return status;
}
public List<ProcessInfo> getRunningProcesses() {
List<ProcessInfo> processes = new ArrayList<>();
// Get current process info
ProcessInfo currentProcess = new ProcessInfo();
currentProcess.setPid(ProcessHandle.current().pid());
currentProcess.setName("java");
currentProcess.setCpuUsage(0.0); // Simplified
currentProcess.setMemoryUsage(0.0); // Simplified
processes.add(currentProcess);
return processes;
}
private String formatUptime(long uptime) {
long days = uptime / (24 60 60 * 1000);
long hours = (uptime % (24 60 60 1000)) / (60 60 * 1000);
long minutes = (uptime % (60 60 1000)) / (60 * 1000);
return String.format("%dd %dh %dm", days, hours, minutes);
}
}
@Service
public class CleanupService {
private final CacheManager cacheManager;
public CleanupService(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
public CleanupResult cleanup(boolean tempFiles, boolean logs, boolean cache) {
CleanupResult result = new CleanupResult();
if (tempFiles) {
result.setTempFilesCleaned(cleanupTempFiles());
}
if (logs) {
result.setLogFilesCleaned(cleanupLogFiles());
}
if (cache) {
result.setCacheCleaned(cleanupCache());
}
return result;
}
private int cleanupTempFiles() {
try {
Path tempDir = Paths.get(System.getProperty("java.io.tmpdir"));
int count = 0;
try (Stream<Path> paths = Files.walk(tempDir)) {
count = (int) paths.filter(Files::isRegularFile)
.filter(path -> path.toString().endsWith(".tmp"))
.peek(path -> {
try {
Files.delete(path);
} catch (IOException e) {
// Ignore deletion errors
}
})
.count();
}
return count;
} catch (Exception e) {
logger.warn("Error cleaning temp files", e);
return 0;
}
}
private int cleanupLogFiles() {
try {
Path logDir = Paths.get("/var/log/application");
int count = 0;
if (Files.exists(logDir)) {
try (Stream<Path> paths = Files.walk(logDir)) {
count = (int) paths.filter(Files::isRegularFile)
.filter(path -> path.toString().endsWith(".log"))
.filter(path -> {
try {
return Files.getLastModifiedTime(path)
.toInstant()
.isBefore(Instant.now().minus(Duration.ofDays(30)));
} catch (IOException e) {
return false;
}
})
.peek(path -> {
try {
Files.delete(path);
} catch (IOException e) {
// Ignore deletion errors
}
})
.count();
}
}
return count;
} catch (Exception e) {
logger.warn("Error cleaning log files", e);
return 0;
}
}
private int cleanupCache() {
try {
int count = 0;
for (String cacheName : cacheManager.getCacheNames()) {
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
cache.clear();
count++;
}
}
return count;
} catch (Exception e) {
logger.warn("Error cleaning cache", e);
return 0;
}
}
}
CLI Testing
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.TestPropertySource;
import org.springframework.beans.factory.annotation.Autowired;@SpringBootTest
@TestPropertySource(properties = {
"tusk.cli.default-command=help",
"tusk.cli.help-enabled=true"
})
public class CliTest {
@Autowired
private CliCommands cliCommands;
@MockBean
private FileProcessor fileProcessor;
@MockBean
private DatabaseService databaseService;
@Test
public void testHelloCommand() {
// Test hello command
String[] args = {"hello"};
cliCommands.run(args);
// Verify output (would need to capture System.out in real test)
}
@Test
public void testGreetCommand() {
// Test greet command with name
String[] args = {"greet", "--name", "John"};
cliCommands.run(args);
// Verify output
}
@Test
public void testProcessFileCommand() {
// Test file processing command
String[] args = {"process", "--input", "test.txt", "--output", "output.txt"};
cliCommands.run(args);
verify(fileProcessor).processFile("test.txt", "output.txt");
}
@Test
public void testDatabaseBackupCommand() {
// Test database backup command
String[] args = {"db-backup", "--database", "testdb", "--output", "backup.sql"};
cliCommands.run(args);
verify(databaseService).backupDatabase("testdb", "backup.sql");
}
}
Configuration Properties
application.yml
tusk:
cli:
default-command: "help"
version: "1.0.0"
help-enabled: true
commands:
hello:
description: "Print hello message"
version: "1.0.0"
help: true
process-file:
description: "Process a file"
options:
input:
required: true
type: "string"
description: "Input file path"
output:
type: "string"
default: "output.txt"
description: "Output file path"
format:
type: "string"
choices: ["json", "xml", "csv"]
description: "Output format"
db-backup:
description: "Backup database"
options:
database:
required: true
type: "string"
description: "Database name"
output:
type: "string"
description: "Backup file path"
system-status:
description: "Show system status"
help: true
aliases:
h: "hello"
p: "process-file"
b: "db-backup"
s: "system-status"spring:
application:
name: "tusk-cli"
Summary
The #cli
directive in TuskLang provides comprehensive command-line interface capabilities for Java applications. With Spring Boot integration, flexible command processing, and support for various CLI patterns, you can implement sophisticated command-line tools that enhance your application's functionality.
Key features include: - Multiple command types: File processing, database operations, system management - Spring Boot integration: Seamless integration with Spring Boot CLI - Flexible configuration: Configurable commands with options and subcommands - Argument processing: Support for required and optional arguments - Help system: Built-in help and version information - Error handling: Comprehensive error handling and exit codes - Testing support: Comprehensive testing utilities
The Java implementation provides enterprise-grade CLI capabilities that integrate seamlessly with Spring Boot applications while maintaining the simplicity and power of TuskLang's declarative syntax.