package client import ( "context" "iter" "time" "gitea.libretechconsulting.com/rmcguire/wingbits/pkg/types/readsb" "gitea.libretechconsulting.com/rmcguire/wingbits/pkg/types/wingbits" ) // Poll turns any fetch into a pull-based stream: an iter.Seq2[R, error] that // yields a fresh result immediately, then once per interval, until ctx is // cancelled or the consumer breaks out of the range. Each step yields exactly // one of a value or the error from that poll. // // It is the engine behind every Poll* method, exported so the same cadence can // be applied to a custom or composed fetch without re-implementing the loop. // Because the iterator runs in the consumer's goroutine, breaking the range // stops polling immediately — there is no background goroutine to leak. func Poll[R any](ctx context.Context, interval time.Duration, fetch func(context.Context) (R, error)) iter.Seq2[R, error] { return func(yield func(R, error) bool) { tick := time.NewTicker(interval) defer tick.Stop() for { if !yield(fetch(ctx)) { return } select { case <-ctx.Done(): return case <-tick.C: } } } } // PollAircraft polls aircraft.json every interval, applying the given filters. func (c *Client) PollAircraft(ctx context.Context, interval time.Duration, filters ...readsb.AircraftFilter) iter.Seq2[*readsb.AircraftReport, error] { return Poll(ctx, interval, func(ctx context.Context) (*readsb.AircraftReport, error) { return c.Aircraft(ctx, filters...) }) } // PollStats polls stats.json every interval. func (c *Client) PollStats(ctx context.Context, interval time.Duration) iter.Seq2[*readsb.Stats, error] { return Poll(ctx, interval, c.Stats) } // PollReceiver polls receiver.json every interval. func (c *Client) PollReceiver(ctx context.Context, interval time.Duration) iter.Seq2[*readsb.Receiver, error] { return Poll(ctx, interval, c.Receiver) } // PollOutline polls outline.json every interval. func (c *Client) PollOutline(ctx context.Context, interval time.Duration) iter.Seq2[*readsb.Outline, error] { return Poll(ctx, interval, c.Outline) } // PollDiagnostics polls /network/diagnostics every interval. func (c *Client) PollDiagnostics(ctx context.Context, interval time.Duration) iter.Seq2[*wingbits.Diagnostics, error] { return Poll(ctx, interval, c.Diagnostics) } // PollMetrics polls /metrics every interval. func (c *Client) PollMetrics(ctx context.Context, interval time.Duration) iter.Seq2[*wingbits.Metrics, error] { return Poll(ctx, interval, c.Metrics) } // PollStatus polls the Tailscale status every interval. func (c *Client) PollStatus(ctx context.Context, interval time.Duration) iter.Seq2[*wingbits.TailscaleStatus, error] { return Poll(ctx, interval, c.Status) }