package main import ( "errors" "fmt" "io" "strings" ) const ( kcpInteractiveCommandHelp = "help" kcpInteractiveCommandQuit = "quit" kcpInteractiveCommandText = "text" kcpInteractiveCommandFile = "file" ) var errKCPEmptyInteractiveCommand = errors.New("interactive command is empty") type kcpInteractiveCommand struct { name string to string value string } func parseKCPInteractiveCommand(line string) (kcpInteractiveCommand, error) { commandName, rest, ok := cutKCPInteractiveField(strings.TrimSpace(line)) if !ok { return kcpInteractiveCommand{}, errKCPEmptyInteractiveCommand } switch strings.ToLower(commandName) { case "help", "h", "?": return kcpInteractiveCommand{name: kcpInteractiveCommandHelp}, nil case "quit", "exit": return kcpInteractiveCommand{name: kcpInteractiveCommandQuit}, nil case kcpInteractiveCommandText: to, body, err := parseKCPInteractiveTargetValue(rest, kcpInteractiveCommandText) if err != nil { return kcpInteractiveCommand{}, err } return kcpInteractiveCommand{name: kcpInteractiveCommandText, to: to, value: body}, nil case kcpInteractiveCommandFile: to, path, err := parseKCPInteractiveTargetValue(rest, kcpInteractiveCommandFile) if err != nil { return kcpInteractiveCommand{}, err } return kcpInteractiveCommand{name: kcpInteractiveCommandFile, to: to, value: path}, nil default: return kcpInteractiveCommand{}, fmt.Errorf("unknown command %q; type help for usage", commandName) } } func parseKCPInteractiveTargetValue(rest, commandName string) (string, string, error) { to, value, ok := cutKCPInteractiveField(strings.TrimSpace(rest)) if !ok { return "", "", fmt.Errorf("%s command requires a target peer and payload", commandName) } if strings.TrimSpace(value) == "" { return "", "", fmt.Errorf("%s command requires a non-empty payload", commandName) } return to, strings.TrimSpace(value), nil } func cutKCPInteractiveField(input string) (string, string, bool) { trimmed := strings.TrimSpace(input) if trimmed == "" { return "", "", false } for i, r := range trimmed { if r == ' ' || r == '\t' { return trimmed[:i], strings.TrimSpace(trimmed[i+1:]), true } } return trimmed, "", true } func printKCPInteractiveHelp(w io.Writer) { _, _ = fmt.Fprintln(w, "interactive mode commands (KCP):") _, _ = fmt.Fprintln(w, " help show this help") _, _ = fmt.Fprintln(w, " text send one text message over the existing KCP session") _, _ = fmt.Fprintln(w, " file send one file over the existing KCP session") _, _ = fmt.Fprintln(w, " quit exit this peer process") }