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 } // Close implements protocol.Operations. func (c *Client) Close(ctx context.Context) error { _, ops, err := c.getProtocol(ctx) if err != nil { return errors.WithStack(err) } if err := ops.Close(ctx); err != nil { return errors.WithStack(err) } return nil } // Connect implements protocol.Operations. func (c *Client) Connect(ctx context.Context) error { _, ops, err := c.getProtocol(ctx) if err != nil { return errors.WithStack(err) } if err := ops.Connect(ctx); err != nil { return errors.WithStack(err) } return nil } func (c *Client) Protocol(ctx context.Context) (protocol.Identifier, error) { id, _, err := c.getProtocol(ctx) if err != nil { return "", errors.WithStack(err) } return id, 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) 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 } var _ protocol.Operations = &Client{}