blob: 11fa77d6f0b150b58b4b4bac9e50d8d900e7b1f2 [file] [log] [blame]
Don Newton379ae252019-04-01 12:17:06 -04001package driver
2
3import (
4 "context"
5 "errors"
6 "strings"
7
8 "github.com/mongodb/mongo-go-driver/x/bsonx/bsoncore"
9)
10
11// ListCollectionsBatchCursor is a special batch cursor returned from ListCollections that properly
12// handles current and legacy ListCollections operations.
13type ListCollectionsBatchCursor struct {
14 legacy bool
15 bc *BatchCursor
16 currentBatch []byte
17 err error
18}
19
20// NewListCollectionsBatchCursor creates a new non-legacy ListCollectionsCursor.
21func NewListCollectionsBatchCursor(bc *BatchCursor) (*ListCollectionsBatchCursor, error) {
22 if bc == nil {
23 return nil, errors.New("batch cursor must not be nil")
24 }
25 return &ListCollectionsBatchCursor{bc: bc}, nil
26}
27
28// NewLegacyListCollectionsBatchCursor creates a new legacy ListCollectionsCursor.
29func NewLegacyListCollectionsBatchCursor(bc *BatchCursor) (*ListCollectionsBatchCursor, error) {
30 if bc == nil {
31 return nil, errors.New("batch cursor must not be nil")
32 }
33 return &ListCollectionsBatchCursor{legacy: true, bc: bc}, nil
34}
35
36// ID returns the cursor ID for this batch cursor.
37func (lcbc *ListCollectionsBatchCursor) ID() int64 {
38 return lcbc.bc.ID()
39}
40
41// Next indicates if there is another batch available. Returning false does not necessarily indicate
42// that the cursor is closed. This method will return false when an empty batch is returned.
43//
44// If Next returns true, there is a valid batch of documents available. If Next returns false, there
45// is not a valid batch of documents available.
46func (lcbc *ListCollectionsBatchCursor) Next(ctx context.Context) bool {
47 if !lcbc.bc.Next(ctx) {
48 return false
49 }
50
51 if !lcbc.legacy {
52 lcbc.currentBatch = lcbc.bc.currentBatch
53 return true
54 }
55
56 batch := lcbc.bc.currentBatch
57 lcbc.currentBatch = lcbc.currentBatch[:0]
58 var doc bsoncore.Document
59 var ok bool
60 for {
61 doc, batch, ok = bsoncore.ReadDocument(batch)
62 if !ok {
63 break
64 }
65
66 doc, lcbc.err = lcbc.projectNameElement(doc)
67 if lcbc.err != nil {
68 return false
69 }
70 lcbc.currentBatch = append(lcbc.currentBatch, doc...)
71 }
72
73 return true
74}
75
76// Batch will append the current batch of documents to dst. RequiredBytes can be called to determine
77// the length of the current batch of documents.
78//
79// If there is no batch available, this method does nothing.
80func (lcbc *ListCollectionsBatchCursor) Batch(dst []byte) []byte {
81 return append(dst, lcbc.currentBatch...)
82}
83
84// RequiredBytes returns the number of bytes required for the current batch.
85func (lcbc *ListCollectionsBatchCursor) RequiredBytes() int { return len(lcbc.currentBatch) }
86
87// Err returns the latest error encountered.
88func (lcbc *ListCollectionsBatchCursor) Err() error {
89 if lcbc.err != nil {
90 return lcbc.err
91 }
92 return lcbc.bc.Err()
93}
94
95// Close closes this batch cursor.
96func (lcbc *ListCollectionsBatchCursor) Close(ctx context.Context) error { return lcbc.bc.Close(ctx) }
97
98// project out the database name for a legacy server
99func (*ListCollectionsBatchCursor) projectNameElement(rawDoc bsoncore.Document) (bsoncore.Document, error) {
100 elems, err := rawDoc.Elements()
101 if err != nil {
102 return nil, err
103 }
104
105 var filteredElems []byte
106 for _, elem := range elems {
107 key := elem.Key()
108 if key != "name" {
109 filteredElems = append(filteredElems, elem...)
110 continue
111 }
112
113 name := elem.Value().StringValue()
114 collName := name[strings.Index(name, ".")+1:]
115 filteredElems = bsoncore.AppendStringElement(filteredElems, "name", collName)
116 }
117
118 var filteredDoc []byte
119 filteredDoc = bsoncore.BuildDocument(filteredDoc, filteredElems)
120 return filteredDoc, nil
121}