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() { opts := []protocol.ProtocolOptionFunc{ protocol.WithProtocolLogger(c.opts.Logger), protocol.WithProtocolDial(c.opts.Dial), } preferred, err := c.opts.Protocols.Get(c.opts.PreferredProtocol, opts...) if err != nil && c.opts.FallbackProtocol == "" { c.getProtocolOnceErr = errors.WithStack(err) return } if preferred != nil { preferredCtx, cancel := context.WithTimeout(ctx, c.opts.AvailableTimeout) defer cancel() ok, err := preferred.Available(preferredCtx, c.addr) if err != nil && c.opts.FallbackProtocol == "" { c.getProtocolOnceErr = errors.WithStack(err) return } if ok { c.proto = preferred.Identifier() c.ops = preferred.Operations(c.addr) return } } if c.opts.FallbackProtocol == "" { c.getProtocolOnceErr = errors.WithStack(err) return } fallback, err := c.opts.Protocols.Get(c.opts.FallbackProtocol, opts...) if err != nil { c.getProtocolOnceErr = errors.WithStack(err) return } fallbackCtx, cancel := context.WithTimeout(ctx, c.opts.AvailableTimeout) defer cancel() ok, err := fallback.Available(fallbackCtx, c.addr) if err != nil && c.opts.FallbackProtocol == "" { c.getProtocolOnceErr = errors.WithStack(err) return } if ok { c.proto = fallback.Identifier() c.ops = fallback.Operations(c.addr) return } c.getProtocolOnceErr = errors.Errorf("neither preferred protocol '%v' or fallback '%v' are available", c.opts.PreferredProtocol, c.opts.FallbackProtocol) }) 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 }