1package pouchdb
2
3import (
4	"bytes"
5	"context"
6	"encoding/json"
7	"fmt"
8	"io/ioutil"
9	"os"
10
11	"github.com/gopherjs/gopherjs/js"
12
13	"github.com/go-kivik/kivik"
14	"github.com/go-kivik/kivik/driver"
15	"github.com/go-kivik/kivik/errors"
16	"github.com/go-kivik/pouchdb/bindings"
17)
18
19type db struct {
20	db *bindings.DB
21
22	client *client
23
24	// compacting is set true when compaction begins, and unset when the
25	// callback returns.
26	compacting bool
27}
28
29var _ driver.DB = &db{}
30
31func (d *db) AllDocs(ctx context.Context, options map[string]interface{}) (driver.Rows, error) {
32	result, err := d.db.AllDocs(ctx, options)
33	if err != nil {
34		return nil, err
35	}
36	return &rows{
37		Object: result,
38	}, nil
39}
40
41func (d *db) Query(ctx context.Context, ddoc, view string, options map[string]interface{}) (driver.Rows, error) {
42	result, err := d.db.Query(ctx, ddoc, view, options)
43	if err != nil {
44		return nil, err
45	}
46	return &rows{
47		Object: result,
48	}, nil
49}
50
51func (d *db) Get(ctx context.Context, docID string, options map[string]interface{}) (*driver.Document, error) {
52	doc, rev, err := d.db.Get(ctx, docID, options)
53	if err != nil {
54		return nil, err
55	}
56	return &driver.Document{
57		ContentLength: int64(len(doc)),
58		Rev:           rev,
59		Body:          ioutil.NopCloser(bytes.NewReader(doc)),
60	}, nil
61}
62
63func (d *db) CreateDoc(ctx context.Context, doc interface{}, options map[string]interface{}) (docID, rev string, err error) {
64	jsonDoc, err := json.Marshal(doc)
65	if err != nil {
66		return "", "", err
67	}
68	jsDoc := js.Global.Get("JSON").Call("parse", string(jsonDoc))
69	return d.db.Post(ctx, jsDoc, options)
70}
71
72func (d *db) Put(ctx context.Context, docID string, doc interface{}, options map[string]interface{}) (rev string, err error) {
73	jsonDoc, err := json.Marshal(doc)
74	if err != nil {
75		return "", err
76	}
77	jsDoc := js.Global.Get("JSON").Call("parse", string(jsonDoc))
78	if id := jsDoc.Get("_id"); id != js.Undefined {
79		if id.String() != docID {
80			return "", errors.Status(kivik.StatusBadAPICall, "id argument must match _id field in document")
81		}
82	}
83	jsDoc.Set("_id", docID)
84	return d.db.Put(ctx, jsDoc, options)
85}
86
87func (d *db) Delete(ctx context.Context, docID, rev string, options map[string]interface{}) (newRev string, err error) {
88	return d.db.Delete(ctx, docID, rev, options)
89}
90
91func (d *db) Stats(ctx context.Context) (*driver.DBStats, error) {
92	i, err := d.db.Info(ctx)
93	return &driver.DBStats{
94		Name:           i.Name,
95		CompactRunning: d.compacting,
96		DocCount:       i.DocCount,
97		UpdateSeq:      i.UpdateSeq,
98	}, err
99}
100
101func (d *db) Compact(_ context.Context) error {
102	d.compacting = true
103	go func() {
104		defer func() { d.compacting = false }()
105		if err := d.db.Compact(); err != nil {
106			fmt.Fprintf(os.Stderr, "compaction failed: %s\n", err)
107		}
108	}()
109	return nil
110}
111
112// CompactView  is unimplemented for PouchDB.
113func (d *db) CompactView(_ context.Context, _ string) error {
114	return nil
115}
116
117func (d *db) ViewCleanup(_ context.Context) error {
118	d.compacting = true
119	go func() { // FIXME: #14
120		defer func() { d.compacting = false }()
121		if err := d.db.ViewCleanup(); err != nil {
122			fmt.Fprintf(os.Stderr, "view cleanup failed: %s\n", err)
123		}
124	}()
125	return nil
126}
127
128var securityNotImplemented = errors.Status(kivik.StatusNotImplemented, "kivik: security interface not supported by PouchDB")
129
130func (d *db) Security(ctx context.Context) (*driver.Security, error) {
131	return nil, securityNotImplemented
132}
133
134func (d *db) SetSecurity(_ context.Context, _ *driver.Security) error {
135	return securityNotImplemented
136}
137