From 3a18041c6f7b790d3a0cb39684ccf04bcdc4ec61 Mon Sep 17 00:00:00 2001 From: ruinivist Date: Sun, 8 Feb 2026 20:40:18 +0000 Subject: [PATCH] feat: integrate restcountries api --- internal/geodata/fetcher.go | 57 +++++++++++++++++++++++++++++++++++++ internal/geodata/models.go | 19 +++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 internal/geodata/fetcher.go create mode 100644 internal/geodata/models.go diff --git a/internal/geodata/fetcher.go b/internal/geodata/fetcher.go new file mode 100644 index 0000000..5a426ee --- /dev/null +++ b/internal/geodata/fetcher.go @@ -0,0 +1,57 @@ +package geodata + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" + "time" +) + +func FetchLive() (TimezoneMap, error) { + client := http.Client{Timeout: 10 * time.Second} + + // from: https://restcountries.com + url := "https://restcountries.com/v3.1/all?fields=name,population,timezones" + + resp, err := client.Get(url) + if err != nil { + return nil, fmt.Errorf("failed to fetch country data") + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to fetch country data") + } + + var apidData []countryApiReponse + if err := json.NewDecoder(resp.Body).Decode(&apidData); err != nil { + return nil, fmt.Errorf("failed to fetch ") + } + + return buildTzMap(apidData), nil +} + +func buildTzMap(data []countryApiReponse) TimezoneMap { + tzMap := make(TimezoneMap) + + for _, countryRaw := range data { + country := Country{ + Name: countryRaw.Name.Common, + Population: countryRaw.Population, + } + + for _, rawTZ := range countryRaw.Timezones { + rawTZ = strings.TrimPrefix(rawTZ, "UTC") + rawTZ = strings.ReplaceAll(rawTZ, ":", "") + + if rawTZ == "" { + rawTZ = "+0000" // api quirk + } + + tzMap[rawTZ] = append(tzMap[rawTZ], country) + } + } + + return tzMap +} diff --git a/internal/geodata/models.go b/internal/geodata/models.go new file mode 100644 index 0000000..867b566 --- /dev/null +++ b/internal/geodata/models.go @@ -0,0 +1,19 @@ +package geodata + +// Country info exposed to other packages +type Country struct { + Name string + Population int64 +} + +// Timezone to Country lookups +type TimezoneMap map[string][]Country + +// internal model for https://restcountries.com/v3.1/all?fields=name,population,timezones +type countryApiReponse struct { + Name struct { + Common string `json:"common"` // it has other fields but I don't want them + } `json:"name"` + Population int64 `json:"population"` + Timezones []string `json:"timezones"` // example: [ "UTC+00:00" ] +}