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() { availables, err := c.opts.Protocols.Availables( ctx, c.addr, c.opts.AvailableTimeout, protocol.WithProtocolLogger(c.opts.Logger), protocol.WithProtocolDial(c.opts.Dial), ) if err != nil { c.getProtocolOnceErr = errors.WithStack(err) return } var preferred protocol.Protocol var fallback protocol.Protocol for _, proto := range availables { if proto.Identifier() == c.opts.FallbackProtocol { fallback = proto } if proto.Identifier() == c.opts.PreferredProtocol { preferred = proto break } } if preferred != nil { c.proto = preferred.Identifier() c.ops = preferred.Operations(c.addr) return } if fallback != nil { c.proto = fallback.Identifier() c.ops = fallback.Operations(c.addr) return } for _, proto := range availables { if proto.Identifier() != c.opts.FallbackProtocol { continue } fallback = proto break } if fallback == nil { c.getProtocolOnceErr = errors.Errorf("neither preferred protocol '%v' or fallback '%v' are available", c.opts.PreferredProtocol, c.opts.FallbackProtocol) return } c.proto = fallback.Identifier() c.ops = fallback.Operations(c.addr) }) 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 }