github.com/elves/Elvish@v0.12.0/daemon/client.go (about)

     1  package daemon
     2  
     3  import (
     4  	"errors"
     5  	"net/rpc"
     6  	"sync"
     7  
     8  	"github.com/elves/elvish/store/storedefs"
     9  )
    10  
    11  const retriesOnShutdown = 3
    12  
    13  var (
    14  	// ErrClientNotInitialized is returned when the Client is not initialized.
    15  	ErrClientNotInitialized = errors.New("client not initialized")
    16  	// ErrDaemonUnreachable is returned when the daemon cannot be reached after
    17  	// several retries.
    18  	ErrDaemonUnreachable = errors.New("daemon offline")
    19  )
    20  
    21  // Client is a client to the Elvish daemon. A nil *Client is safe to use.
    22  type Client struct {
    23  	sockPath  string
    24  	rpcClient *rpc.Client
    25  	waits     sync.WaitGroup
    26  }
    27  
    28  var _ storedefs.Store = (*Client)(nil)
    29  
    30  // NewClient creates a new Client instance that talks to the socket. Connection
    31  // creation is deferred to the first request.
    32  func NewClient(sockPath string) *Client {
    33  	return &Client{sockPath, nil, sync.WaitGroup{}}
    34  }
    35  
    36  // SockPath returns the socket path that the Client talks to. If the client is
    37  // nil, it returns an empty string.
    38  func (c *Client) SockPath() string {
    39  	if c == nil {
    40  		return ""
    41  	}
    42  	return c.sockPath
    43  }
    44  
    45  // ResetConn resets the current connection. A new connection will be established
    46  // the next time a request is made. If the client is nil, it does nothing.
    47  func (c *Client) ResetConn() error {
    48  	if c == nil || c.rpcClient == nil {
    49  		return nil
    50  	}
    51  	rc := c.rpcClient
    52  	c.rpcClient = nil
    53  	return rc.Close()
    54  }
    55  
    56  // Close waits for all outstanding requests to finish and close the connection.
    57  // If the client is nil, it does nothing and returns nil.
    58  func (c *Client) Close() error {
    59  	if c == nil {
    60  		return nil
    61  	}
    62  	c.waits.Wait()
    63  	return c.ResetConn()
    64  }
    65  
    66  func (c *Client) call(f string, req, res interface{}) error {
    67  	if c == nil {
    68  		return ErrClientNotInitialized
    69  	}
    70  	c.waits.Add(1)
    71  	defer c.waits.Done()
    72  
    73  	for attempt := 0; attempt < retriesOnShutdown; attempt++ {
    74  		if c.rpcClient == nil {
    75  			conn, err := dial(c.sockPath)
    76  			if err != nil {
    77  				return err
    78  			}
    79  			c.rpcClient = rpc.NewClient(conn)
    80  		}
    81  
    82  		err := c.rpcClient.Call(ServiceName+"."+f, req, res)
    83  		if err == rpc.ErrShutdown {
    84  			// Clear rpcClient so as to reconnect next time
    85  			c.rpcClient = nil
    86  			continue
    87  		} else {
    88  			return err
    89  		}
    90  	}
    91  	return ErrDaemonUnreachable
    92  }
    93  
    94  // Convenience methods for RPC methods. These are quite repetitive; when the
    95  // number of RPC calls grow above some threshold, a code generator should be
    96  // written to generate them.
    97  
    98  func (c *Client) Version() (int, error) {
    99  	req := &VersionRequest{}
   100  	res := &VersionResponse{}
   101  	err := c.call("Version", req, res)
   102  	return res.Version, err
   103  }
   104  
   105  func (c *Client) Pid() (int, error) {
   106  	req := &PidRequest{}
   107  	res := &PidResponse{}
   108  	err := c.call("Pid", req, res)
   109  	return res.Pid, err
   110  }
   111  
   112  func (c *Client) NextCmdSeq() (int, error) {
   113  	req := &NextCmdRequest{}
   114  	res := &NextCmdSeqResponse{}
   115  	err := c.call("NextCmdSeq", req, res)
   116  	return res.Seq, err
   117  }
   118  
   119  func (c *Client) AddCmd(text string) (int, error) {
   120  	req := &AddCmdRequest{text}
   121  	res := &AddCmdResponse{}
   122  	err := c.call("AddCmd", req, res)
   123  	return res.Seq, err
   124  }
   125  
   126  func (c *Client) DelCmd(seq int) error {
   127  	req := &DelCmdRequest{seq}
   128  	res := &DelCmdResponse{}
   129  	err := c.call("DelCmd", req, res)
   130  	return err
   131  }
   132  
   133  func (c *Client) Cmd(seq int) (string, error) {
   134  	req := &CmdRequest{seq}
   135  	res := &CmdResponse{}
   136  	err := c.call("Cmd", req, res)
   137  	return res.Text, err
   138  }
   139  
   140  func (c *Client) Cmds(from, upto int) ([]string, error) {
   141  	req := &CmdsRequest{from, upto}
   142  	res := &CmdsResponse{}
   143  	err := c.call("Cmds", req, res)
   144  	return res.Cmds, err
   145  }
   146  
   147  func (c *Client) NextCmd(from int, prefix string) (int, string, error) {
   148  	req := &NextCmdRequest{from, prefix}
   149  	res := &NextCmdResponse{}
   150  	err := c.call("NextCmd", req, res)
   151  	return res.Seq, res.Text, err
   152  }
   153  
   154  func (c *Client) PrevCmd(upto int, prefix string) (int, string, error) {
   155  	req := &PrevCmdRequest{upto, prefix}
   156  	res := &PrevCmdResponse{}
   157  	err := c.call("PrevCmd", req, res)
   158  	return res.Seq, res.Text, err
   159  }
   160  
   161  func (c *Client) AddDir(dir string, incFactor float64) error {
   162  	req := &AddDirRequest{dir, incFactor}
   163  	res := &AddDirResponse{}
   164  	err := c.call("AddDir", req, res)
   165  	return err
   166  }
   167  
   168  func (c *Client) DelDir(dir string) error {
   169  	req := &DelDirRequest{dir}
   170  	res := &DelDirResponse{}
   171  	err := c.call("DelDir", req, res)
   172  	return err
   173  }
   174  
   175  func (c *Client) Dirs(blacklist map[string]struct{}) ([]storedefs.Dir, error) {
   176  	req := &DirsRequest{blacklist}
   177  	res := &DirsResponse{}
   178  	err := c.call("Dirs", req, res)
   179  	return res.Dirs, err
   180  }
   181  
   182  func (c *Client) SharedVar(name string) (string, error) {
   183  	req := &SharedVarRequest{name}
   184  	res := &SharedVarResponse{}
   185  	err := c.call("SharedVar", req, res)
   186  	return res.Value, err
   187  }
   188  
   189  func (c *Client) SetSharedVar(name, value string) error {
   190  	req := &SetSharedVarRequest{name, value}
   191  	res := &SetSharedVarResponse{}
   192  	return c.call("SetSharedVar", req, res)
   193  }
   194  
   195  func (c *Client) DelSharedVar(name string) error {
   196  	req := &DelSharedVarRequest{}
   197  	res := &DelSharedVarResponse{}
   198  	return c.call("DelSharedVar", req, res)
   199  }