Added pokemon encounter struct

This commit is contained in:
Enrique Ordaz 2025-06-05 16:09:14 -06:00
parent ae7b6d0f83
commit e51545a0c3
7 changed files with 166 additions and 15 deletions

View File

@ -6,9 +6,9 @@ import (
) )
func commandMap(cfg *config) error { func commandMap(cfg *config) error {
if cfg.nextLocationsURL == nil { // if cfg.nextLocationsURL == nil {
return errors.New("you're on the last page") // return errors.New("you're on the last page")
} // }
locationsResp, err := cfg.pokeClient.ListLocations(cfg.nextLocationsURL) locationsResp, err := cfg.pokeClient.ListLocations(cfg.nextLocationsURL)
if err != nil { if err != nil {

View File

@ -3,16 +3,20 @@ package pokeapi
import ( import (
"net/http" "net/http"
"time" "time"
"git.eo13-dev.com/enordaz/gokedex/internal/pokecache"
) )
type Client struct { type Client struct {
httpClient http.Client httpClient http.Client
cache pokecache.Cache
} }
func NewClient(timeout time.Duration) Client { func NewClient(timeout, cacheInterval time.Duration) Client {
return Client{ return Client{
httpClient: http.Client{ httpClient: http.Client{
Timeout: timeout, Timeout: timeout,
}, },
cache: pokecache.NewCache(cacheInterval),
} }
} }

View File

@ -1,7 +1,9 @@
package pokeapi package pokeapi
import ( import (
"bytes"
"encoding/json" "encoding/json"
"io"
"net/http" "net/http"
) )
@ -11,20 +13,30 @@ func (c *Client) ListLocations(pageUrl *string) (RespShallowLocations, error) {
url = *pageUrl url = *pageUrl
} }
req, err := http.NewRequest("GET", url, nil) data, ok := c.cache.Get(url)
if err != nil { if !ok {
return RespShallowLocations{}, err
}
resp, err := c.httpClient.Do(req) req, err := http.NewRequest("GET", url, nil)
if err != nil { if err != nil {
return RespShallowLocations{}, err return RespShallowLocations{}, err
}
resp, err := c.httpClient.Do(req)
if err != nil {
return RespShallowLocations{}, err
}
defer resp.Body.Close()
data, err = io.ReadAll(resp.Body)
if err != nil {
return RespShallowLocations{}, err
}
c.cache.Add(url, data)
} }
defer resp.Body.Close()
var locationsResp RespShallowLocations var locationsResp RespShallowLocations
decoder := json.NewDecoder(resp.Body) decoder := json.NewDecoder(bytes.NewReader(data))
if err = decoder.Decode(&locationsResp); err != nil { if err := decoder.Decode(&locationsResp); err != nil {
return RespShallowLocations{}, err return RespShallowLocations{}, err
} }

View File

@ -0,0 +1,12 @@
package pokeapi
type RespShallowLocationExplore struct {
Id int `json:"id"`
Name string `json:"name"`
Encounters []struct {
Pokemon struct {
Name string `json:"name"`
URL string `json:"url"`
} `json:"pokemon"`
}
}

View File

@ -0,0 +1,63 @@
package pokecache
import (
"sync"
"time"
)
type Cache struct {
entries map[string]cacheEntry
mux *sync.RWMutex
}
type cacheEntry struct {
createdAt time.Time
val []byte
}
func NewCache(interval time.Duration) Cache {
cache := Cache{
entries: make(map[string]cacheEntry),
mux: &sync.RWMutex{},
}
go cache.reapLoop(interval)
return cache
}
func (c *Cache) Add(key string, value []byte) {
c.mux.Lock()
defer c.mux.Unlock()
c.entries[key] = cacheEntry{
createdAt: time.Now().UTC(),
val: value,
}
}
func (c *Cache) Get(key string) ([]byte, bool) {
c.mux.RLock()
defer c.mux.RUnlock()
val, ok := c.entries[key]
return val.val, ok
}
func (c *Cache) reapLoop(interval time.Duration) {
ticker := time.NewTicker(interval)
for range ticker.C {
c.reap(time.Now().UTC(), interval)
}
}
func (c *Cache) reap(now time.Time, last time.Duration) {
c.mux.Lock()
defer c.mux.Unlock()
for k, v := range c.entries {
if v.createdAt.Before(now.Add(-last)) {
delete(c.entries, k)
}
}
}

View File

@ -0,0 +1,60 @@
package pokecache
import (
"fmt"
"testing"
"time"
)
func TestAddGet(t *testing.T) {
const interval = 5 * time.Second
cases := []struct {
key string
val []byte
}{
{
key: "https://example.com",
val: []byte("testdata"),
},
{
key: "https://example.com/path",
val: []byte("moretestdata"),
},
}
for i, c := range cases {
t.Run(fmt.Sprintf("Test case %v", i), func(t *testing.T) {
cache := NewCache(interval)
cache.Add(c.key, c.val)
val, ok := cache.Get(c.key)
if !ok {
t.Errorf("expected to find key")
return
}
if string(val) != string(c.val) {
t.Errorf("expected to find value")
}
})
}
}
func TestReapLoop(t *testing.T) {
const baseTime = 5 * time.Millisecond
const waitTime = baseTime + 5*time.Millisecond
cache := NewCache(baseTime)
cache.Add("https://example.com", []byte("testdata"))
_, ok := cache.Get("https://example.com")
if !ok {
t.Errorf("expected to find key")
return
}
time.Sleep(waitTime)
_, ok = cache.Get("https://example.com")
if ok {
t.Errorf("expected to not fing key")
return
}
}

View File

@ -9,7 +9,7 @@ import (
const prompt = "Pokedex > " const prompt = "Pokedex > "
func main() { func main() {
pokeClient := pokeapi.NewClient(5 * time.Second) pokeClient := pokeapi.NewClient(5*time.Second, 5*time.Minute)
cfg := &config{ cfg := &config{
pokeClient: pokeClient, pokeClient: pokeClient,
} }