package handler import ( "encoding/json" "fmt" "net/http" "github.com/go-playground/validator/v10" "github.com/google/uuid" "github.com/iliadenisov/galaxy/internal/controller" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "github.com/iliadenisov/galaxy/internal/model/rest" ) func CommandHandler(c *gin.Context, executor CommandExecutor) { var cmd rest.Command if errorResponded(c, c.ShouldBindJSON(&cmd)) { return } commands := make([]Command, 0) for i := range cmd.Commands { command, err := parseCommand(cmd.Actor, cmd.Commands[i]) if errorResponded(c, err) { return } commands = append(commands, command) } if len(commands) == 0 { c.JSON(http.StatusBadRequest, gin.H{"error": "no commands given"}) return } if errorResponded(c, executor.Execute(commands...)) { return } c.Status(http.StatusNoContent) } func parseCommand(actor string, c json.RawMessage) (Command, error) { meta := new(rest.CommandMeta) if err := json.Unmarshal(c, meta); err != nil { return nil, err } switch t := meta.Type; t { case rest.CommandTypeRaceQuit: return commandRaceQuit(actor) case rest.CommandTypeRaceVote: return commandRaceVote(actor, c) case rest.CommandTypeRaceRelation: return commandRaceRelation(actor, c) case rest.CommandTypeShipClassCreate: return commandShipClassCreate(actor, c) case rest.CommandTypeShipClassMerge: return commandShipClassMerge(actor, c) case rest.CommandTypeShipClassRemove: return commandShipClassRemove(actor, c) case rest.CommandTypeShipGroupBreak: return commandShipGroupBreak(actor, c) case rest.CommandTypeShipGroupLoad: return commandShipGroupLoad(actor, c) case rest.CommandTypeShipGroupUnload: return commandShipGroupUnload(actor, c) case rest.CommandTypeShipGroupSend: return commandShipGroupSend(actor, c) case rest.CommandTypeShipGroupUpgrade: return commandShipGroupUpgrade(actor, c) case rest.CommandTypeShipGroupMerge: return commandShipGroupMerge(actor, c) case rest.CommandTypeShipGroupDismantle: return commandShipGroupDismantle(actor, c) case rest.CommandTypeShipGroupTransfer: return commandShipGroupTransfer(actor, c) case rest.CommandTypeShipGroupJoinFleet: return commandShipGroupJoinFleet(actor, c) case rest.CommandTypeFleetMerge: return commandFleetMerge(actor, c) case rest.CommandTypeFleetSend: return commandFleetSend(actor, c) case rest.CommandTypeScienceCreate: return commandScienceCreate(actor, c) case rest.CommandTypeScienceRemove: return commandScienceRemove(actor, c) case rest.CommandTypePlanetRename: return commandPlanetRename(actor, c) case rest.CommandTypePlanetProduce: return commandPlanetProduce(actor, c) case rest.CommandTypePlanetRouteSet: return commandPlanetRouteSet(actor, c) case rest.CommandTypePlanetRouteRemove: return commandPlanetRouteRemove(actor, c) default: return nil, fmt.Errorf("unknown comman type: %s", t) } } func commandRaceQuit(actor string) (Command, error) { return func(c controller.Ctrl) error { return c.RaceQuit(actor) }, nil } func commandRaceVote(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandRaceVote)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.RaceVote(actor, v.Acceptor) }, nil } } func commandRaceRelation(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandRaceRelation)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.RaceRelation(actor, v.Acceptor, v.Relation) }, nil } } func commandShipClassCreate(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandShipClassCreate)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.ShipClassCreate(actor, v.Name, v.Drive, int(v.Armament), v.Weapons, v.Shields, v.Cargo) }, nil } } func commandShipClassMerge(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandShipClassMerge)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.ShipClassMerge(actor, v.Name, v.Target) }, nil } } func commandShipClassRemove(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandShipClassRemove)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.ShipClassRemove(actor, v.Name) }, nil } } func commandShipGroupBreak(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandShipGroupBreak)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.ShipGroupBreak(actor, uuid.MustParse(v.ID), uuid.MustParse(v.NewID), uint(v.Quantity)) }, nil } } func commandShipGroupLoad(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandShipGroupLoad)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.ShipGroupLoad(actor, uuid.MustParse(v.ID), v.Cargo, v.Quantity) }, nil } } func commandShipGroupUnload(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandShipGroupUnload)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.ShipGroupUnload(actor, uuid.MustParse(v.ID), v.Quantity) }, nil } } func commandShipGroupSend(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandShipGroupSend)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.ShipGroupSend(actor, uuid.MustParse(v.ID), uint(v.Destination)) }, nil } } func commandShipGroupUpgrade(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandShipGroupUpgrade)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.ShipGroupUpgrade(actor, uuid.MustParse(v.ID), v.Tech, v.Level) }, nil } } func commandShipGroupMerge(actor string, c json.RawMessage) (Command, error) { return func(c controller.Ctrl) error { return c.ShipGroupMerge(actor) }, nil } func commandShipGroupDismantle(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandShipGroupDismantle)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.ShipGroupDismantle(actor, uuid.MustParse(v.ID)) }, nil } } func commandShipGroupTransfer(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandShipGroupTransfer)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.ShipGroupTransfer(actor, v.Acceptor, uuid.MustParse(v.ID)) }, nil } } func commandShipGroupJoinFleet(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandShipGroupJoinFleet)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.ShipGroupJoinFleet(actor, v.Name, uuid.MustParse(v.ID)) }, nil } } func commandFleetMerge(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandFleetMerge)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.FleetMerge(actor, v.Name, v.Target) }, nil } } func commandFleetSend(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandFleetSend)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.FleetSend(actor, v.Name, uint(v.Destination)) }, nil } } func commandScienceCreate(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandScienceCreate)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.ScienceCreate(actor, v.Name, v.Drive, v.Weapons, v.Shields, v.Cargo) }, nil } } func commandScienceRemove(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandScienceRemove)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.ScienceRemove(actor, v.Name) }, nil } } func commandPlanetRename(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandPlanetRename)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.PlanetRename(actor, v.Number, v.Name) }, nil } } func commandPlanetProduce(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandPlanetProduce)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.PlanetProduce(actor, v.Number, v.Production, v.Subject) }, nil } } func commandPlanetRouteSet(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandPlanetRouteSet)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.PlanetRouteSet(actor, v.LoadType, uint(v.Origin), uint(v.Destination)) }, nil } } func commandPlanetRouteRemove(actor string, c json.RawMessage) (Command, error) { if v, err := unmarshallCommand(c, new(rest.CommandPlanetRouteRemove)); err != nil { return nil, err } else { return func(c controller.Ctrl) error { return c.PlanetRouteRemove(actor, v.LoadType, uint(v.Origin)) }, nil } } // Helpers func unmarshallCommand[T rest.DecodableCommand](c json.RawMessage, v *T) (*T, error) { if err := json.Unmarshal(c, v); err != nil { return nil, err } if err := validateCommand(v); err != nil { return nil, err } return v, nil } func validateCommand(v any) error { if ve, ok := binding.Validator.Engine().(*validator.Validate); ok { if err := ve.Struct(v); err != nil { return err } } return nil }