diff --git a/CHANGELOG.md b/CHANGELOG.md index 06ae85e..20f029f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Changelog -## 0.3.1 - Unreleased +## 0.4.0 - 2026-05-04 + +- Add `business_status` to search, nearby, details, JSON output, and human CLI output. (#8) - thanks @doomsday-rgb +- Add drive-only `--avoid-tolls`, `--avoid-highways`, and `--avoid-ferries` direction flags backed by Routes API `routeModifiers`. (#7) - thanks @gabob23 ## 0.3.0 - 2026-02-14 diff --git a/README.md b/README.md index e576699..2a972a5 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Modern Go client + CLI for the Google Places API (New). Fast for humans, tidy fo - Nearby search around a location restriction. - Place photos in details + photo media URLs. - Route search along a driving path (Routes API). -- Directions between two points with distance, duration, and steps (Routes API). +- Directions between two points with distance, duration, steps, and optional drive route modifiers (Routes API). - Location bias (lat/lng/radius) and pagination tokens. - Place details: hours, phone, website, rating, price, types. - Optional reviews in details (`--reviews` / `IncludeReviews`). @@ -20,7 +20,7 @@ Modern Go client + CLI for the Google Places API (New). Fast for humans, tidy fo ## Install / Run -Latest release: v0.3.0 (2026-02-14). +Latest release: v0.4.0 (2026-05-04). - Homebrew: `brew install steipete/tap/goplaces` - Go: `go install github.com/steipete/goplaces/cmd/goplaces@latest` @@ -129,6 +129,13 @@ goplaces directions --from "Pike Place Market" --to "Space Needle" goplaces directions --from-place-id --to-place-id --compare drive --steps ``` +Driving route modifiers: + +```bash +goplaces directions --from "Paris" --to "Brest" --mode drive --avoid-tolls +goplaces directions --from "Paris" --to "Brest" --mode drive --avoid-highways --avoid-ferries +``` + Units (default metric): ```bash @@ -230,6 +237,8 @@ route, err := client.Route(ctx, goplaces.RouteRequest{ - Reviews are returned only when `IncludeReviews`/`--reviews` is set. - Photos are returned only when `IncludePhotos`/`--photos` is set. - Route search requires the Google Routes API to be enabled. +- `business_status` is returned for search, nearby, and details when Google includes it. +- Direction route modifiers (`--avoid-tolls`, `--avoid-highways`, `--avoid-ferries`) require `--mode drive`. - Field masks are defined alongside each request (e.g. `search.go`, `details.go`, `autocomplete.go`). - The Places API is billed and quota-limited; keep an eye on your Cloud Console quotas. @@ -252,3 +261,4 @@ Optional env overrides: - Override the search text used in E2E: `GOOGLE_PLACES_E2E_QUERY` - Override language code for E2E: `GOOGLE_PLACES_E2E_LANGUAGE` - Override region code for E2E: `GOOGLE_PLACES_E2E_REGION` +- Override directions endpoints/locations: `GOOGLE_DIRECTIONS_E2E_BASE_URL`, `GOOGLE_PLACES_E2E_DIRECTIONS_FROM`, `GOOGLE_PLACES_E2E_DIRECTIONS_TO` diff --git a/client_test.go b/client_test.go index 9f751c2..7b8a6b5 100644 --- a/client_test.go +++ b/client_test.go @@ -47,7 +47,8 @@ func TestSearchSuccess(t *testing.T) { "userRatingCount": 532, "priceLevel": "PRICE_LEVEL_MODERATE", "types": ["cafe"], - "currentOpeningHours": {"openNow": true} + "currentOpeningHours": {"openNow": true}, + "businessStatus": "OPERATIONAL" } ], "nextPageToken": "next" @@ -102,6 +103,9 @@ func TestSearchSuccess(t *testing.T) { if result.OpenNow == nil || *result.OpenNow != true { t.Fatalf("unexpected openNow: %#v", result.OpenNow) } + if result.BusinessStatus != "OPERATIONAL" { + t.Fatalf("unexpected business status: %s", result.BusinessStatus) + } if response.NextPageToken != "next" { t.Fatalf("unexpected token: %s", response.NextPageToken) } @@ -396,6 +400,7 @@ func TestDetailsSuccess(t *testing.T) { "types": ["park"], "regularOpeningHours": {"weekdayDescriptions": ["Mon: 9-5"]}, "currentOpeningHours": {"openNow": false}, + "businessStatus": "CLOSED_TEMPORARILY", "nationalPhoneNumber": "+1 555", "websiteUri": "https://example.com" }`)) @@ -420,6 +425,9 @@ func TestDetailsSuccess(t *testing.T) { if place.OpenNow == nil || *place.OpenNow != false { t.Fatalf("unexpected openNow") } + if place.BusinessStatus != "CLOSED_TEMPORARILY" { + t.Fatalf("unexpected business status: %s", place.BusinessStatus) + } if len(place.Hours) != 1 { t.Fatalf("unexpected hours") } diff --git a/details.go b/details.go index bb64514..d21b3fc 100644 --- a/details.go +++ b/details.go @@ -9,7 +9,7 @@ import ( ) const ( - detailsFieldMaskBase = "id,displayName,formattedAddress,location,rating,userRatingCount,priceLevel,types,regularOpeningHours,currentOpeningHours,nationalPhoneNumber,websiteUri" + detailsFieldMaskBase = "id,displayName,formattedAddress,location,rating,userRatingCount,priceLevel,types,regularOpeningHours,currentOpeningHours,businessStatus,nationalPhoneNumber,websiteUri" detailsFieldMaskReview = "reviews" detailsFieldMaskPhotos = "photos" ) @@ -73,6 +73,7 @@ func mapPlaceDetails(place placeItem) PlaceDetails { Website: place.WebsiteURI, Hours: weekdayDescriptions(place.RegularOpeningHours), OpenNow: openNow(place.CurrentOpeningHours), + BusinessStatus: strings.TrimSpace(place.BusinessStatus), Reviews: mapReviews(place.Reviews), Photos: mapPhotos(place.Photos), } diff --git a/directions.go b/directions.go index dbab113..4c99ce1 100644 --- a/directions.go +++ b/directions.go @@ -40,16 +40,19 @@ var directionsUnits = map[string]struct{}{ // DirectionsRequest describes a directions query between two locations. type DirectionsRequest struct { - From string `json:"from,omitempty"` - To string `json:"to,omitempty"` - FromPlaceID string `json:"from_place_id,omitempty"` - ToPlaceID string `json:"to_place_id,omitempty"` - FromLocation *LatLng `json:"from_location,omitempty"` - ToLocation *LatLng `json:"to_location,omitempty"` - Mode string `json:"mode,omitempty"` - Language string `json:"language,omitempty"` - Region string `json:"region,omitempty"` - Units string `json:"units,omitempty"` + From string `json:"from,omitempty"` + To string `json:"to,omitempty"` + FromPlaceID string `json:"from_place_id,omitempty"` + ToPlaceID string `json:"to_place_id,omitempty"` + FromLocation *LatLng `json:"from_location,omitempty"` + ToLocation *LatLng `json:"to_location,omitempty"` + Mode string `json:"mode,omitempty"` + Language string `json:"language,omitempty"` + Region string `json:"region,omitempty"` + Units string `json:"units,omitempty"` + AvoidTolls bool `json:"avoid_tolls,omitempty"` + AvoidHighways bool `json:"avoid_highways,omitempty"` + AvoidFerries bool `json:"avoid_ferries,omitempty"` } // DirectionsResponse contains a single route summary and steps. @@ -164,6 +167,9 @@ func validateDirectionsRequest(req DirectionsRequest) error { return ValidationError{Field: "units", Message: "must be metric or imperial"} } } + if (req.AvoidTolls || req.AvoidHighways || req.AvoidFerries) && req.Mode != directionsModeDrive { + return ValidationError{Field: "route_modifiers", Message: "avoid tolls/highways/ferries require drive mode"} + } return nil } @@ -290,6 +296,13 @@ func buildDirectionsBody(req DirectionsRequest) map[string]any { if strings.TrimSpace(req.Region) != "" { body["regionCode"] = strings.TrimSpace(req.Region) } + if req.AvoidTolls || req.AvoidHighways || req.AvoidFerries { + body["routeModifiers"] = map[string]any{ + "avoidTolls": req.AvoidTolls, + "avoidHighways": req.AvoidHighways, + "avoidFerries": req.AvoidFerries, + } + } return body } diff --git a/directions_test.go b/directions_test.go index 5325878..9941c56 100644 --- a/directions_test.go +++ b/directions_test.go @@ -124,6 +124,14 @@ func TestDirectionsUnitsValidation(t *testing.T) { } } +func TestDirectionsRouteModifiersRequireDrive(t *testing.T) { + req := DirectionsRequest{From: "A", To: "B", Mode: "walk", AvoidTolls: true} + err := validateDirectionsRequest(applyDirectionsDefaults(req)) + if err == nil || !strings.Contains(err.Error(), "route_modifiers") { + t.Fatalf("expected route modifier validation error, got %v", err) + } +} + func TestDirectionsLocationValidation(t *testing.T) { req := DirectionsRequest{FromPlaceID: "a", From: "b", To: "c"} if err := validateDirectionsRequest(applyDirectionsDefaults(req)); err == nil { @@ -283,6 +291,49 @@ func TestDirectionsRequestLocaleAndImperialUnits(t *testing.T) { } } +func TestDirectionsRequestRouteModifiers(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != routesPath { + t.Fatalf("unexpected path: %s", r.URL.Path) + } + var payload map[string]any + if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { + t.Fatalf("decode body: %v", err) + } + modifiers, ok := payload["routeModifiers"].(map[string]any) + if !ok { + t.Fatalf("missing routeModifiers: %#v", payload) + } + if modifiers["avoidTolls"] != true || modifiers["avoidHighways"] != true || modifiers["avoidFerries"] != true { + t.Fatalf("unexpected routeModifiers: %#v", modifiers) + } + _, _ = w.Write([]byte(`{ + "routes":[{ + "legs":[{ + "distanceMeters":1609, + "duration":"300s", + "localizedValues":{"distance":{"text":"1 mi"},"duration":{"text":"5 mins"}}, + "steps":[] + }] + }] +}`)) + })) + defer server.Close() + + client := NewClient(Options{APIKey: "test-key", DirectionsBaseURL: server.URL}) + _, err := client.Directions(context.Background(), DirectionsRequest{ + From: "Seattle", + To: "Portland", + Mode: "drive", + AvoidTolls: true, + AvoidHighways: true, + AvoidFerries: true, + }) + if err != nil { + t.Fatalf("Directions error: %v", err) + } +} + func TestDirectionsLocationBoundsValidation(t *testing.T) { req := DirectionsRequest{ FromLocation: &LatLng{Lat: 91, Lng: 0}, diff --git a/docs/directions.md b/docs/directions.md index 81b5482..ddfa721 100644 --- a/docs/directions.md +++ b/docs/directions.md @@ -33,9 +33,17 @@ Imperial units: goplaces directions --from-place-id --to-place-id --units imperial ``` +Driving route modifiers: + +```bash +goplaces directions --from "Paris" --to "Brest" --mode drive --avoid-tolls +goplaces directions --from "Paris" --to "Brest" --mode drive --avoid-highways --avoid-ferries +``` + ## Notes - Default mode is walking. - Default units are metric (use `--units imperial` for miles/feet). - Use `--steps` for turn-by-turn instructions. - Use `--compare drive` to add a driving ETA. +- Use `--avoid-tolls`, `--avoid-highways`, and `--avoid-ferries` with `--mode drive` to request drive routes that avoid those features when reasonable. diff --git a/docs/nearby-search.md b/docs/nearby-search.md index 0286834..48a88bf 100644 --- a/docs/nearby-search.md +++ b/docs/nearby-search.md @@ -34,3 +34,4 @@ response, err := client.NearbySearch(ctx, goplaces.NearbySearchRequest{ - Location restriction (lat/lng/radius) is required. - Use `IncludedTypes`/`--type` to filter result types. +- Results include `business_status` when Google returns it. diff --git a/e2e_test.go b/e2e_test.go index da4cf26..7d7e962 100644 --- a/e2e_test.go +++ b/e2e_test.go @@ -148,6 +148,46 @@ func TestE2ENearbySearch(t *testing.T) { } } +func TestE2EDirectionsAvoidTolls(t *testing.T) { + apiKey := os.Getenv("GOOGLE_PLACES_API_KEY") + if apiKey == "" { + t.Skip("GOOGLE_PLACES_API_KEY not set") + } + + from := os.Getenv("GOOGLE_PLACES_E2E_DIRECTIONS_FROM") + if from == "" { + from = "Paris, France" + } + to := os.Getenv("GOOGLE_PLACES_E2E_DIRECTIONS_TO") + if to == "" { + to = "Brest, France" + } + + ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) + defer cancel() + + client := NewClient(Options{ + APIKey: apiKey, + DirectionsBaseURL: os.Getenv("GOOGLE_DIRECTIONS_E2E_BASE_URL"), + Timeout: 15 * time.Second, + }) + + response, err := client.Directions(ctx, DirectionsRequest{ + From: from, + To: to, + Mode: "drive", + AvoidTolls: true, + Language: "en", + Region: "FR", + }) + if err != nil { + t.Fatalf("directions error: %v", err) + } + if response.DistanceMeters == 0 || response.DurationSeconds == 0 { + t.Fatalf("expected distance and duration, got %#v", response) + } +} + func TestE2EPhotoMedia(t *testing.T) { apiKey := os.Getenv("GOOGLE_PLACES_API_KEY") if apiKey == "" { diff --git a/internal/cli/cli_equals_test.go b/internal/cli/cli_equals_test.go index 3173d26..fb8b957 100644 --- a/internal/cli/cli_equals_test.go +++ b/internal/cli/cli_equals_test.go @@ -162,3 +162,55 @@ func TestRunDirectionsWithEqualsFlags(t *testing.T) { t.Fatalf("unexpected stdout: %s", stdout.String()) } } + +func TestRunDirectionsWithAvoidFlags(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != directionsPath { + t.Fatalf("unexpected path: %s", r.URL.Path) + } + var payload map[string]any + if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { + t.Fatalf("decode payload: %v", err) + } + if payload["travelMode"] != directionsModeDriveAPI { + t.Fatalf("unexpected mode: %#v", payload["travelMode"]) + } + modifiers, ok := payload["routeModifiers"].(map[string]any) + if !ok { + t.Fatalf("missing routeModifiers: %#v", payload) + } + if modifiers["avoidTolls"] != true || modifiers["avoidHighways"] != true || modifiers["avoidFerries"] != true { + t.Fatalf("unexpected routeModifiers: %#v", modifiers) + } + _, _ = w.Write([]byte(`{ + "routes":[{"legs":[{"distanceMeters":1000,"duration":"600s","localizedValues":{"distance":{"text":"1 km"},"duration":{"text":"10 mins"}},"steps":[]}]}] +}`)) + })) + defer server.Close() + + var stdout bytes.Buffer + var stderr bytes.Buffer + + exitCode := Run([]string{ + "directions", + "--from=A", + "--to=B", + "--api-key=test-key", + "--directions-base-url=" + server.URL, + "--mode=drive", + "--avoid-tolls", + "--avoid-highways", + "--avoid-ferries", + "--json", + }, &stdout, &stderr) + + if exitCode != 0 { + t.Fatalf("expected exit code 0, got %d (stdout=%s stderr=%s)", exitCode, stdout.String(), stderr.String()) + } + if stderr.Len() != 0 { + t.Fatalf("unexpected stderr: %s", stderr.String()) + } + if !strings.Contains(stdout.String(), "\"mode\": \"DRIVING\"") { + t.Fatalf("unexpected stdout: %s", stdout.String()) + } +} diff --git a/internal/cli/directions.go b/internal/cli/directions.go index ad32e07..ac92041 100644 --- a/internal/cli/directions.go +++ b/internal/cli/directions.go @@ -9,20 +9,23 @@ import ( // DirectionsCmd fetches directions between two points. type DirectionsCmd struct { - From string `help:"Origin address or place name."` - To string `help:"Destination address or place name."` - FromPlaceID string `help:"Origin place ID." name:"from-place-id"` - ToPlaceID string `help:"Destination place ID." name:"to-place-id"` - FromLat *float64 `help:"Origin latitude." name:"from-lat"` - FromLng *float64 `help:"Origin longitude." name:"from-lng"` - ToLat *float64 `help:"Destination latitude." name:"to-lat"` - ToLng *float64 `help:"Destination longitude." name:"to-lng"` - Mode string `help:"Travel mode: walk, drive, bicycle, transit." default:"walk"` - Compare string `help:"Compare with another mode: walk, drive, bicycle, transit."` - Steps bool `help:"Include step-by-step instructions."` - Units string `help:"Units: metric or imperial." default:"metric"` - Language string `help:"BCP-47 language code (e.g. en, en-US)."` - Region string `help:"CLDR region code (e.g. US, DE)."` + From string `help:"Origin address or place name."` + To string `help:"Destination address or place name."` + FromPlaceID string `help:"Origin place ID." name:"from-place-id"` + ToPlaceID string `help:"Destination place ID." name:"to-place-id"` + FromLat *float64 `help:"Origin latitude." name:"from-lat"` + FromLng *float64 `help:"Origin longitude." name:"from-lng"` + ToLat *float64 `help:"Destination latitude." name:"to-lat"` + ToLng *float64 `help:"Destination longitude." name:"to-lng"` + Mode string `help:"Travel mode: walk, drive, bicycle, transit." default:"walk"` + Compare string `help:"Compare with another mode: walk, drive, bicycle, transit."` + Steps bool `help:"Include step-by-step instructions."` + Units string `help:"Units: metric or imperial." default:"metric"` + AvoidTolls bool `help:"Avoid toll roads when driving."` + AvoidHighways bool `help:"Avoid highways when driving."` + AvoidFerries bool `help:"Avoid ferries when driving."` + Language string `help:"BCP-47 language code (e.g. en, en-US)."` + Region string `help:"CLDR region code (e.g. US, DE)."` } // Run executes the directions command. @@ -43,14 +46,17 @@ func (c *DirectionsCmd) Run(app *App) error { } request := goplaces.DirectionsRequest{ - From: c.From, - To: c.To, - FromPlaceID: c.FromPlaceID, - ToPlaceID: c.ToPlaceID, - Mode: primaryMode, - Units: c.Units, - Language: c.Language, - Region: c.Region, + From: c.From, + To: c.To, + FromPlaceID: c.FromPlaceID, + ToPlaceID: c.ToPlaceID, + Mode: primaryMode, + Units: c.Units, + AvoidTolls: c.AvoidTolls, + AvoidHighways: c.AvoidHighways, + AvoidFerries: c.AvoidFerries, + Language: c.Language, + Region: c.Region, } if c.FromLat != nil || c.FromLng != nil { if c.FromLat == nil || c.FromLng == nil { @@ -74,6 +80,9 @@ func (c *DirectionsCmd) Run(app *App) error { if compareMode != "" { compareRequest := request compareRequest.Mode = compareMode + compareRequest.AvoidTolls = false + compareRequest.AvoidHighways = false + compareRequest.AvoidFerries = false second, err := app.client.Directions(context.Background(), compareRequest) if err != nil { return err diff --git a/internal/cli/render.go b/internal/cli/render.go index 1255539..94637bc 100644 --- a/internal/cli/render.go +++ b/internal/cli/render.go @@ -237,6 +237,7 @@ func writePlaceSummary(out *bytes.Buffer, color Color, place goplaces.PlaceSumma writeRating(out, color, place.Rating, place.UserRatingCount, place.PriceLevel) writeTypes(out, color, place.Types) writeOpenNow(out, color, place.OpenNow) + writeLine(out, color, "Status", place.BusinessStatus) } func writeAutocompleteSuggestion(out *bytes.Buffer, color Color, suggestion goplaces.AutocompleteSuggestion) { @@ -255,6 +256,7 @@ func writePlaceDetails(out *bytes.Buffer, color Color, place goplaces.PlaceDetai writeRating(out, color, place.Rating, place.UserRatingCount, place.PriceLevel) writeTypes(out, color, place.Types) writeOpenNow(out, color, place.OpenNow) + writeLine(out, color, "Status", place.BusinessStatus) writeLine(out, color, "Phone", place.Phone) writeLine(out, color, "Website", place.Website) writePhotos(out, color, place.Photos) diff --git a/internal/cli/render_test.go b/internal/cli/render_test.go index 7fd2765..8ae68c0 100644 --- a/internal/cli/render_test.go +++ b/internal/cli/render_test.go @@ -25,6 +25,7 @@ func TestRenderSearch(t *testing.T) { PriceLevel: &level, Types: []string{"cafe", "coffee_shop"}, OpenNow: &open, + BusinessStatus: "OPERATIONAL", }, }, NextPageToken: "next", @@ -43,6 +44,9 @@ func TestRenderSearch(t *testing.T) { if !strings.Contains(output, "Open now") { t.Fatalf("missing open now") } + if !strings.Contains(output, "Status: OPERATIONAL") { + t.Fatalf("missing status") + } if !strings.Contains(output, "next") { t.Fatalf("missing next page token") } @@ -226,16 +230,17 @@ func TestRenderDetailsAndResolve(t *testing.T) { open := false level := 0 details := goplaces.PlaceDetails{ - PlaceID: "place-1", - Name: "Park", - Address: "Central", - Rating: floatPtr(4.2), - PriceLevel: &level, - Types: []string{"park"}, - Phone: "+1 555", - Website: "https://example.com", - Hours: []string{"Mon: 9-5"}, - OpenNow: &open, + PlaceID: "place-1", + Name: "Park", + Address: "Central", + Rating: floatPtr(4.2), + PriceLevel: &level, + Types: []string{"park"}, + Phone: "+1 555", + Website: "https://example.com", + Hours: []string{"Mon: 9-5"}, + OpenNow: &open, + BusinessStatus: "CLOSED_TEMPORARILY", Photos: []goplaces.Photo{ {Name: "places/place-1/photos/photo-1", WidthPx: 1200, HeightPx: 800}, }, @@ -258,6 +263,9 @@ func TestRenderDetailsAndResolve(t *testing.T) { if !strings.Contains(output, "Reviews:") || !strings.Contains(output, "Alice") { t.Fatalf("missing reviews output: %s", output) } + if !strings.Contains(output, "Status: CLOSED_TEMPORARILY") { + t.Fatalf("missing status output: %s", output) + } resolve := goplaces.LocationResolveResponse{ Results: []goplaces.ResolvedLocation{{PlaceID: "loc-1", Name: "Downtown"}}, diff --git a/nearby.go b/nearby.go index 05ac644..938c3cf 100644 --- a/nearby.go +++ b/nearby.go @@ -8,7 +8,7 @@ import ( "strings" ) -const nearbyFieldMask = "places.id,places.displayName,places.formattedAddress,places.location,places.rating,places.userRatingCount,places.priceLevel,places.types,places.currentOpeningHours" +const nearbyFieldMask = "places.id,places.displayName,places.formattedAddress,places.location,places.rating,places.userRatingCount,places.priceLevel,places.types,places.currentOpeningHours,places.businessStatus" // NearbySearch performs a nearby search around a location restriction. func (c *Client) NearbySearch(ctx context.Context, req NearbySearchRequest) (NearbySearchResponse, error) { diff --git a/payloads.go b/payloads.go index 34ca52d..ef093c3 100644 --- a/payloads.go +++ b/payloads.go @@ -16,6 +16,7 @@ type placeItem struct { Types []string `json:"types,omitempty"` CurrentOpeningHours *openingHours `json:"currentOpeningHours,omitempty"` RegularOpeningHours *openingHours `json:"regularOpeningHours,omitempty"` + BusinessStatus string `json:"businessStatus,omitempty"` NationalPhoneNumber string `json:"nationalPhoneNumber,omitempty"` WebsiteURI string `json:"websiteUri,omitempty"` Reviews []reviewPayload `json:"reviews,omitempty"` diff --git a/search.go b/search.go index ca66bdf..dd7601e 100644 --- a/search.go +++ b/search.go @@ -8,7 +8,7 @@ import ( "strings" ) -const searchFieldMask = "places.id,places.displayName,places.formattedAddress,places.location,places.rating,places.userRatingCount,places.priceLevel,places.types,places.currentOpeningHours,nextPageToken" +const searchFieldMask = "places.id,places.displayName,places.formattedAddress,places.location,places.rating,places.userRatingCount,places.priceLevel,places.types,places.currentOpeningHours,places.businessStatus,nextPageToken" // Search performs a text search with optional filters. func (c *Client) Search(ctx context.Context, req SearchRequest) (SearchResponse, error) { @@ -109,6 +109,7 @@ func mapPlaceSummary(place placeItem) PlaceSummary { PriceLevel: mapPriceLevel(place.PriceLevel), Types: place.Types, OpenNow: openNow(place.CurrentOpeningHours), + BusinessStatus: strings.TrimSpace(place.BusinessStatus), } } diff --git a/types.go b/types.go index ec78e8d..1bc975c 100644 --- a/types.go +++ b/types.go @@ -93,6 +93,7 @@ type PlaceSummary struct { PriceLevel *int `json:"price_level,omitempty"` Types []string `json:"types,omitempty"` OpenNow *bool `json:"open_now,omitempty"` + BusinessStatus string `json:"business_status,omitempty"` } // PlaceDetails is a detailed view of a place. @@ -109,6 +110,7 @@ type PlaceDetails struct { Website string `json:"website,omitempty"` Hours []string `json:"hours,omitempty"` OpenNow *bool `json:"open_now,omitempty"` + BusinessStatus string `json:"business_status,omitempty"` Reviews []Review `json:"reviews,omitempty"` Photos []Photo `json:"photos,omitempty"` }