aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Majewsky <majewsky@gmx.net>2018-02-16 16:41:18 +0100
committerStefan Majewsky <majewsky@gmx.net>2018-02-16 16:41:18 +0100
commit6be5f143bf7abd39129145d310980c8ab3c47203 (patch)
tree88c76325cfcf045bf4a48231d0e419c9b90b8f42
parent403359114bf971f037e2737b43e2734a89df9f0a (diff)
downloadgo-schwift-6be5f143bf7abd39129145d310980c8ab3c47203.tar.gz
prepare ContainerIterator for sharing code with ObjectIterator
-rw-r--r--container_iterator.go119
-rw-r--r--iterator.go136
2 files changed, 163 insertions, 92 deletions
diff --git a/container_iterator.go b/container_iterator.go
index 71c17be..8c04be6 100644
--- a/container_iterator.go
+++ b/container_iterator.go
@@ -19,13 +19,21 @@
package schwift
import (
- "encoding/json"
"fmt"
- "strconv"
- "strings"
"time"
)
+//ContainerInfo is a result type returned by ContainerIterator for detailed
+//container listings. The metadata in this type is a subset of Container.Headers(),
+//but since it is returned as part of the detailed container listing, it can be
+//obtained without making additional HEAD requests on the container(s).
+type ContainerInfo struct {
+ Container *Container
+ ObjectCount uint64
+ BytesUsed uint64
+ LastModified time.Time
+}
+
//ContainerIterator iterates over the accounts in a container. It is typically
//constructed with the Account.Containers() method. For example:
//
@@ -61,55 +69,14 @@ type ContainerIterator struct {
//Options may contain additional query parameters for the GET request.
Options *RequestOptions
- marker string
- eof bool
+ base *iteratorBase
}
-//ContainerInfo is a result type returned by ContainerIterator for detailed
-//container listings. The metadata in this type is a subset of Container.Headers(),
-//but since it is returned as part of the detailed container listing, it can be
-//obtained without making additional HEAD requests on the container(s).
-type ContainerInfo struct {
- Container *Container
- ObjectCount uint64
- BytesUsed uint64
- LastModified time.Time
-}
-
-func (i ContainerIterator) request(limit int, detailed bool) Request {
- r := Request{
- Method: "GET",
- Headers: headersToHTTP(i.Headers),
- Options: cloneRequestOptions(i.Options),
+func (i *ContainerIterator) getBase() *iteratorBase {
+ if i.base == nil {
+ i.base = &iteratorBase{i: i}
}
-
- if i.Prefix != "" {
- r.Options.Values.Set("prefix", i.Prefix)
- }
-
- if i.marker == "" {
- r.Options.Values.Del("marker")
- } else {
- r.Options.Values.Set("marker", i.marker)
- }
-
- if limit < 0 {
- r.Options.Values.Del("limit")
- } else {
- r.Options.Values.Set("limit", strconv.FormatUint(uint64(limit), 10))
- }
-
- if detailed {
- r.Headers.Set("Accept", "application/json")
- r.Options.Values.Set("format", "json")
- r.ExpectStatusCodes = []int{200}
- } else {
- r.Headers.Set("Accept", "text/plain")
- r.Options.Values.Set("format", "plain")
- r.ExpectStatusCodes = []int{200, 204}
- }
-
- return r
+ return i.base
}
//NextPage queries Swift for the next page of container names. If limit is
@@ -122,47 +89,21 @@ func (i ContainerIterator) request(limit int, detailed bool) Request {
//This method offers maximal flexibility, but most users will prefer the
//simpler interfaces offered by Collect() and Foreach().
func (i *ContainerIterator) NextPage(limit int) ([]*Container, error) {
- if i.eof {
- return nil, nil
- }
- resp, err := i.request(limit, false).Do(i.Account.client)
- if err != nil {
- return nil, err
- }
-
- buf, err := collectResponseBody(resp)
+ names, err := i.getBase().nextPage(limit)
if err != nil {
return nil, err
}
- bufStr := strings.TrimSuffix(string(buf), "\n")
- var result []*Container
- if bufStr != "" {
- names := strings.Split(bufStr, "\n")
- result = make([]*Container, len(names))
- for idx, name := range names {
- result[idx] = i.Account.Container(name)
- }
- }
- if len(result) == 0 {
- i.eof = true
- i.marker = ""
- } else {
- i.eof = false
- i.marker = result[len(result)-1].Name()
+ result := make([]*Container, len(names))
+ for idx, name := range names {
+ result[idx] = i.Account.Container(name)
}
return result, nil
}
//NextPageDetailed is like NextPage, but includes basic metadata.
func (i *ContainerIterator) NextPageDetailed(limit int) ([]ContainerInfo, error) {
- if i.eof {
- return nil, nil
- }
- resp, err := i.request(limit, true).Do(i.Account.client)
- if err != nil {
- return nil, err
- }
+ b := i.getBase()
var document []struct {
BytesUsed uint64 `json:"bytes"`
@@ -170,14 +111,14 @@ func (i *ContainerIterator) NextPageDetailed(limit int) ([]ContainerInfo, error)
LastModifiedStr string `json:"last_modified"`
Name string `json:"name"`
}
- err = json.NewDecoder(resp.Body).Decode(&document)
- closeErr := resp.Body.Close()
- if err == nil {
- err = closeErr
- }
+ err := b.nextPageDetailed(limit, &document)
if err != nil {
return nil, err
}
+ if len(document) == 0 {
+ b.setMarker("") //indicate EOF to iteratorBase
+ return nil, nil
+ }
result := make([]ContainerInfo, len(document))
for idx, data := range document {
@@ -191,13 +132,7 @@ func (i *ContainerIterator) NextPageDetailed(limit int) ([]ContainerInfo, error)
}
}
- if len(result) == 0 {
- i.eof = true
- i.marker = ""
- } else {
- i.eof = false
- i.marker = result[len(result)-1].Container.Name()
- }
+ b.setMarker(result[len(result)-1].Container.Name())
return result, nil
}
diff --git a/iterator.go b/iterator.go
new file mode 100644
index 0000000..af01bbe
--- /dev/null
+++ b/iterator.go
@@ -0,0 +1,136 @@
+/******************************************************************************
+*
+* Copyright 2018 Stefan Majewsky <majewsky@gmx.net>
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+******************************************************************************/
+
+package schwift
+
+import (
+ "encoding/json"
+ "strconv"
+ "strings"
+)
+
+//iteratorInterface allows iteratorBase to access public attributes of
+//ContainerIterator/ObjectIterator.
+type iteratorInterface interface {
+ getAccount() *Account
+ getContainerName() string
+ getPrefix() string
+ getHeaders() map[string]string
+ getOptions() *RequestOptions
+}
+
+func (i ContainerIterator) getAccount() *Account { return i.Account }
+func (i ContainerIterator) getContainerName() string { return "" }
+func (i ContainerIterator) getPrefix() string { return i.Prefix }
+func (i ContainerIterator) getHeaders() map[string]string { return i.Headers }
+func (i ContainerIterator) getOptions() *RequestOptions { return i.Options }
+
+//iteratorBase provides shared behavior for ContainerIterator and ObjectIterator.
+type iteratorBase struct {
+ i iteratorInterface
+ marker string
+ eof bool
+}
+
+func (b *iteratorBase) request(limit int, detailed bool) Request {
+ r := Request{
+ Method: "GET",
+ ContainerName: b.i.getContainerName(),
+ Headers: headersToHTTP(b.i.getHeaders()),
+ Options: cloneRequestOptions(b.i.getOptions()),
+ }
+
+ if prefix := b.i.getPrefix(); prefix != "" {
+ r.Options.Values.Set("prefix", prefix)
+ }
+
+ if b.marker == "" {
+ r.Options.Values.Del("marker")
+ } else {
+ r.Options.Values.Set("marker", b.marker)
+ }
+
+ if limit < 0 {
+ r.Options.Values.Del("limit")
+ } else {
+ r.Options.Values.Set("limit", strconv.FormatUint(uint64(limit), 10))
+ }
+
+ if detailed {
+ r.Headers.Set("Accept", "application/json")
+ r.Options.Values.Set("format", "json")
+ r.ExpectStatusCodes = []int{200}
+ } else {
+ r.Headers.Set("Accept", "text/plain")
+ r.Options.Values.Set("format", "plain")
+ r.ExpectStatusCodes = []int{200, 204}
+ }
+
+ return r
+}
+
+func (b *iteratorBase) nextPage(limit int) ([]string, error) {
+ if b.eof {
+ return nil, nil
+ }
+ resp, err := b.request(limit, false).Do(b.i.getAccount().client)
+ if err != nil {
+ return nil, err
+ }
+
+ buf, err := collectResponseBody(resp)
+ if err != nil {
+ return nil, err
+ }
+ bufStr := strings.TrimSuffix(string(buf), "\n")
+ var result []string
+ if bufStr != "" {
+ result = strings.Split(bufStr, "\n")
+ }
+
+ if len(result) == 0 {
+ b.eof = true
+ b.marker = ""
+ } else {
+ b.eof = false
+ b.marker = result[len(result)-1]
+ }
+ return result, nil
+}
+
+func (b *iteratorBase) nextPageDetailed(limit int, data interface{}) error {
+ if b.eof {
+ return nil
+ }
+ resp, err := b.request(limit, true).Do(b.i.getAccount().client)
+ if err != nil {
+ return err
+ }
+
+ err = json.NewDecoder(resp.Body).Decode(&data)
+ closeErr := resp.Body.Close()
+ if err == nil {
+ err = closeErr
+ }
+ return err
+}
+
+func (b *iteratorBase) setMarker(marker string) {
+ b.marker = marker
+ b.eof = marker == ""
+}