@@ -24,7 +24,7 @@ const (
2424)
2525
2626// StartClient starts a client for the given options.
27- func StartClient [Q , M , R any ](opts ClientOptions [Q , M , R ]) (* Client [Q , M , R ], error ) {
27+ func StartClient [C , Q , M , R any ](opts ClientOptions [C , Q , M , R ]) (* Client [C , Q , M , R ], error ) {
2828 if opts .Codec == nil {
2929 return nil , errors .New ("opts: Codec is required" )
3030 }
@@ -37,16 +37,23 @@ func StartClient[Q, M, R any](opts ClientOptions[Q, M, R]) (*Client[Q, M, R], er
3737 return nil , err
3838 }
3939
40- return & Client [Q , M , R ]{
40+ c := & Client [C , Q , M , R ]{
4141 rawClient : rawClient ,
4242 opts : opts ,
43- }, nil
43+ }
44+
45+ err = c .init (opts .Config )
46+ if err != nil {
47+ return nil , err
48+ }
49+
50+ return c , nil
4451}
4552
4653// Client is a strongly typed RPC client.
47- type Client [Q , M , R any ] struct {
54+ type Client [C , Q , M , R any ] struct {
4855 rawClient * ClientRaw
49- opts ClientOptions [Q , M , R ]
56+ opts ClientOptions [C , Q , M , R ]
5057}
5158
5259// Result is the result of a request
@@ -85,13 +92,49 @@ func (r Result[M, R]) close() {
8592// MessagesRaw returns the raw messages from the server.
8693// These are not connected to the request-response flow,
8794// typically used for log messages etc.
88- func (c * Client [Q , M , R ]) MessagesRaw () <- chan Message {
95+ func (c * Client [C , Q , M , R ]) MessagesRaw () <- chan Message {
8996 return c .rawClient .Messages
9097}
9198
99+ // init passes the configuration to the server.
100+ func (c * Client [C , Q , M , R ]) init (cfg C ) error {
101+ body , err := c .opts .Codec .Encode (cfg )
102+ if err != nil {
103+ return fmt .Errorf ("failed to encode config: %w" , err )
104+ }
105+ var (
106+ messagec = make (chan Message , 10 )
107+ errc = make (chan error , 1 )
108+ )
109+
110+ go func () {
111+ err := c .rawClient .Execute (
112+ func (m * Message ) {
113+ m .Body = body
114+ m .Header .Status = MessageStatusInitServer
115+ },
116+ messagec ,
117+ )
118+ if err != nil {
119+ errc <- fmt .Errorf ("failed to execute init: %w" , err )
120+ }
121+ }()
122+
123+ select {
124+ case err := <- errc :
125+ return err
126+ case m := <- messagec :
127+ if m .Header .Status != MessageStatusOK {
128+ return fmt .Errorf ("failed to init: %s (error code %d)" , m .Body , m .Header .Status )
129+ }
130+ }
131+
132+ return nil
133+ }
134+
92135// Execute sends the request to the server and returns the result.
93136// You should check Err() both before and after reading from the messages and receipt channels.
94- func (c * Client [Q , M , R ]) Execute (r Q ) Result [M , R ] {
137+ func (c * Client [C , Q , M , R ]) Execute (r Q ) Result [M , R ] {
95138 result := Result [M , R ]{
96139 messages : make (chan M , 10 ),
97140 receipt : make (chan R , 1 ),
@@ -112,28 +155,31 @@ func (c *Client[Q, M, R]) Execute(r Q) Result[M, R] {
112155
113156 messagesRaw := make (chan Message , 10 )
114157 go func () {
115- err := c .rawClient .Execute (body , messagesRaw )
158+ err := c .rawClient .Execute (func ( m * Message ) { m . Body = body } , messagesRaw )
116159 if err != nil {
117160 result .errc <- fmt .Errorf ("failed to execute: %w" , err )
118161 }
119162 }()
120163
121164 for message := range messagesRaw {
122- if message .Header .Status > MessageStatusContinue && message .Header .Status <= MessageStatusSystemReservedMax {
165+ if message .Header .Status >= MessageStatusErrDecodeFailed && message .Header .Status <= MessageStatusSystemReservedMax {
123166 // All of these are currently error situations produced by the server.
124167 result .errc <- fmt .Errorf ("%s (error code %d)" , message .Body , message .Header .Status )
125168 return
126169 }
127170
128- if message .Header .Status == MessageStatusContinue {
171+ switch message .Header .Status {
172+ case MessageStatusContinue :
129173 var resp M
130174 err = c .opts .Codec .Decode (message .Body , & resp )
131175 if err != nil {
132176 result .errc <- err
133177 return
134178 }
135179 result .messages <- resp
136- } else {
180+ case MessageStatusInitServer :
181+ panic ("unexpected status" )
182+ default :
137183 // Receipt.
138184 var rec R
139185 err = c .opts .Codec .Decode (message .Body , & rec )
@@ -152,7 +198,7 @@ func (c *Client[Q, M, R]) Execute(r Q) Result[M, R] {
152198}
153199
154200// Close closes the client.
155- func (c * Client [Q , M , R ]) Close () error {
201+ func (c * Client [C , Q , M , R ]) Close () error {
156202 return c .rawClient .Close ()
157203}
158204
@@ -248,10 +294,10 @@ func (c *ClientRaw) Close() error {
248294// Execute sends body to the server and sends any messages to the messages channel.
249295// It's safe to call Execute from multiple goroutines.
250296// The messages channel wil be closed when the call is done.
251- func (c * ClientRaw ) Execute (body [] byte , messages chan <- Message ) error {
297+ func (c * ClientRaw ) Execute (withMessage func ( m * Message ) , messages chan <- Message ) error {
252298 defer close (messages )
253299
254- call , err := c .newCall (body , messages )
300+ call , err := c .newCall (withMessage , messages )
255301 if err != nil {
256302 return err
257303 }
@@ -276,20 +322,21 @@ func (c *ClientRaw) addErrContext(op string, err error) error {
276322 return fmt .Errorf ("%s: %s %s" , op , err , c .conn .stdErr .String ())
277323}
278324
279- func (c * ClientRaw ) newCall (body [] byte , messages chan <- Message ) (* call , error ) {
325+ func (c * ClientRaw ) newCall (withMessage func ( m * Message ) , messages chan <- Message ) (* call , error ) {
280326 c .mu .Lock ()
281327 c .seq ++
282328 id := c .seq
329+ m := Message {
330+ Header : Header {
331+ Version : c .version ,
332+ ID : id ,
333+ },
334+ }
335+ withMessage (& m )
283336
284337 call := & call {
285- Done : make (chan * call , 1 ),
286- Request : Message {
287- Header : Header {
288- Version : c .version ,
289- ID : id ,
290- },
291- Body : body ,
292- },
338+ Done : make (chan * call , 1 ),
339+ Request : m ,
293340 Messages : messages ,
294341 }
295342
@@ -384,8 +431,13 @@ func (c *ClientRaw) send(call *call) error {
384431}
385432
386433// ClientOptions are options for the client.
387- type ClientOptions [Q , M , R any ] struct {
434+ type ClientOptions [C , Q , M , R any ] struct {
388435 ClientRawOptions
436+
437+ // The configuration to pass to the server.
438+ Config C
439+
440+ // The codec to use.
389441 Codec codecs.Codec
390442}
391443
0 commit comments