2024-07-30 14:28:39 +02:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"forge.cadoles.com/cadoles/go-emlid/reach/client/protocol"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Client struct {
|
|
|
|
addr string
|
|
|
|
opts *Options
|
|
|
|
|
|
|
|
proto protocol.Identifier
|
|
|
|
ops protocol.Operations
|
|
|
|
|
|
|
|
getProtocolOnce sync.Once
|
|
|
|
getProtocolOnceErr error
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) Protocol(ctx context.Context) (protocol.Identifier, protocol.Operations, error) {
|
|
|
|
id, ops, err := c.getProtocol(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return "", nil, errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return id, ops, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) getProtocol(ctx context.Context) (protocol.Identifier, protocol.Operations, error) {
|
|
|
|
c.getProtocolOnce.Do(func() {
|
2024-08-13 15:21:53 +02:00
|
|
|
opts := []protocol.ProtocolOptionFunc{
|
2024-08-05 18:10:19 +02:00
|
|
|
protocol.WithProtocolLogger(c.opts.Logger),
|
|
|
|
protocol.WithProtocolDial(c.opts.Dial),
|
2024-08-13 15:21:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
preferred, err := c.opts.Protocols.Get(c.opts.PreferredProtocol, opts...)
|
|
|
|
if err != nil && c.opts.FallbackProtocol == "" {
|
2024-07-30 14:28:39 +02:00
|
|
|
c.getProtocolOnceErr = errors.WithStack(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-08-13 15:21:53 +02:00
|
|
|
if preferred != nil {
|
|
|
|
preferredCtx, cancel := context.WithTimeout(ctx, c.opts.AvailableTimeout)
|
|
|
|
defer cancel()
|
2024-07-30 14:28:39 +02:00
|
|
|
|
2024-08-13 15:21:53 +02:00
|
|
|
ok, err := preferred.Available(preferredCtx, c.addr)
|
|
|
|
if err != nil && c.opts.FallbackProtocol == "" {
|
|
|
|
c.getProtocolOnceErr = errors.WithStack(err)
|
|
|
|
return
|
2024-07-30 14:28:39 +02:00
|
|
|
}
|
|
|
|
|
2024-08-13 15:21:53 +02:00
|
|
|
if ok {
|
|
|
|
c.proto = preferred.Identifier()
|
|
|
|
c.ops = preferred.Operations(c.addr)
|
|
|
|
return
|
2024-07-30 14:28:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-13 15:21:53 +02:00
|
|
|
if c.opts.FallbackProtocol == "" {
|
|
|
|
c.getProtocolOnceErr = errors.WithStack(err)
|
2024-07-30 14:28:39 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-08-13 15:21:53 +02:00
|
|
|
fallback, err := c.opts.Protocols.Get(c.opts.FallbackProtocol, opts...)
|
|
|
|
if err != nil {
|
|
|
|
c.getProtocolOnceErr = errors.WithStack(err)
|
2024-07-30 14:28:39 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-08-13 15:21:53 +02:00
|
|
|
fallbackCtx, cancel := context.WithTimeout(ctx, c.opts.AvailableTimeout)
|
|
|
|
defer cancel()
|
2024-07-30 14:28:39 +02:00
|
|
|
|
2024-08-13 15:21:53 +02:00
|
|
|
ok, err := fallback.Available(fallbackCtx, c.addr)
|
|
|
|
if err != nil && c.opts.FallbackProtocol == "" {
|
|
|
|
c.getProtocolOnceErr = errors.WithStack(err)
|
|
|
|
return
|
2024-07-30 14:28:39 +02:00
|
|
|
}
|
|
|
|
|
2024-08-13 15:21:53 +02:00
|
|
|
if ok {
|
|
|
|
c.proto = fallback.Identifier()
|
|
|
|
c.ops = fallback.Operations(c.addr)
|
2024-07-30 14:28:39 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-08-13 15:21:53 +02:00
|
|
|
c.getProtocolOnceErr = errors.Errorf("neither preferred protocol '%v' or fallback '%v' are available", c.opts.PreferredProtocol, c.opts.FallbackProtocol)
|
2024-07-30 14:28:39 +02:00
|
|
|
})
|
|
|
|
if c.getProtocolOnceErr != nil {
|
|
|
|
return "", nil, errors.WithStack(c.getProtocolOnceErr)
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.proto, c.ops, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewClient(addr string, funcs ...OptionFunc) *Client {
|
|
|
|
opts := NewOptions(funcs...)
|
|
|
|
client := &Client{
|
|
|
|
addr: addr,
|
|
|
|
opts: opts,
|
|
|
|
}
|
|
|
|
return client
|
|
|
|
}
|