feat: 实现从资源文件中加载 Snow 编程语言版本信息

This commit is contained in:
Luke 2025-06-18 10:05:24 +08:00
parent 34fd7c6d8b
commit dd2d14743e
6 changed files with 155 additions and 78 deletions

10
pom.xml
View File

@ -7,7 +7,7 @@
<groupId>org.jcnc.snow</groupId> <groupId>org.jcnc.snow</groupId>
<artifactId>Snow</artifactId> <artifactId>Snow</artifactId>
<version>1.0-SNAPSHOT</version> <version>0.4.0</version>
<properties> <properties>
<maven.compiler.source>24</maven.compiler.source> <maven.compiler.source>24</maven.compiler.source>
@ -17,6 +17,13 @@
<!-- 通用编译 & 打包插件 --> <!-- 通用编译 & 打包插件 -->
<build> <build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins> <plugins>
<!-- Java 编译插件 --> <!-- Java 编译插件 -->
<plugin> <plugin>
@ -37,6 +44,7 @@
<manifest> <manifest>
<mainClass>org.jcnc.snow.compiler.cli.SnowCLI</mainClass> <mainClass>org.jcnc.snow.compiler.cli.SnowCLI</mainClass>
<addClasspath>true</addClasspath> <addClasspath>true</addClasspath>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest> </manifest>
</archive> </archive>
</configuration> </configuration>

View File

@ -3,40 +3,31 @@ package org.jcnc.snow.compiler.cli;
import org.jcnc.snow.compiler.cli.commands.CompileCommand; import org.jcnc.snow.compiler.cli.commands.CompileCommand;
import org.jcnc.snow.compiler.cli.commands.RunCommand; import org.jcnc.snow.compiler.cli.commands.RunCommand;
import org.jcnc.snow.compiler.cli.commands.VersionCommand; import org.jcnc.snow.compiler.cli.commands.VersionCommand;
import org.jcnc.snow.compiler.cli.utils.CLIUtils;
import org.jcnc.snow.compiler.cli.utils.VersionUtils;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.function.Supplier; import java.util.function.Supplier;
/** /**
* SnowCLI 是项目的命令行入口类负责解析用户输入 * Snow 编程语言的命令行接口入口类
* 分发子命令并统一处理帮助版本和错误输出 * <p>
* 该类解析用户输入的命令行参数调度相应的子命令compilerunversion
* 并处理全局帮助和版本信息输出
* </p>
*/ */
public class SnowCLI { public class SnowCLI {
/** Snow 编程语言的版本号。 */
public static final String SNOW_VERSION = "1.0.0";
/** 全局帮助标志,当输入匹配时显示帮助信息。 */
private static final Set<String> GLOBAL_HELP_FLAGS = Set.of("help", "-h", "--help");
/** 全局版本标志,当输入匹配时显示版本信息。 */
private static final Set<String> GLOBAL_VERSION_FLAGS = Set.of("-v", "--version");
/** /**
* 全局选项定义列表 * Snow 编程语言的当前版本号从资源文件中加载
* 每个 Option 包含可用标志列表及对应的描述信息
*/ */
private static final List<Option> GLOBAL_OPTIONS = List.of( public static final String SNOW_VERSION = VersionUtils.loadVersion();
new Option(List.of("-h", "--help"), "Show this help message and exit"),
new Option(List.of("-v", "--version"), "Print snow programming language version and exit")
);
/** /**
* 所有可用子命令的映射 * 可用子命令名称与对应命令处理器的映射表
* 键为命令名称值为对应命令实例的提供者 * 键为子命令名称"compile", "run", "version"
* 通过 Map.of 初始化添加新命令时只需在此注册 * 值为返回相应 {@link CLICommand} 实例的 Supplier
*/ */
private static final Map<String, Supplier<CLICommand>> COMMANDS = Map.of( private static final Map<String, Supplier<CLICommand>> COMMANDS = Map.of(
"compile", CompileCommand::new, "compile", CompileCommand::new,
@ -45,92 +36,57 @@ public class SnowCLI {
); );
/** /**
* 程序入口 * 程序入口方法解析并调度子命令
* <p> * <p>
* 负责处理以下逻辑
* <ul>
* <li>全局帮助标志无参数或 help 标志时显示通用帮助并退出</li>
* <li>全局版本标志-v/--version 时打印版本并退出</li>
* <li>子命令调度根据第一个参数匹配子命令并分发执行</li>
* <li>子命令帮助子命令后带 --help 时显示该命令帮助</li>
* <li>错误处理捕获执行异常并打印错误信息</li>
* </ul>
* *
* @param args 用户在命令行中输入的参数数组 * @param args 命令行参数数组
* 无参数或首参数为帮助标志时打印全局用法说明并退出
* 首参数为版本标志时打印版本信息并退出
* 首参数为子命令名时进一步解析该子命令的参数并执行
*/ */
public static void main(String[] args) { public static void main(String[] args) {
// 全局帮助 // // 处理全局帮助
if (args.length == 0 || GLOBAL_HELP_FLAGS.contains(args[0])) { if (args.length == 0 || CLIUtils.GLOBAL_HELP_FLAGS.contains(args[0])) {
printGeneralUsage(); CLIUtils.printGeneralUsage(COMMANDS);
System.exit(0); System.exit(0);
} }
// 全局版本 // // 处理全局版本请求
if (GLOBAL_VERSION_FLAGS.contains(args[0])) { if (CLIUtils.GLOBAL_VERSION_FLAGS.contains(args[0])) {
new VersionCommand().execute(new String[0]); new VersionCommand().execute(new String[0]);
System.exit(0); System.exit(0);
} }
// 子命令调度 // // 子命令名称
String cmdName = args[0]; String cmdName = args[0];
Supplier<CLICommand> cmdSupplier = COMMANDS.get(cmdName); Supplier<CLICommand> cmdSupplier = COMMANDS.get(cmdName);
// 未知子命令处理
if (cmdSupplier == null) { if (cmdSupplier == null) {
System.err.println("Unknown command: " + cmdName); System.err.println("Unknown command: " + cmdName);
printGeneralUsage(); CLIUtils.printGeneralUsage(COMMANDS);
System.exit(1); System.exit(1);
} }
// 创建对应子命令实例
CLICommand cmd = cmdSupplier.get(); CLICommand cmd = cmdSupplier.get();
// 提取子命令参数
String[] subArgs = Arrays.copyOfRange(args, 1, args.length); String[] subArgs = Arrays.copyOfRange(args, 1, args.length);
// 子命令帮助 // // 如果子命令请求帮助则打印该命令的用法说明并退出
if (subArgs.length > 0 && GLOBAL_HELP_FLAGS.contains(subArgs[0])) { if (subArgs.length > 0 && CLIUtils.GLOBAL_HELP_FLAGS.contains(subArgs[0])) {
cmd.printUsage(); cmd.printUsage();
System.exit(0); System.exit(0);
} }
// 执行子命令 // // 执行子命令并根据返回的退出码退出
try { try {
int exitCode = cmd.execute(subArgs); int exitCode = cmd.execute(subArgs);
System.exit(exitCode); System.exit(exitCode);
} catch (Exception e) { } catch (Exception e) {
// 捕获命令执行过程中的异常并打印错误消息
System.err.println("Error: " + e.getMessage()); System.err.println("Error: " + e.getMessage());
System.exit(1); System.exit(1);
} }
} }
/**
* 打印动态生成的通用帮助信息
* <p>
* 列出全局选项和所有注册子命令的名称与描述
*/
private static void printGeneralUsage() {
System.out.println("Usage:");
System.out.println(" snow [OPTIONS] <command>");
System.out.println();
System.out.println("Options:");
// 动态遍历全局选项
for (Option opt : GLOBAL_OPTIONS) {
String flags = String.join(", ", opt.flags());
System.out.printf(" %-15s %s%n", flags, opt.description());
}
System.out.println();
System.out.println("Commands:");
// 遍历注册的子命令动态输出
COMMANDS.forEach((name, supplier) -> {
CLICommand c = supplier.get();
System.out.printf(" %-10s %s%n", name, c.description());
});
System.out.println();
System.out.println("Use \"snow <command> --help\" for command-specific options.");
}
/**
* 全局选项的数据结构包含标志列表和描述信息
*
* @param flags 选项的所有标志例如 -h, --help
* @param description 选项的功能说明
*/
private record Option(List<String> flags, String description) {
}
} }

View File

@ -49,7 +49,8 @@ public final class VersionCommand implements CLICommand {
*/ */
@Override @Override
public int execute(String[] args) { public int execute(String[] args) {
System.out.println("snow version " + SnowCLI.SNOW_VERSION); System.out.println("snow version " + "\"" + SnowCLI.SNOW_VERSION + "\"");
return 0; return 0;
} }
} }

