mirror of
https://github.com/chubin/wttr.in
synced 2025-01-12 03:58:45 +00:00
Add GeoIP info cache access
This commit is contained in:
parent
a6f2844c67
commit
074b8b6ec8
2 changed files with 170 additions and 0 deletions
99
internal/geo/ip/ip.go
Normal file
99
internal/geo/ip/ip.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
package ip
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/chubin/wttr.in/internal/config"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotFound = errors.New("cache entry not found")
|
||||
ErrInvalidCacheEntry = errors.New("invalid cache entry format")
|
||||
)
|
||||
|
||||
// Location information.
|
||||
type Location struct {
|
||||
CountryCode string
|
||||
Country string
|
||||
Region string
|
||||
City string
|
||||
Latitude float64
|
||||
Longitude float64
|
||||
}
|
||||
|
||||
// Cache provides access to the IP Geodata cache.
|
||||
type Cache struct {
|
||||
config *config.Config
|
||||
}
|
||||
|
||||
// NewCache returns new cache reader for the specified config.
|
||||
func NewCache(config *config.Config) *Cache {
|
||||
return &Cache{
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
// Read returns location information from the cache, if found,
|
||||
// or ErrNotFound if not found. If the entry is found, but its format
|
||||
// is invalid, ErrInvalidCacheEntry is returned.
|
||||
//
|
||||
// Format:
|
||||
//
|
||||
// [CountryCode];Country;Region;City;[Latitude];[Longitude]
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// DE;Germany;Free and Hanseatic City of Hamburg;Hamburg;53.5736;9.9782
|
||||
//
|
||||
func (c *Cache) Read(addr string) (*Location, error) {
|
||||
bytes, err := os.ReadFile(c.cacheFile(addr))
|
||||
if err != nil {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
return parseCacheEntry(string(bytes))
|
||||
}
|
||||
|
||||
// cacheFile retuns path to the cache entry for addr.
|
||||
func (c *Cache) cacheFile(addr string) string {
|
||||
return path.Join(c.config.Geo.IPCache, addr)
|
||||
}
|
||||
|
||||
// parseCacheEntry parses the location cache entry s,
|
||||
// and return location, or error, if the cache entry is invalid.
|
||||
func parseCacheEntry(s string) (*Location, error) {
|
||||
var (
|
||||
lat float64 = -1000
|
||||
long float64 = -1000
|
||||
err error
|
||||
)
|
||||
|
||||
parts := strings.Split(s, ";")
|
||||
if len(parts) < 4 {
|
||||
return nil, ErrInvalidCacheEntry
|
||||
}
|
||||
|
||||
if len(parts) >= 6 {
|
||||
lat, err = strconv.ParseFloat(parts[4], 64)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidCacheEntry
|
||||
}
|
||||
|
||||
long, err = strconv.ParseFloat(parts[5], 64)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidCacheEntry
|
||||
}
|
||||
}
|
||||
|
||||
return &Location{
|
||||
CountryCode: parts[0],
|
||||
Country: parts[1],
|
||||
Region: parts[2],
|
||||
City: parts[3],
|
||||
Latitude: lat,
|
||||
Longitude: long,
|
||||
}, nil
|
||||
}
|
71
internal/geo/ip/ip_test.go
Normal file
71
internal/geo/ip/ip_test.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
package ip
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParseCacheEntry(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected Location
|
||||
err error
|
||||
}{
|
||||
{
|
||||
"DE;Germany;Free and Hanseatic City of Hamburg;Hamburg;53.5736;9.9782",
|
||||
Location{
|
||||
CountryCode: "DE",
|
||||
Country: "Germany",
|
||||
Region: "Free and Hanseatic City of Hamburg",
|
||||
City: "Hamburg",
|
||||
Latitude: 53.5736,
|
||||
Longitude: 9.9782,
|
||||
},
|
||||
nil,
|
||||
},
|
||||
|
||||
{
|
||||
"ES;Spain;Madrid, Comunidad de;Madrid;40.4165;-3.70256;28223;Orange Espagne SA;orange.es",
|
||||
Location{
|
||||
CountryCode: "ES",
|
||||
Country: "Spain",
|
||||
Region: "Madrid, Comunidad de",
|
||||
City: "Madrid",
|
||||
Latitude: 40.4165,
|
||||
Longitude: -3.70256,
|
||||
},
|
||||
nil,
|
||||
},
|
||||
|
||||
{
|
||||
"US;United States of America;California;Mountain View",
|
||||
Location{
|
||||
CountryCode: "US",
|
||||
Country: "United States of America",
|
||||
Region: "California",
|
||||
City: "Mountain View",
|
||||
Latitude: -1000,
|
||||
Longitude: -1000,
|
||||
},
|
||||
nil,
|
||||
},
|
||||
|
||||
// Invalid entries
|
||||
{
|
||||
"DE;Germany;Free and Hanseatic City of Hamburg;Hamburg;53.5736;XXX",
|
||||
Location{},
|
||||
ErrInvalidCacheEntry,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
result, err := parseCacheEntry(tt.input)
|
||||
if tt.err == nil {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, *result, tt.expected)
|
||||
} else {
|
||||
require.ErrorIs(t, err, tt.err)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue