mirror of
https://github.com/sstent/go-garth.git
synced 2026-01-25 08:35:06 +00:00
143 lines
3.5 KiB
Go
143 lines
3.5 KiB
Go
package data
|
|
|
|
import (
|
|
"errors"
|
|
"sync"
|
|
"time"
|
|
|
|
"garmin-connect/garth/client"
|
|
)
|
|
|
|
// Data defines the interface for Garmin Connect data types.
|
|
// 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 *client.Client) (interface{}, error)
|
|
List(end time.Time, days int, c *client.Client, 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 struct {
|
|
// data.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 *client.Client) (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 *client.Client) (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 *client.Client) (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: First error encountered during processing, if any
|
|
func (b *BaseData) List(end time.Time, days int, c *client.Client, maxWorkers int) ([]interface{}, error) {
|
|
if maxWorkers < 1 {
|
|
maxWorkers = 1
|
|
}
|
|
|
|
// Generate date range (end backwards for 'days' days)
|
|
dates := make([]time.Time, days)
|
|
for i := 0; i < days; i++ {
|
|
dates[i] = end.AddDate(0, 0, -i)
|
|
}
|
|
|
|
var wg sync.WaitGroup
|
|
workCh := make(chan time.Time, days)
|
|
resultsCh := make(chan interface{}, days)
|
|
errCh := make(chan error, 1)
|
|
done := make(chan bool)
|
|
|
|
// Worker function
|
|
worker := func() {
|
|
defer wg.Done()
|
|
for date := range workCh {
|
|
result, err := b.Get(date, c)
|
|
if err != nil {
|
|
select {
|
|
case errCh <- err:
|
|
default:
|
|
}
|
|
return
|
|
}
|
|
resultsCh <- result
|
|
}
|
|
}
|
|
|
|
// Start workers
|
|
wg.Add(maxWorkers)
|
|
for i := 0; i < maxWorkers; i++ {
|
|
go worker()
|
|
}
|
|
|
|
// Send work to channel
|
|
go func() {
|
|
for _, date := range dates {
|
|
workCh <- date
|
|
}
|
|
close(workCh)
|
|
}()
|
|
|
|
// Close results channel when all workers finish
|
|
go func() {
|
|
wg.Wait()
|
|
close(resultsCh)
|
|
done <- true
|
|
}()
|
|
|
|
// Collect results
|
|
var results []interface{}
|
|
var err error
|
|
|
|
collect:
|
|
for {
|
|
select {
|
|
case result := <-resultsCh:
|
|
results = append(results, result)
|
|
case err = <-errCh:
|
|
break collect
|
|
case <-done:
|
|
break collect
|
|
}
|
|
}
|
|
|
|
return results, err
|
|
}
|