diff options
| author | Stefan Majewsky <majewsky@gmx.net> | 2018-02-16 16:41:18 +0100 |
|---|---|---|
| committer | Stefan Majewsky <majewsky@gmx.net> | 2018-02-16 16:41:18 +0100 |
| commit | 6be5f143bf7abd39129145d310980c8ab3c47203 (patch) | |
| tree | 88c76325cfcf045bf4a48231d0e419c9b90b8f42 | |
| parent | 403359114bf971f037e2737b43e2734a89df9f0a (diff) | |
| download | go-schwift-6be5f143bf7abd39129145d310980c8ab3c47203.tar.gz | |
prepare ContainerIterator for sharing code with ObjectIterator
| -rw-r--r-- | container_iterator.go | 119 | ||||
| -rw-r--r-- | iterator.go | 136 |
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 == "" +} |
