refactoring

This commit is contained in:
bel
2026-03-08 22:16:53 -06:00
parent d37b6e292f
commit ca3c4d6607
6 changed files with 200 additions and 75 deletions

BIN
cmd/.db.go.swp Normal file

Binary file not shown.

69
cmd/db.go Normal file
View File

@@ -0,0 +1,69 @@
package cmd
import (
"context"
"database/sql"
"time"
_ "modernc.org/sqlite"
)
type DB struct {
*sql.DB
}
type Cache DB
var cacheAddr = "/tmp/turbomaps-er.db"
func NewCache(ctx context.Context) Cache {
ctx, can := context.WithTimeout(ctx, 5*time.Second)
defer can()
db, err := sql.Open("sqlite", cacheAddr)
if err != nil {
panic(err)
}
if err := db.PingContext(ctx); err != nil {
panic(err)
}
return Cache(DB{DB: db})
}
func (db Cache) Get(ctx context.Context, k string) ([]byte, error) {
if err := db.init(ctx); err != nil {
return nil, err
}
row := db.QueryRowContext(ctx, `
SELECT v FROM cache WHERE k=$1
`, k)
var v []byte
if err := row.Scan(&v); err != nil {
return nil, err
}
return v, row.Err()
}
func (db Cache) Set(ctx context.Context, k string, v []byte) error {
if err := db.init(ctx); err != nil {
return err
}
_, err := db.ExecContext(ctx, `
INSERT INTO cache (k, v) VALUES ($1, $2)
ON CONFLICT DO UPDATE SET v=$2 WHERE k=$1
`, k, v)
return err
}
func (db Cache) init(ctx context.Context) error {
_, err := db.ExecContext(ctx, `
CREATE TABLE IF NOT EXISTS cache(k TEXT PRIMARY KEY, v TEXT)
`)
return err
}

26
cmd/db_test.go Normal file
View File

@@ -0,0 +1,26 @@
package cmd
import (
"bytes"
"context"
"path"
"testing"
)
func TestCache(t *testing.T) {
ctx := context.Background()
cacheAddr = path.Join(t.TempDir(), "test.db")
c := NewCache(ctx)
k := "k"
v := []byte("v")
if err := c.Set(ctx, k, v); err != nil {
t.Fatal(err)
}
if got, err := c.Get(ctx, k); err != nil {
t.Fatal(err)
} else if !bytes.Equal(v, got) {
t.Fatalf("expected %q but got %q", v, got)
}
}

View File

@@ -1,75 +0,0 @@
package cmd
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"golang.org/x/time/rate"
"googlemaps.github.io/maps"
)
func Run(ctx context.Context) error {
rps := 2
limit := 200
limiter := rate.NewLimiter(rate.Limit(rps), 1)
c, err := maps.NewClient(maps.WithAPIKey(os.Getenv("GOOGLE_PLACES_API_KEY")))
if err != nil {
return err
}
resp, err := c.TextSearch(ctx, &maps.TextSearchRequest{
Query: os.Args[1],
Location: nil,
Radius: uint(0),
})
if err != nil {
return err
} else if len(resp.Results) < 1 {
return fmt.Errorf("no results for %q", os.Args[1])
}
origin := resp.Results[0].Geometry.Location
type Result struct {
Name string
Lat float64
Lng float64
Address string
}
results := []Result{}
var nextToken string
for len(results) < limit {
limiter.Wait(ctx)
resp, err := c.TextSearch(ctx, &maps.TextSearchRequest{
Query: os.Args[2],
Location: &origin,
Radius: uint(250),
PageToken: nextToken,
})
if err != nil {
return err
}
for _, result := range resp.Results {
results = append(results, Result{
Name: result.Name,
Lat: result.Geometry.Location.Lat,
Lng: result.Geometry.Location.Lng,
Address: result.FormattedAddress,
})
}
nextToken = resp.NextPageToken
if nextToken == "" {
break
} else {
log.Printf("%d...", len(resp.Results))
}
}
b, _ := json.Marshal(results)
fmt.Printf("%s\n", b)
return ctx.Err()
}

96
cmd/maps.go Normal file
View File

@@ -0,0 +1,96 @@
package cmd
import (
"context"
"fmt"
"os"
"golang.org/x/time/rate"
"googlemaps.github.io/maps"
)
type Maps struct {
around Location
}
func NewMapsOf(ctx context.Context, town string) (*Maps, error) {
m := &Maps{}
var err error
m.around, err = m.textSearchOne(ctx, town)
return m, err
}
var rps = 2
var limit = 200
var mapsLimiter = rate.NewLimiter(rate.Limit(rps), 1)
type Location struct {
Name string
Lat float64
Lng float64
Address string
}
func (m *Maps) textSearchOne(ctx context.Context, query string) (Location, error) {
results, err := m.textSearch(ctx, query)
if err != nil {
return Location{}, err
} else if len(results) < 1 {
return Location{}, fmt.Errorf("no results for %q", query)
}
return results[0], nil
}
func (m *Maps) textSearch(ctx context.Context, query string) ([]Location, error) {
locations := []Location{}
nextToken := ""
for {
results, err := m._textSearch(ctx, query, nextToken)
if err != nil {
return nil, err
}
for _, result := range results.Results {
locations = append(locations, Location{
Name: result.Name,
Lat: result.Geometry.Location.Lat,
Lng: result.Geometry.Location.Lng,
Address: result.FormattedAddress,
})
}
nextToken = results.NextPageToken
if nextToken == "" {
break
}
}
return locations, ctx.Err()
}
func (m *Maps) _textSearch(ctx context.Context, query string, nextToken string) (maps.PlacesSearchResponse, error) {
mapsLimiter.Wait(ctx)
var location *maps.LatLng
radius := uint(0)
if m.around == (Location{}) {
radius = 250
} else {
location = &maps.LatLng{
Lat: m.around.Lat,
Lng: m.around.Lng,
}
}
return m.client().TextSearch(ctx, &maps.TextSearchRequest{
Query: query,
Location: location,
Radius: radius,
PageToken: nextToken,
})
}
func (*Maps) client() *maps.Client {
c, err := maps.NewClient(maps.WithAPIKey(os.Getenv("GOOGLE_PLACES_API_KEY")))
if err != nil {
panic(err)
}
return c
}

9
cmd/run.go Normal file
View File

@@ -0,0 +1,9 @@
package cmd
import (
"context"
)
func Run(ctx context.Context) error {
return ctx.Err()
}