package main import ( "encoding/json" "fmt" "os" "strings" "gopkg.in/yaml.v3" ) type Command struct { Name string Description string Action CommandAction Arguments []string IsVarargs bool } type CommandAction func([]string, []string) (string, error) var commands = []Command{ { Name: "install", Description: "Install one or more mod archives into modman", Arguments: []string{"game name", "archive path"}, IsVarargs: true, Action: func(requiredArguments []string, providedArguments []string) (string, error) { if len(providedArguments) < len(requiredArguments) { return "", fmt.Errorf("expected %s folowed by at least one %ss", requiredArguments[0], requiredArguments[1]) } var game, gameOpenError = OpenGame(providedArguments[0]) if gameOpenError != nil { return "", gameOpenError } defer func() { if closeError := game.Close(); closeError != nil { panic(closeError) } }() for _, archivePath := range providedArguments[1:] { if installError := game.InstallMod(archivePath); installError != nil { return "", installError } } return "mods installed", nil }, }, { Name: "register", Description: "Registers the data directory path of game for management under the given name", Arguments: []string{"data path", "game name"}, IsVarargs: false, Action: func(requiredArguments []string, providedArguments []string) (string, error) { if len(providedArguments) < len(requiredArguments) { return "", fmt.Errorf("expected %s folowed by at least one %ss", requiredArguments[0], requiredArguments[1]) } var dataPath = providedArguments[0] var gameName = providedArguments[1] if registerGameError := RegisterGame(gameName, dataPath); registerGameError != nil { return "", registerGameError } return fmt.Sprintf("Registered %s at %s", gameName, dataPath), nil }, }, { Name: "remove", Description: "Remove one or more mods from modman", Arguments: []string{"game name", "mod name"}, IsVarargs: true, Action: func(requiredArguments []string, providedArguments []string) (string, error) { if len(providedArguments) < len(requiredArguments) { return "", fmt.Errorf("expected %s followed by one or more %ss", requiredArguments[0], requiredArguments[1]) } var game, gameOpenError = OpenGame(providedArguments[0]) if gameOpenError != nil { return "", gameOpenError } defer func() { if closeError := game.Close(); closeError != nil { panic(closeError) } }() if removeModsError := game.RemoveMods(providedArguments[1:]); removeModsError != nil { return "", removeModsError } return "removed mods", nil }, }, { Name: "rename", Description: "Rename a mod within modman", Arguments: []string{"game name", "mod name", "new name"}, IsVarargs: false, Action: func(requiredArguments []string, providedArguments []string) (string, error) { if len(providedArguments) != len(requiredArguments) { return "", fmt.Errorf("expected %s followed by %s and %s", requiredArguments[0], requiredArguments[1], requiredArguments[2]) } var game, gameOpenError = OpenGame(providedArguments[0]) if gameOpenError != nil { return "", gameOpenError } defer func() { if closeError := game.Close(); closeError != nil { panic(closeError) } }() if renameError := game.RenameMod( providedArguments[1], providedArguments[2]); renameError != nil { return "", renameError } return "renamed", nil }, }, { Name: "manifest", Description: "Retrieve a manifest of all installed mods", Arguments: []string{"game name", "format"}, IsVarargs: false, Action: func(requiredArguments []string, providedArguments []string) (string, error) { if len(providedArguments) != len(requiredArguments) { return "", fmt.Errorf("expected %s followed by %s", requiredArguments[0], requiredArguments[1]) } var game, gameOpenError = OpenGame(providedArguments[0]) if gameOpenError != nil { return "", gameOpenError } defer func() { if closeError := game.Close(); closeError != nil { panic(closeError) } }() var format = providedArguments[1] var formatter, formatterExists = formatters[format] if !(formatterExists) { return "", fmt.Errorf("unsupported format: `%s`", format) } var manifestBuilder = strings.Builder{} if formatError := formatter(&manifestBuilder, game.Mods); formatError != nil { return "", formatError } return manifestBuilder.String(), nil }, }, { Name: "deploy", Description: "Deploy all specified mods in order of listing", Arguments: []string{"game name", "mod name"}, IsVarargs: true, Action: func(requiredArguments []string, providedArguments []string) (string, error) { if len(providedArguments) != len(requiredArguments) { return "", fmt.Errorf("expected %s followed by one or more %ss", requiredArguments[0], requiredArguments[1]) } var game, gameOpenError = OpenGame(providedArguments[0]) if gameOpenError != nil { return "", gameOpenError } defer func() { if closeError := game.Close(); closeError != nil { panic(closeError) } }() if cleanError := game.CleanDeployedMods(); cleanError != nil { return "", cleanError } for _, modName := range providedArguments[1:] { if deployError := game.DeployMod(modName); deployError != nil { return "", deployError } } return "deployed", nil }, }, { Name: "clean", Description: "Clean all deployed mods", Arguments: []string{"game name"}, IsVarargs: false, Action: func(requiredArguments []string, arguments []string) (string, error) { if len(arguments) != len(requiredArguments) { return "", fmt.Errorf("expected %s", requiredArguments[0]) } var game, gameOpenError = OpenGame(arguments[0]) if gameOpenError != nil { return "", gameOpenError } defer func() { if closeError := game.Close(); closeError != nil { panic(closeError) } }() if cleanDeployedModsError := game.CleanDeployedMods(); cleanDeployedModsError != nil { return "", cleanDeployedModsError } return "cleaned", nil }, }, } var formatters = map[string]func(*strings.Builder, any) error{ "json": func(builder *strings.Builder, data any) error { var encoder = json.NewEncoder(builder) if encodeError := encoder.Encode(data); encodeError != nil { return encodeError } return nil }, "yaml": func(builder *strings.Builder, data any) error { var encoder = yaml.NewEncoder(builder) encoder.SetIndent(2) if encodeError := encoder.Encode(data); encodeError != nil { return encodeError } return nil }, } func main() { var argCount = len(os.Args) if argCount == 1 { fmt.Println("Modman v0.1") fmt.Println("Enter one of the following commands following modman:") for i := range commands { var command = commands[i] fmt.Print("\t", command.Name) for j := range command.Arguments { fmt.Print(" [", command.Arguments[j], "]") } if command.IsVarargs { fmt.Println("...") } else { fmt.Println() } } return } var commandName = os.Args[1] for i := range commands { var command = commands[i] if command.Name == commandName { var response, actionError = command.Action(command.Arguments, os.Args[2:]) if actionError != nil { fmt.Fprintln(os.Stderr, actionError.Error()) os.Exit(1) } if len(response) != 0 { fmt.Println(response) } return } } fmt.Fprintf(os.Stderr, "unknown command: `%s`\n", commandName) }