View File

@ -0,0 +1,70 @@
package org.jcnc.snow.compiler.cli.utils;
import org.jcnc.snow.compiler.cli.CLICommand;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
/**
* 命令行界面通用工具类提供全局帮助和版本标志全局选项定义以及打印通用用法的方法
*/
public class CLIUtils {
/**
* 全局帮助标志集合支持 "help""-h""--help"
*/
public static final Set<String> GLOBAL_HELP_FLAGS = Set.of(
"help", "-h", "--help"
);
/**
* 全局版本标志集合支持 "-v""--version"
*/
public static final Set<String> GLOBAL_VERSION_FLAGS = Set.of(
"-v", "--version"
);
/**
* 全局选项列表包括帮助和版本选项的描述
*/
public static final List<Option> GLOBAL_OPTIONS = List.of(
new Option(List.of("-h", "--help"), "Show this help message and exit"),
new Option(List.of("-v", "--version"), "Print snow programming language version and exit")
);
/**
* 打印命令行工具的通用用法说明
* <p>
*
* @param commands 可用子命令名称到命令处理器的映射用于列出所有子命令及其描述
*/
public static void printGeneralUsage(Map<String, Supplier<CLICommand>> commands) {
System.out.println("Usage:");
System.out.println(" snow [OPTIONS] <command>");
System.out.println();
System.out.println("Options:");
for (Option opt : GLOBAL_OPTIONS) {
String flags = String.join(", ", opt.flags());
System.out.printf(" %-15s %s%n", flags, opt.description());
}
System.out.println();
System.out.println("Commands:");
commands.forEach((name, supplier) -> {
CLICommand c = supplier.get();
System.out.printf(" %-10s %s%n", name, c.description());
});
System.out.println();
System.out.println("Use \"snow <command> --help\" for command-specific options.");
}
/**
* 全局选项的数据结构包含选项标志和描述
*
* @param flags 选项标志列表例如 ["-h", "--help"]
* @param description 选项功能描述
*/
public record Option(List<String> flags, String description) {
}
}

View File

@ -0,0 +1,41 @@
package org.jcnc.snow.compiler.cli.utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.Properties;
/**
* 版本工具类用于加载资源文件中的 Snow 编程语言版本信息
* <p>
* classpath 路径下的 version.properties 文件中读取属性 "snow.version"
* 并返回对应的版本号字符串
* </p>
*/
public class VersionUtils {
/**
* 资源文件路径常量指向 classpath 根目录下的 version.properties 文件
*/
private static final String PROPERTIES_PATH = "/version.properties";
/**
* 加载并返回 Snow 编程语言的版本号
* <p>
* 通过 Class.getResourceAsStream 方法读取 version.properties 文件
* 使用 {@link Properties} 类解析并获取 key "snow.version" 的值
* </p>
*
* @return version.properties 中读取的版本号 "1.0.0"
* @throws UncheckedIOException 如果读取或解析配置文件时发生 I/O 错误
*/
public static String loadVersion() {
try (InputStream in = VersionUtils.class.getResourceAsStream(PROPERTIES_PATH)) {
Properties properties = new Properties();
properties.load(in);
return properties.getProperty("snow.version");
} catch (IOException e) {
throw new UncheckedIOException("Failed to load version from " + PROPERTIES_PATH, e);
}
}
}

View File

@ -0,0 +1 @@
snow.version=${project.version}