package main import ( "errors" "fmt" "strings" ) var errUDPEmptyCommand = errors.New("interactive command is empty") type udpInteractiveCommand struct { name string to string value string } func parseUDPInteractiveCommand(line string) (udpInteractiveCommand, error) { commandName, rest, ok := cutUDPField(strings.TrimSpace(line)) if !ok { return udpInteractiveCommand{}, errUDPEmptyCommand } switch strings.ToLower(commandName) { case "help", "h", "?": return udpInteractiveCommand{name: "help"}, nil case "quit", "exit": return udpInteractiveCommand{name: "quit"}, nil case "text": to, body, err := parseUDPTargetValue(rest, "text") if err != nil { return udpInteractiveCommand{}, err } return udpInteractiveCommand{name: "text", to: to, value: body}, nil case "file": to, path, err := parseUDPTargetValue(rest, "file") if err != nil { return udpInteractiveCommand{}, err } return udpInteractiveCommand{name: "file", to: to, value: path}, nil default: return udpInteractiveCommand{}, fmt.Errorf("unknown command %q; type help for usage", commandName) } } func parseUDPTargetValue(rest, commandName string) (string, string, error) { to, value, ok := cutUDPField(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 cutUDPField(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 }