Created
November 21, 2024 06:03
-
-
Save googlefan256/837b6de6da4a97381f4e95e8df6d1dc0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package commandparser | |
import ( | |
"encoding/json" | |
"errors" | |
"fmt" | |
"regexp" | |
"strings" | |
) | |
// CommandType represents the type of command | |
type CommandType string | |
// SubcommandType represents the type of subcommand | |
type SubcommandType string | |
// ArgumentType represents the type of arguments | |
type ArgumentType string | |
const ( | |
// コマンドタイプの例 | |
CommandTypeUser CommandType = "user" | |
CommandTypeRole CommandType = "role" | |
CommandTypeServer CommandType = "server" | |
// サブコマンドの例 | |
SubcommandTypeAdd SubcommandType = "add" | |
SubcommandTypeRemove SubcommandType = "remove" | |
SubcommandTypeList SubcommandType = "list" | |
SubcommandTypeInfo SubcommandType = "info" | |
// 引数タイプの例 | |
ArgumentTypeTextChannel ArgumentType = "text-channels" | |
ArgumentTypeUser ArgumentType = "users" | |
ArgumentTypeRole ArgumentType = "roles" | |
ArgumentTypeString ArgumentType = "string" | |
ArgumentTypeNumber ArgumentType = "number" | |
) | |
// Argument represents a single command argument | |
type Argument struct { | |
Name string `json:"name"` | |
Type ArgumentType `json:"type"` | |
Value string `json:"value"` | |
} | |
// ParsedCommand represents a parsed command with potential subcommand | |
type ParsedCommand struct { | |
Name CommandType `json:"name"` | |
Subcommand SubcommandType `json:"subcommand,omitempty"` | |
Arguments []Argument `json:"arguments"` | |
} | |
// ArgumentExtractor defines how to extract a specific argument | |
type ArgumentExtractor struct { | |
Name string | |
Type ArgumentType | |
ExtractRegex *regexp.Regexp | |
Optional bool | |
Transformer func(string) string // 追加:値の変換関数 | |
} | |
// SubcommandParseRule defines parsing rules for a specific subcommand | |
type SubcommandParseRule struct { | |
Validator func(string) bool | |
Extractors []ArgumentExtractor | |
} | |
// CommandParseRule defines how to parse a specific type of command | |
type CommandParseRule struct { | |
Prefix string | |
Validator func(string) bool | |
Subcommands map[SubcommandType]SubcommandParseRule | |
} | |
// CommandParser manages parsing rules for different commands | |
type CommandParser struct { | |
rules map[CommandType]CommandParseRule | |
} | |
// NewCommandParser creates a new CommandParser with default rules | |
func NewCommandParser() *CommandParser { | |
parser := &CommandParser{ | |
rules: make(map[CommandType]CommandParseRule), | |
} | |
// ユーザー関連のコマンドルール | |
parser.AddRule(CommandTypeUser, CommandParseRule{ | |
Prefix: "!user", | |
Validator: func(input string) bool { | |
return strings.HasPrefix(strings.TrimSpace(input), "!user") | |
}, | |
Subcommands: map[SubcommandType]SubcommandParseRule{ | |
SubcommandTypeAdd: { | |
Validator: func(input string) bool { | |
return strings.Contains(input, "add") | |
}, | |
Extractors: []ArgumentExtractor{ | |
{ | |
Name: "username", | |
Type: ArgumentTypeUser, | |
ExtractRegex: regexp.MustCompile(`@(\w+)`), | |
Optional: false, | |
}, | |
{ | |
Name: "role", | |
Type: ArgumentTypeRole, | |
ExtractRegex: regexp.MustCompile(`(\w+)$`), | |
Optional: true, | |
Transformer: strings.ToLower, | |
}, | |
}, | |
}, | |
SubcommandTypeRemove: { | |
Validator: func(input string) bool { | |
return strings.Contains(input, "remove") | |
}, | |
Extractors: []ArgumentExtractor{ | |
{ | |
Name: "username", | |
Type: ArgumentTypeUser, | |
ExtractRegex: regexp.MustCompile(`@(\w+)`), | |
Optional: false, | |
}, | |
}, | |
}, | |
SubcommandTypeList: { | |
Validator: func(input string) bool { | |
return strings.Contains(input, "list") | |
}, | |
Extractors: []ArgumentExtractor{ | |
{ | |
Name: "filter", | |
Type: ArgumentTypeString, | |
ExtractRegex: regexp.MustCompile(`(\w+)$`), | |
Optional: true, | |
}, | |
}, | |
}, | |
}, | |
}) | |
// サーバー関連のコマンドルール | |
parser.AddRule(CommandTypeServer, CommandParseRule{ | |
Prefix: "!server", | |
Validator: func(input string) bool { | |
return strings.HasPrefix(strings.TrimSpace(input), "!server") | |
}, | |
Subcommands: map[SubcommandType]SubcommandParseRule{ | |
SubcommandTypeInfo: { | |
Validator: func(input string) bool { | |
return strings.Contains(input, "info") | |
}, | |
Extractors: []ArgumentExtractor{ | |
{ | |
Name: "detail", | |
Type: ArgumentTypeString, | |
ExtractRegex: regexp.MustCompile(`(\w+)$`), | |
Optional: true, | |
}, | |
}, | |
}, | |
}, | |
}) | |
return parser | |
} | |
// AddRule adds a new parsing rule for a command type | |
func (p *CommandParser) AddRule(cmdType CommandType, rule CommandParseRule) { | |
p.rules[cmdType] = rule | |
} | |
// Parse attempts to parse an input string into a structured command | |
func (p *CommandParser) Parse(input string) (*ParsedCommand, error) { | |
input = strings.TrimSpace(input) | |
// 登録されているルールを順番にチェック | |
for cmdType, rule := range p.rules { | |
if !rule.Validator(input) { | |
continue | |
} | |
// サブコマンドを検索 | |
var matchedSubcommand SubcommandType | |
var matchedSubRule SubcommandParseRule | |
for subCmd, subRule := range rule.Subcommands { | |
if subRule.Validator(input) { | |
matchedSubcommand = subCmd | |
matchedSubRule = subRule | |
break | |
} | |
} | |
if matchedSubcommand == "" { | |
return nil, fmt.Errorf("no matching subcommand found for command %s", cmdType) | |
} | |
// 引数を抽出 | |
arguments := make([]Argument, 0) | |
// サブコマンドの引数を抽出 | |
for _, extractor := range matchedSubRule.Extractors { | |
matches := extractor.ExtractRegex.FindStringSubmatch(input) | |
if len(matches) < 2 { | |
if !extractor.Optional { | |
return nil, fmt.Errorf("required argument '%s' not found for subcommand %s", extractor.Name, matchedSubcommand) | |
} | |
continue | |
} | |
// 値の変換(オプション) | |
value := matches[1] | |
if extractor.Transformer != nil { | |
value = extractor.Transformer(value) | |
} | |
arguments = append(arguments, Argument{ | |
Name: extractor.Name, | |
Type: extractor.Type, | |
Value: value, | |
}) | |
} | |
return &ParsedCommand{ | |
Name: cmdType, | |
Subcommand: matchedSubcommand, | |
Arguments: arguments, | |
}, nil | |
} | |
return nil, errors.New("no matching command found") | |
} | |
// ToJSON converts the parsed command to a JSON string | |
func (pc *ParsedCommand) ToJSON() (string, error) { | |
jsonBytes, err := json.Marshal(pc) | |
if err != nil { | |
return "", err | |
} | |
return string(jsonBytes), nil | |
} | |
// Example usage function to demonstrate how to use the parser | |
func ExampleUsage() { | |
// Create a new command parser | |
parser := NewCommandParser() | |
// Parse different types of commands with subcommands | |
commands := []string{ | |
"!user add @johndoe admin", | |
"!user remove @janedoe", | |
"!user list active", | |
"!server info members", | |
} | |
for _, cmd := range commands { | |
parsedCmd, err := parser.Parse(cmd) | |
if err != nil { | |
fmt.Printf("Error parsing '%s': %v\n", cmd, err) | |
continue | |
} | |
jsonStr, _ := parsedCmd.ToJSON() | |
fmt.Printf("Parsed command: %s\n", jsonStr) | |
} | |
} | |
// AddCustomRule allows adding a completely new command type with its rules | |
func (p *CommandParser) AddCustomRule(cmdType CommandType, rule CommandParseRule) { | |
p.rules[cmdType] = rule | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment