diff --git a/src/main/java/com/serliunx/ddns/ManagerLite.java b/src/main/java/com/serliunx/ddns/ManagerLite.java index 856621e..4cb437c 100644 --- a/src/main/java/com/serliunx/ddns/ManagerLite.java +++ b/src/main/java/com/serliunx/ddns/ManagerLite.java @@ -16,6 +16,7 @@ import com.serliunx.ddns.support.command.target.HelpCommand; import com.serliunx.ddns.support.command.target.ReloadCommand; import com.serliunx.ddns.support.command.target.StopCommand; import com.serliunx.ddns.support.command.target.config.ConfigCommand; +import com.serliunx.ddns.support.command.target.instance.InstanceCommand; import com.serliunx.ddns.support.log.JLineAdaptAppender; import org.jline.reader.LineReader; import org.jline.reader.LineReaderBuilder; @@ -143,6 +144,8 @@ public final class ManagerLite { commandDispatcher.register(new ConfigCommand(configuration)); // stop commandDispatcher.register(new StopCommand()); + // instance + commandDispatcher.register(new InstanceCommand(systemInitializer)); } /** diff --git a/src/main/java/com/serliunx/ddns/core/context/AbstractInstanceContext.java b/src/main/java/com/serliunx/ddns/core/context/AbstractInstanceContext.java index 7516865..a287473 100644 --- a/src/main/java/com/serliunx/ddns/core/context/AbstractInstanceContext.java +++ b/src/main/java/com/serliunx/ddns/core/context/AbstractInstanceContext.java @@ -109,7 +109,7 @@ public abstract class AbstractInstanceContext implements InstanceContext, Multip @Override public Set getInstances() { - return instanceMap == null ? Collections.emptySet() : new HashSet<>(instanceMap.values()); + return instanceMap == null ? Collections.emptySet() : new LinkedHashSet<>(instanceMap.values()); } @Override diff --git a/src/main/java/com/serliunx/ddns/support/ConsoleStyleHelper.java b/src/main/java/com/serliunx/ddns/support/ConsoleStyleHelper.java new file mode 100644 index 0000000..316b71d --- /dev/null +++ b/src/main/java/com/serliunx/ddns/support/ConsoleStyleHelper.java @@ -0,0 +1,66 @@ +package com.serliunx.ddns.support; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * 控制台样式助手 + * + * @author SerLiunx + * @version 1.0.4 + * @since 2025/2/4 + */ +public final class ConsoleStyleHelper { + + /** + * ANSI 控制台经典样式编码映射表 + */ + private static final Map CLASSIC_STYLE_MAP = new HashMap<>(); + + static { + // 格式 + CLASSIC_STYLE_MAP.put("&r", "\033[0m"); // 重置 + CLASSIC_STYLE_MAP.put("&l", "\033[1m"); // 加粗或高亮 + + // 颜色 + CLASSIC_STYLE_MAP.put("&0", "\033[30m"); // 黑色 + CLASSIC_STYLE_MAP.put("&1", "\033[31m"); // 红色 + CLASSIC_STYLE_MAP.put("&2", "\033[32m"); // 绿色 + CLASSIC_STYLE_MAP.put("&3", "\033[33m"); // 黄色 + CLASSIC_STYLE_MAP.put("&4", "\033[34m"); // 蓝色 + CLASSIC_STYLE_MAP.put("&5", "\033[35m"); // 品红 + CLASSIC_STYLE_MAP.put("&6", "\033[36m"); // 青色 + CLASSIC_STYLE_MAP.put("&7", "\033[37m"); // 白色 + } + + /** + * 格式化输出, 支持颜色代码 + * + * @param format 格式 + * @param args 参数 + */ + public static void coloredPrintf(String format, final Object... args) { + if (!format.endsWith("%n")) { + format = format + "%n"; + } + if (!format.endsWith("&r")) { + format = format + "&r"; + } + System.out.printf(replaceStyleCode(format), args); + } + + /** + * 替换样式代码 + * + * @param original 原始文本 + * @return 替换后的文本 + */ + private static String replaceStyleCode(String original) { + Set> entries = CLASSIC_STYLE_MAP.entrySet(); + for (Map.Entry entry : entries) { + original = original.replace(entry.getKey(), entry.getValue()); + } + return original; + } +} diff --git a/src/main/java/com/serliunx/ddns/support/command/AbstractCommand.java b/src/main/java/com/serliunx/ddns/support/command/AbstractCommand.java index dd5fd78..65d38d1 100644 --- a/src/main/java/com/serliunx/ddns/support/command/AbstractCommand.java +++ b/src/main/java/com/serliunx/ddns/support/command/AbstractCommand.java @@ -10,6 +10,8 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; +import static com.serliunx.ddns.support.ConsoleStyleHelper.coloredPrintf; + /** * 指令的抽象实现 *
  • 实现公共逻辑及定义具体逻辑 @@ -68,8 +70,10 @@ public abstract class AbstractCommand implements Command { @Override public boolean onCommand(String[] args) { if (!hasArgs(args) || - args.length < 2) { - log.warn("用法 => {}", getUsage()); + args.length < 1) { + System.out.println(); + coloredPrintf("&2用法 =>&r &6%s", getUsage()); + System.out.println(); return true; } diff --git a/src/main/java/com/serliunx/ddns/support/command/CommandDispatcher.java b/src/main/java/com/serliunx/ddns/support/command/CommandDispatcher.java index 76cb8d4..145d98d 100644 --- a/src/main/java/com/serliunx/ddns/support/command/CommandDispatcher.java +++ b/src/main/java/com/serliunx/ddns/support/command/CommandDispatcher.java @@ -7,6 +7,8 @@ import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; +import static com.serliunx.ddns.support.ConsoleStyleHelper.coloredPrintf; + /** * 指令调度器 * @@ -70,11 +72,13 @@ public final class CommandDispatcher { Command command = commands.get(cmd); if (command == null) { - logger.warn("未知指令: {}, 请输入 help 查看帮助!", cmd); + System.out.println(); + coloredPrintf("&1未知指令&r: &2%s&r, &1请输入 &3help&r &1查看帮助!", cmd); + System.out.println(); return; } if (!command.onCommand(splitArgs(args))) { - logger.error("指令执行出现了错误: {}", Arrays.toString(args)); + coloredPrintf("&1指令执行出现了错误:&r &5%s", Arrays.toString(args)); } } diff --git a/src/main/java/com/serliunx/ddns/support/command/target/HelpCommand.java b/src/main/java/com/serliunx/ddns/support/command/target/HelpCommand.java index 7ab5342..1287085 100644 --- a/src/main/java/com/serliunx/ddns/support/command/target/HelpCommand.java +++ b/src/main/java/com/serliunx/ddns/support/command/target/HelpCommand.java @@ -11,6 +11,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import static com.serliunx.ddns.support.ConsoleStyleHelper.coloredPrintf; + /** * 指令: help * @@ -31,29 +33,32 @@ public class HelpCommand extends AbstractCommand { if (hasArgs(args)) { final String cmd = args[0]; final Command command = commands.get(cmd); + System.out.println(); if (command == null) { - System.out.printf("无法找到指令 %s 的相关信息, 请使用 help 查看可用的指令及帮助!%n", cmd); + coloredPrintf("&1无法找到指令 %s 的相关信息, 请使用 help 查看可用的指令及帮助!%n", cmd); } else { List subCommands = command.getSubCommands(); if (subCommands == null || subCommands.isEmpty()) { - System.out.printf("指令:%s - %s - %s%n", cmd, command.getDescription(), command.getUsage()); + coloredPrintf("&2%s&r - &6%s&r - &5%s%n", cmd, command.getDescription(), command.getUsage()); } else { subCommands.forEach(c -> { - System.out.printf("%s - %s - %s%n", c.getName(), c.getDescription(), c.getUsage()); + coloredPrintf("&2%s&r - &6%s&r - &5%s%n", c.getName(), c.getDescription(), c.getUsage()); }); } } + System.out.println(); } else { + System.out.println(); commands.forEach((k, v) -> { // 忽略 help 自身 if (k.equals(getName())) { return; } - System.out.printf("%s - %s - %s%n", k, v.getDescription(), v.getUsage()); + coloredPrintf("&2%s&r\t - &6%s&r - &5%s%n", k, v.getDescription(), v.getUsage()); }); System.out.println(); - System.out.println("使用 help <指令> 来查看更详细的帮助信息."); + coloredPrintf("&6&l使用 help <指令> 来查看更详细的帮助信息."); } return true; } diff --git a/src/main/java/com/serliunx/ddns/support/command/target/config/ConfigGetCommand.java b/src/main/java/com/serliunx/ddns/support/command/target/config/ConfigGetCommand.java index 175919e..02c58f1 100644 --- a/src/main/java/com/serliunx/ddns/support/command/target/config/ConfigGetCommand.java +++ b/src/main/java/com/serliunx/ddns/support/command/target/config/ConfigGetCommand.java @@ -8,6 +8,8 @@ import org.jline.reader.ParsedLine; import java.util.List; +import static com.serliunx.ddns.support.ConsoleStyleHelper.coloredPrintf; + /** * 指令: config get * @@ -31,7 +33,9 @@ public final class ConfigGetCommand extends AbstractCommand { public boolean onCommand(String[] args) { if (!hasArgs(args) || args.length < 1) { - log.warn("用法 => {}", getUsage()); + System.out.println(); + coloredPrintf("&2用法 =>&r &6%s", getUsage()); + System.out.println(); return true; } System.out.println(configuration.getString(args[0])); diff --git a/src/main/java/com/serliunx/ddns/support/command/target/config/ConfigSetCommand.java b/src/main/java/com/serliunx/ddns/support/command/target/config/ConfigSetCommand.java index 4e40ba9..670cc28 100644 --- a/src/main/java/com/serliunx/ddns/support/command/target/config/ConfigSetCommand.java +++ b/src/main/java/com/serliunx/ddns/support/command/target/config/ConfigSetCommand.java @@ -8,6 +8,8 @@ import org.jline.reader.ParsedLine; import java.util.List; +import static com.serliunx.ddns.support.ConsoleStyleHelper.coloredPrintf; + /** * 指令: config set * @@ -31,7 +33,9 @@ public final class ConfigSetCommand extends AbstractCommand { public boolean onCommand(String[] args) { if (!hasArgs(args) || args.length < 2) { - log.warn("用法 => {}", getUsage()); + System.out.println(); + coloredPrintf("&2用法 =>&r &6%s", getUsage()); + System.out.println(); return true; } final String target = args[0]; diff --git a/src/main/java/com/serliunx/ddns/support/command/target/instance/InstanceCommand.java b/src/main/java/com/serliunx/ddns/support/command/target/instance/InstanceCommand.java new file mode 100644 index 0000000..5dd9e80 --- /dev/null +++ b/src/main/java/com/serliunx/ddns/support/command/target/instance/InstanceCommand.java @@ -0,0 +1,22 @@ +package com.serliunx.ddns.support.command.target.instance; + +import com.serliunx.ddns.support.SystemInitializer; +import com.serliunx.ddns.support.command.AbstractCommand; + +import java.util.ArrayList; + +/** + * 指令: instance + * + * @author SerLiunx + * @version 1.0.4 + * @since 2025/2/4 + */ +public final class InstanceCommand extends AbstractCommand { + + public InstanceCommand(SystemInitializer systemInitializer) { + super("instance", new ArrayList<>(), "实例相关指令", "instance list/add/..."); + // 子命令: list + addSubCommand(new InstanceListCommand(systemInitializer)); + } +} diff --git a/src/main/java/com/serliunx/ddns/support/command/target/instance/InstanceListCommand.java b/src/main/java/com/serliunx/ddns/support/command/target/instance/InstanceListCommand.java new file mode 100644 index 0000000..7186a72 --- /dev/null +++ b/src/main/java/com/serliunx/ddns/support/command/target/instance/InstanceListCommand.java @@ -0,0 +1,55 @@ +package com.serliunx.ddns.support.command.target.instance; + +import com.serliunx.ddns.core.instance.Instance; +import com.serliunx.ddns.support.Assert; +import com.serliunx.ddns.support.ConsoleStyleHelper; +import com.serliunx.ddns.support.SystemInitializer; +import com.serliunx.ddns.support.command.AbstractCommand; +import org.jline.reader.Candidate; +import org.jline.reader.LineReader; +import org.jline.reader.ParsedLine; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * 指令: instance list + * + * @author SerLiunx + * @version 1.0.4 + * @since 2025/2/4 + */ +public final class InstanceListCommand extends AbstractCommand { + + private final SystemInitializer systemInitializer; + + public InstanceListCommand(SystemInitializer systemInitializer) { + super("list", new ArrayList<>(), "列出所有实例", "instance list"); + Assert.notNull(systemInitializer); + this.systemInitializer = systemInitializer; + } + + @Override + public boolean onCommand(String[] args) { + Set instances = systemInitializer.getInstances(); + System.out.println(); + + instances.forEach(i -> { + ConsoleStyleHelper.coloredPrintf("&2%s&r(&3%s&r)", i.getName(), i.getType()); + }); + + System.out.println(); + return true; + } + + @Override + public List getArgs() { + return null; + } + + @Override + public void onComplete(LineReader reader, ParsedLine line, int index, List candidates) { + //do nothing for list + } +}