1package client
2
3import (
4	"fmt"
5	"io"
6	"net/http"
7	"net/url"
8
9	"github.com/cayleygraph/cayley/quad"
10	"github.com/cayleygraph/cayley/quad/pquads"
11)
12
13func New(addr string) *Client {
14	return &Client{addr: addr, cli: http.DefaultClient}
15}
16
17type Client struct {
18	addr string
19	cli  *http.Client
20}
21
22func (c *Client) SetHttpClient(cli *http.Client) {
23	c.cli = cli
24}
25func (c *Client) url(s string, q map[string]string) string {
26	addr := c.addr + s
27	if len(q) != 0 {
28		p := make(url.Values, len(q))
29		for k, v := range q {
30			p.Set(k, v)
31		}
32		addr += "?" + p.Encode()
33	}
34	return addr
35}
36
37type errRequestFailed struct {
38	Status     string
39	StatusCode int
40}
41
42func (e errRequestFailed) Error() string {
43	return fmt.Sprintf("request failed: %d %v", e.StatusCode, e.Status)
44}
45func (c *Client) QuadReader() (quad.ReadCloser, error) {
46	resp, err := http.Get(c.url("/api/v2/read", map[string]string{
47		"format": "pquads",
48	}))
49	if err != nil {
50		return nil, err
51	}
52	if resp.StatusCode != http.StatusOK {
53		resp.Body.Close()
54		return nil, errRequestFailed{StatusCode: resp.StatusCode, Status: resp.Status}
55	}
56	r := pquads.NewReader(resp.Body, 10*1024*1024)
57	r.SetCloser(resp.Body)
58	return r, nil
59}
60
61type funcCloser struct {
62	f      func() error
63	closed bool
64}
65
66func (c funcCloser) Close() error {
67	if c.closed {
68		return nil
69	}
70	c.closed = true
71	return c.f()
72}
73func (c *Client) QuadWriter() (quad.WriteCloser, error) {
74	pr, pw := io.Pipe()
75	req, err := http.NewRequest("POST", c.url("/api/v2/write", nil), pr)
76	if err != nil {
77		return nil, err
78	}
79	req.Header.Set("Content-Type", pquads.ContentType)
80	errc := make(chan error, 1)
81	go func() {
82		defer func() {
83			close(errc)
84			pr.Close()
85		}()
86		resp, err := c.cli.Do(req)
87		if resp != nil && resp.Body != nil {
88			defer resp.Body.Close()
89		}
90		if err == nil && resp.StatusCode != http.StatusOK {
91			err = errRequestFailed{StatusCode: resp.StatusCode, Status: resp.Status}
92		}
93		errc <- err
94	}()
95	qw := pquads.NewWriter(pw, &pquads.Options{
96		Full:   false,
97		Strict: false,
98	})
99	qw.SetCloser(funcCloser{f: func() error {
100		pw.Close()
101		return <-errc
102	}})
103	return qw, nil
104}
105