pokedex/main.go
Dan Anglin 4152a9d14f
feat: add initial project code
Add the initial code for the PokeDex project. For now the code can list
the next and previous 20 location areas within the Pokemon world.
2024-09-17 18:43:21 +01:00

174 lines
3 KiB
Go

package main
import (
"bufio"
"context"
"encoding/json"
"fmt"
"maps"
"net/http"
"os"
"slices"
"time"
)
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 func() error
}
func main() {
run()
}
func run() {
fmt.Print("pokedex > ")
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
command := scanner.Text()
cmdMap := getCommandMap()
if _, ok := cmdMap[command]; !ok {
fmt.Println("ERROR: Unrecognised command.")
fmt.Print("\npokedex > ")
continue
}
if err := cmdMap[command].callback(); err != nil {
fmt.Printf("ERROR: %v.\n", err)
}
fmt.Print("pokedex > ")
}
}
func getCommandMap() map[string]command {
return map[string]command{
"exit": {
name: "exit",
description: "Exit the Pokedex",
callback: commandExit,
},
"help": {
name: "help",
description: "Displays a help message",
callback: commandHelp,
},
"map": {
name: "map",
description: "Displays the next 20 locations in the Pokemon world",
callback: commandMap,
},
"mapb": {
name: "map back",
description: "Displays the previous 20 locations in the Pokemon world",
callback: commandMapB,
},
}
}
func commandHelp() error {
cmdMap := getCommandMap()
keys := []string{}
for key := range maps.All(cmdMap) {
keys = append(keys, key)
}
slices.Sort(keys)
fmt.Printf("\nWelcome to the Pokedex!\nUsage:\n")
for _, key := range slices.All(keys) {
fmt.Printf("\n%s: %s", key, cmdMap[key].description)
}
fmt.Println("\n")
return nil
}
func commandExit() error {
os.Exit(0)
return nil
}
func commandMap() error {
url := state.Next
if url == nil {
url = new(string)
*url = baseURL + locationAreaEndpoint
}
return printMap(*url)
}
func commandMapB() error {
url := state.Previous
if url == nil {
return fmt.Errorf("no previous locations available")
}
return printMap(*url)
}
func printMap(url string) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return fmt.Errorf("error creating the HTTP request: %w", err)
}
client := http.Client{}
resp, err := client.Do(request)
if err != nil {
return fmt.Errorf("error getting the response from the server: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
return fmt.Errorf(
"received a bad status from %s: (%d) %s",
url,
resp.StatusCode,
resp.Status,
)
}
var result NamedAPIResourceList
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return fmt.Errorf("unable to decode the JSON response: %w", err)
}
state.Next = result.Next
state.Previous = result.Previous
for _, location := range slices.All(result.Results) {
fmt.Println(location.Name)
}
return nil
}