package iskra import ( "fmt" "net" ) // Client is a synchronous iskra-server client. type Client struct { conn net.Conn } // Dial connects to an iskra-server at addr (e.g. "localhost:4847" or "/run/iskra.sock"). func Dial(addr string) (*Client, error) { network := "tcp" if len(addr) > 0 && addr[0] == '/' { network = "unix" } conn, err := net.Dial(network, addr) if err != nil { return nil, err } return &Client{conn: conn}, nil } func (c *Client) Close() error { return c.conn.Close() } func (c *Client) call(req []byte) ([]byte, error) { if err := WriteFrame(c.conn, req); err != nil { return nil, fmt.Errorf("write: %w", err) } resp, err := ReadFrame(c.conn) if err != nil { return nil, fmt.Errorf("read: %w", err) } if len(resp) == 0 { return nil, fmt.Errorf("empty response") } if resp[0] == StatusError { if len(resp) > 1 { return nil, fmt.Errorf("server: %s", string(resp[1:])) } return nil, fmt.Errorf("server error") } return resp, nil } func (c *Client) ClientBigramWeight(domain uint8, coord uint64, prev, next string) (uint32, error) { resp, err := c.call(EncodeBigramWeightReq(domain, coord, prev, next)) if err != nil { return 0, err } if len(resp) < 5 { return 0, fmt.Errorf("short response") } v, _ := DecodeUint32(resp, 1) return v, nil } func (c *Client) ClientBigramWeightRelaxed(domain uint8, coord uint64, prev, next string) (uint32, uint64, error) { resp, err := c.call(EncodeBigramWeightRelaxedReq(domain, coord, prev, next)) if err != nil { return 0, 0, err } if len(resp) < 13 { return 0, 0, fmt.Errorf("short response") } w, _ := DecodeUint32(resp, 1) co, _ := DecodeUint64(resp, 5) return w, co, nil } func (c *Client) ClientIngestBigram(domain uint8, coord uint64, prev, next string) (uint32, error) { resp, err := c.call(EncodeIngestBigramReq(domain, coord, prev, next)) if err != nil { return 0, err } if len(resp) < 5 { return 0, fmt.Errorf("short response") } v, _ := DecodeUint32(resp, 1) return v, nil } func (c *Client) ClientIngestText(domain uint8, coord uint64, tokens []string) error { _, err := c.call(EncodeIngestTextReq(domain, coord, tokens)) return err } func (c *Client) ClientWalkStep(domain uint8, coord uint64, word string, maxCandidates int32) ([]WalkCandidate, error) { resp, err := c.call(EncodeWalkStepReq(domain, coord, word, uint16(maxCandidates))) if err != nil { return nil, err } if len(resp) < 3 { return nil, fmt.Errorf("short response") } count := int32(uint16(resp[1]) | uint16(resp[2])<<8) var candidates []WalkCandidate off := 3 for i := 0; i < count; i++ { if off >= len(resp) { break } var cand WalkCandidate cand.State.Domain = resp[off] off++ cand.State.Coord, _ = DecodeUint64(resp, off) off += 8 var n int32 cand.State.Word, n = DecodeString(resp, off) off += n cand.State.Score, _ = DecodeUint32(resp, off) off += 4 if off < len(resp) { cand.State.Depth = resp[off] off++ } cand.Weight, _ = DecodeUint32(resp, off) off += 4 candidates = append(candidates, cand) } return candidates, nil } func (c *Client) ClientTranslate(srcDomain, dstDomain uint8, coord uint64, word string) (string, error) { resp, err := c.call(EncodeTranslateReq(srcDomain, dstDomain, coord, word)) if err != nil { return "", err } if resp[0] == StatusNotFound { return "", nil } s, _ := DecodeString(resp, 1) return s, nil } func (c *Client) ClientBeamRun(domain uint8, coord uint64, word string, width, maxSteps int32, kind WalkKind) (WalkState, error) { resp, err := c.call(EncodeBeamRunReq(domain, coord, word, uint16(width), uint16(maxSteps), kind)) if err != nil { return WalkState{}, err } if len(resp) < 15 { return WalkState{}, fmt.Errorf("short response") } var s WalkState s.Domain = resp[1] s.Depth = resp[2] s.Coord, _ = DecodeUint64(resp, 3) s.Score, _ = DecodeUint32(resp, 11) s.Word, _ = DecodeString(resp, 15) return s, nil } func (c *Client) ClientFlush() error { _, err := c.call(EncodeFlushReq()) return err }