package main import ( "bufio" "fmt" "maps" "os" "slices" "time" "codeflow.dananglin.me.uk/apollo/pokedex/internal/pokeclient" ) const ( baseURL string = "https://pokeapi.co/api/v2" locationAreaEndpoint string = "/location-area" ) type State struct { Previous *string Next *string } var state State type command struct { name string description string callback callbackFunc } type callbackFunc func() error func main() { run() } func run() { client := pokeclient.NewClient( 5*time.Minute, 10*time.Second, ) commandMap := map[string]command{ "exit": { name: "exit", description: "Exit the Pokedex", callback: commandExit, }, "help": { name: "help", description: "Displays a help message", callback: nil, }, "map": { name: "map", description: "Displays the next 20 locations in the Pokemon world", callback: commandMap(client), }, "mapb": { name: "map back", description: "Displays the previous 20 locations in the Pokemon world", callback: commandMapB(client), }, } summaries := summaryMap(commandMap) commandMap["help"] = command{ name: "help", description: "Displays a help message", callback: commandHelp(summaries), } fmt.Printf("\nWelcome to the Pokedex!\n") fmt.Print("\npokedex > ") scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { command := scanner.Text() cmd, ok := commandMap[command] if !ok { fmt.Println("ERROR: Unrecognised command.") fmt.Print("\npokedex > ") continue } if cmd.callback == nil { fmt.Println("ERROR: This command is defined but does not have a callback function.") fmt.Print("\npokedex > ") continue } if err := commandMap[command].callback(); err != nil { fmt.Printf("ERROR: %v.\n", err) } fmt.Print("pokedex > ") } } func commandHelp(summaries map[string]string) callbackFunc { return func() error { keys := []string{} for key := range maps.All(summaries) { keys = append(keys, key) } slices.Sort(keys) fmt.Printf("\nCommands:\n") for _, key := range slices.All(keys) { fmt.Printf("\n%s: %s", key, summaries[key]) } fmt.Printf("\n\n") return nil } } func commandExit() error { os.Exit(0) return nil } func commandMap(client *pokeclient.Client) callbackFunc { return func() error { url := state.Next if url == nil { url = new(string) *url = baseURL + locationAreaEndpoint } return printResourceList(client, *url) } } func commandMapB(client *pokeclient.Client) callbackFunc { return func() error { url := state.Previous if url == nil { return fmt.Errorf("no previous locations available") } return printResourceList(client, *url) } } func printResourceList(client *pokeclient.Client, url string) error { list, err := client.GetNamedAPIResourceList(url) if err != nil { return fmt.Errorf("unable to get the list of resources: %w", err) } state.Next = list.Next state.Previous = list.Previous for _, location := range slices.All(list.Results) { fmt.Println(location.Name) } return nil } func summaryMap(commandMap map[string]command) map[string]string { summaries := make(map[string]string) for key, value := range maps.All(commandMap) { summaries[key] = value.description } return summaries }