generated from templates/go-generic
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.
This commit is contained in:
parent
c29412e612
commit
4152a9d14f
9 changed files with 242 additions and 24 deletions
|
@ -22,5 +22,5 @@ jobs:
|
||||||
with:
|
with:
|
||||||
target: test
|
target: test
|
||||||
env:
|
env:
|
||||||
PROJECT_TEST_VERBOSE: "1"
|
POKEDEX_TEST_VERBOSE: "1"
|
||||||
PROJECT_TEST_COVER: "1"
|
POKEDEX_TEST_COVER: "1"
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,2 +1 @@
|
||||||
/__build/*
|
pokedex
|
||||||
!__build/.gitkeep
|
|
||||||
|
|
3
go.mod
Normal file
3
go.mod
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module codeflow.dananglin.me.uk/apollo/pokedex
|
||||||
|
|
||||||
|
go 1.23.0
|
5
magefiles/go.mod
Normal file
5
magefiles/go.mod
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module codeflow.dananglin.me.uk/apollo/pokedex/magefiles
|
||||||
|
|
||||||
|
go 1.23.0
|
||||||
|
|
||||||
|
require github.com/magefile/mage v1.15.0
|
2
magefiles/go.sum
Normal file
2
magefiles/go.sum
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
|
||||||
|
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
|
@ -14,23 +14,23 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
app = "binary"
|
app = "pokedex"
|
||||||
defaultInstallPrefix = "/usr/local"
|
defaultInstallPrefix = "/usr/local"
|
||||||
envInstallPrefix = "PROJECT_INSTALL_PREFIX"
|
envInstallPrefix = "POKEDEX_INSTALL_PREFIX"
|
||||||
envTestVerbose = "PROJECT_TEST_VERBOSE"
|
envTestVerbose = "POKEDEX_TEST_VERBOSE"
|
||||||
envTestCover = "PROJECT_TEST_COVER"
|
envTestCover = "POKEDEX_TEST_COVER"
|
||||||
envBuildRebuildAll = "PROJECT_BUILD_REBUILD_ALL"
|
envBuildRebuildAll = "POKEDEX_BUILD_REBUILD_ALL"
|
||||||
envBuildVerbose = "PROJECT_BUILD_VERBOSE"
|
envBuildVerbose = "POKEDEX_BUILD_VERBOSE"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Default = Build
|
Default = Build
|
||||||
binary = "./__build/" + app
|
binary = app
|
||||||
)
|
)
|
||||||
|
|
||||||
// Test run the go tests.
|
// Test run the go tests.
|
||||||
// To enable verbose mode set PROJECT_TEST_VERBOSE=1.
|
// To enable verbose mode set POKEDEX_TEST_VERBOSE=1.
|
||||||
// To enable coverage mode set PROJECT_TEST_COVER=1.
|
// To enable coverage mode set POKEDEX_TEST_COVER=1.
|
||||||
func Test() error {
|
func Test() error {
|
||||||
goTest := sh.RunCmd("go", "test")
|
goTest := sh.RunCmd("go", "test")
|
||||||
|
|
||||||
|
@ -53,8 +53,8 @@ func Lint() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build build the executable.
|
// Build build the executable.
|
||||||
// To rebuild packages that are already up-to-date set PROJECT_BUILD_REBUILD_ALL=1
|
// To rebuild packages that are already up-to-date set POKEDEX_BUILD_REBUILD_ALL=1
|
||||||
// To enable verbose mode set PROJECT_BUILD_VERBOSE=1
|
// To enable verbose mode set POKEDEX_BUILD_VERBOSE=1
|
||||||
func Build() error {
|
func Build() error {
|
||||||
main := "main.go"
|
main := "main.go"
|
||||||
flags := ldflags()
|
flags := ldflags()
|
||||||
|
|
168
main.go
168
main.go
|
@ -1,24 +1,174 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"maps"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
const (
|
||||||
binaryVersion string
|
baseURL string = "https://pokeapi.co/api/v2"
|
||||||
buildTime string
|
locationAreaEndpoint string = "/location-area"
|
||||||
goVersion string
|
|
||||||
gitCommit string
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type State struct {
|
||||||
|
Previous *string
|
||||||
|
Next *string
|
||||||
|
}
|
||||||
|
|
||||||
|
var state State
|
||||||
|
|
||||||
|
type command struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
callback func() error
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if err := run(); err != nil {
|
run()
|
||||||
fmt.Printf("ERROR: %v.\n", err)
|
}
|
||||||
os.Exit(1)
|
|
||||||
|
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 run() error {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
59
types.go
Normal file
59
types.go
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
// LocationArea is a section of areas such as floors in a building or a cave.
|
||||||
|
type LocationArea struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
GameIndex int `json:"game_index"`
|
||||||
|
EncounterMethodRates []EncounterMethodRate `json:"encounter_method_rates"`
|
||||||
|
Location NamedAPIResource `json:"location"`
|
||||||
|
Names []Name `json:"names"`
|
||||||
|
PokemonEncounters []PokemonEncounter `json:"pokemon_encounters"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type EncounterMethodRate struct {
|
||||||
|
EncounterMethod NamedAPIResource `json:"encounter_method"`
|
||||||
|
VersionDetails EncounterVersionDetails `json:"version_details"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type EncounterVersionDetails struct {
|
||||||
|
Rate int `json:"rate"`
|
||||||
|
Version NamedAPIResource `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Name struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Language NamedAPIResource `json:"language"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PokemonEncounter is details of a possible Pokemon encounter.
|
||||||
|
type PokemonEncounter struct {
|
||||||
|
Pokemon NamedAPIResource `json:"pokemon"`
|
||||||
|
VersionDetails []VersionEncounterDetails `json:"version_details"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type VersionEncounterDetails struct {
|
||||||
|
Version NamedAPIResource `json:"version"`
|
||||||
|
MaxChance int `json:"max_chance"`
|
||||||
|
EncounterDetails []Encounter `json:"encounter_details"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Encounter struct {
|
||||||
|
MinLevel int `json:"min_level"`
|
||||||
|
MaxLevel int `json:"max_level"`
|
||||||
|
ConditionValues []NamedAPIResource `json:"condition_values"`
|
||||||
|
Chance int `json:"chance"`
|
||||||
|
Method NamedAPIResource `json:"method"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NamedAPIResourceList struct {
|
||||||
|
Count int `json:"count"`
|
||||||
|
Next *string `json:"next"`
|
||||||
|
Previous *string `json:"previous"`
|
||||||
|
Results []NamedAPIResource `json:"results"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NamedAPIResource struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
Loading…
Reference in a new issue