mirror of
https://github.com/sstent/go-garth.git
synced 2026-01-26 17:11:42 +00:00
sync - build broken
This commit is contained in:
19
shared/interfaces/api_client.go
Normal file
19
shared/interfaces/api_client.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package interfaces
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
types "go-garth/internal/models/types"
|
||||
"go-garth/internal/users"
|
||||
)
|
||||
|
||||
// APIClient defines the interface for making API calls that data packages need.
|
||||
type APIClient interface {
|
||||
ConnectAPI(path string, method string, params url.Values, body io.Reader) ([]byte, error)
|
||||
GetUsername() string
|
||||
GetUserSettings() (*users.UserSettings, error)
|
||||
GetUserProfile() (*types.UserProfile, error)
|
||||
GetWellnessData(startDate, endDate time.Time) ([]types.WellnessData, error)
|
||||
}
|
||||
129
shared/interfaces/data.go
Normal file
129
shared/interfaces/data.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package interfaces
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go-garth/internal/utils"
|
||||
)
|
||||
|
||||
// Data defines the interface for Garmin Connect data models.
|
||||
// Concrete data types (BodyBattery, HRV, Sleep, etc.) must implement this interface.
|
||||
//
|
||||
// The Get method retrieves data for a single day.
|
||||
// The List method concurrently retrieves data for a range of days.
|
||||
type Data interface {
|
||||
Get(day time.Time, c APIClient) (interface{}, error)
|
||||
List(end time.Time, days int, c APIClient, maxWorkers int) ([]interface{}, []error)
|
||||
}
|
||||
|
||||
// BaseData provides a reusable implementation for data types to embed.
|
||||
// It handles the concurrent List() implementation while allowing concrete types
|
||||
// to focus on implementing the Get() method for their specific data structure.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// type BodyBatteryData {
|
||||
// interfaces.BaseData
|
||||
// // ... additional fields
|
||||
// }
|
||||
//
|
||||
// func NewBodyBatteryData() *BodyBatteryData {
|
||||
// bb := &BodyBatteryData{}
|
||||
// bb.GetFunc = bb.get // Assign the concrete Get implementation
|
||||
// return bb
|
||||
// }
|
||||
//
|
||||
// func (bb *BodyBatteryData) get(day time.Time, c APIClient) (interface{}, error) {
|
||||
// // Implementation specific to body battery data
|
||||
// }
|
||||
type BaseData struct {
|
||||
// GetFunc must be set by concrete types to implement the Get method.
|
||||
// This function pointer allows BaseData to call the concrete implementation.
|
||||
GetFunc func(day time.Time, c APIClient) (interface{}, error)
|
||||
}
|
||||
|
||||
// Get implements the Data interface by calling the configured GetFunc.
|
||||
// Returns an error if GetFunc is not set.
|
||||
func (b *BaseData) Get(day time.Time, c APIClient) (interface{}, error) {
|
||||
if b.GetFunc == nil {
|
||||
return nil, errors.New("GetFunc not implemented for this data type")
|
||||
}
|
||||
return b.GetFunc(day, c)
|
||||
}
|
||||
|
||||
// List implements concurrent data fetching using a worker pool pattern.
|
||||
// This method efficiently retrieves data for multiple days by distributing
|
||||
// work across a configurable number of workers (goroutines).
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// end: The end date of the range (inclusive)
|
||||
// days: Number of days to fetch (going backwards from end date)
|
||||
// c: Client instance for API access
|
||||
// maxWorkers: Maximum concurrent workers (minimum 1)
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// []interface{}: Slice of results (order matches date range)
|
||||
// []error: Slice of errors encountered during processing
|
||||
func (b *BaseData) List(end time.Time, days int, c APIClient, maxWorkers int) ([]interface{}, []error) {
|
||||
if maxWorkers < 1 {
|
||||
maxWorkers = 10 // Match Python's MAX_WORKERS
|
||||
}
|
||||
|
||||
dates := utils.DateRange(end, days)
|
||||
|
||||
// Define result type for channel
|
||||
type result struct {
|
||||
data interface{}
|
||||
err error
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
workCh := make(chan time.Time, days)
|
||||
resultsCh := make(chan result, days)
|
||||
|
||||
// Worker function
|
||||
worker := func() {
|
||||
defer wg.Done()
|
||||
for date := range workCh {
|
||||
data, err := b.Get(date, c)
|
||||
resultsCh <- result{data: data, err: err}
|
||||
}
|
||||
}
|
||||
|
||||
// Start workers
|
||||
wg.Add(maxWorkers)
|
||||
for i := 0; i < maxWorkers; i++ {
|
||||
go worker()
|
||||
}
|
||||
|
||||
// Send work
|
||||
go func() {
|
||||
for _, date := range dates {
|
||||
workCh <- date
|
||||
}
|
||||
close(workCh)
|
||||
}()
|
||||
|
||||
// Close results channel when workers are done
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(resultsCh)
|
||||
}()
|
||||
|
||||
var results []interface{}
|
||||
var errs []error
|
||||
|
||||
for r := range resultsCh {
|
||||
if r.err != nil {
|
||||
errs = append(errs, r.err)
|
||||
} else if r.data != nil {
|
||||
results = append(results, r.data)
|
||||
}
|
||||
}
|
||||
|
||||
return results, errs
|
||||
}
|
||||
Reference in New Issue
Block a user