aboutsummaryrefslogtreecommitdiff
path: root/headers.go
diff options
context:
space:
mode:
authorStefan Majewsky <majewsky@gmx.net>2018-01-29 21:19:39 +0100
committerStefan Majewsky <majewsky@gmx.net>2018-01-29 21:43:45 +0100
commitcad4a10319b98dd15c0a74d0fea13a2da4a0d3cc (patch)
tree0703764e2d3a94fce3a553720f2a182e57612de2 /headers.go
parent3834e49c90c39f4c95e3b9e7bb52b35204a75625 (diff)
downloadgo-schwift-cad4a10319b98dd15c0a74d0fea13a2da4a0d3cc.tar.gz
lay down the full Account API
Diffstat (limited to 'headers.go')
-rw-r--r--headers.go232
1 files changed, 232 insertions, 0 deletions
diff --git a/headers.go b/headers.go
new file mode 100644
index 0000000..0d9f0e0
--- /dev/null
+++ b/headers.go
@@ -0,0 +1,232 @@
+/******************************************************************************
+*
+* 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 (
+ "fmt"
+ "net/http"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+//AccountHeaders contains the headers for an account. The Raw attribute
+//contains the original set of headers returned from a HEAD or GET request on
+//the account. The other attributes contain the parsed values of common
+//headers, as noted in the tags next to each field. Well-known metadata headers
+//can be accessed in a type-safe way using the methods on this type.
+type AccountHeaders struct {
+ BytesUsed uint64 `schwift:"ro,X-Account-Bytes-Used"`
+ ContainerCount uint64 `schwift:"ro,X-Account-Container-Count"`
+ ObjectCount uint64 `schwift:"ro,X-Account-Object-Count"`
+ Metadata map[string]string `schwift:"rw,X-Account-Meta-,X-Remove-Account-Meta-"`
+ Raw http.Header
+}
+
+//QuotaBytes returns a handle to read or write the X-Account-Meta-Quota-Bytes field.
+func (a AccountHeaders) QuotaBytes() UnsignedIntField {
+ return UnsignedIntField{
+ a.Metadata,
+ "X-Account-Meta-", "Quota-Bytes",
+ false,
+ }
+}
+
+//TempURLKey returns a handle to read or write the X-Account-Meta-Temp-URL-Key field.
+func (a AccountHeaders) TempURLKey() StringField {
+ return StringField{
+ a.Metadata,
+ "X-Account-Meta-", "Temp-URL-Key",
+ false,
+ }
+}
+
+//TempURLKey2 returns a handle to read or write the X-Account-Meta-Temp-URL-Key-2 field.
+func (a AccountHeaders) TempURLKey2() StringField {
+ return StringField{
+ a.Metadata,
+ "X-Account-Meta-", "Temp-URL-Key-2",
+ false,
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// field types
+
+//StringField is a helper type used in the interface of AccountHeaders,
+//ContainerHeaders and ObjectHeaders. For example:
+//
+// var headers AccountHeaders
+// ...
+// value := headers.TempURLKey().Get()
+// headers.TempURLKey().Set(value + " changed")
+// headers.TempURLKey().Clear()
+type StringField struct {
+ metadata map[string]string
+ prefix string
+ key string
+ clearByDeleting bool
+}
+
+//Get returns the value for this key, or the empty string if the key does not exist.
+func (f StringField) Get() string {
+ return f.metadata[f.key]
+}
+
+//Set writes a new value for this key into the original AccountHeaders,
+//ContainerHeaders or ObjectHeaders instance.
+func (f StringField) Set(value string) {
+ f.metadata[f.key] = value
+}
+
+//Clear removes this key from the original AccountHeaders, ContainerHeaders or
+//ObjectHeaders instance.
+func (f StringField) Clear() {
+ if f.clearByDeleting {
+ delete(f.metadata, f.key)
+ } else {
+ f.metadata[f.key] = ""
+ }
+}
+
+//UnsignedIntField is a helper type used in the interface of AccountHeaders,
+//ContainerHeaders and ObjectHeaders. For example:
+//
+// var headers AccountHeaders
+// ...
+// value, err := headers.QuotaBytes().Get()
+// headers.QuotaBytes().Set(value * 2)
+// headers.QuotaBytes().Clear()
+type UnsignedIntField struct {
+ metadata map[string]string
+ prefix string
+ key string
+ clearByDeleting bool
+}
+
+//Get returns the value for this key, or 0 if the key does not exist.
+func (f UnsignedIntField) Get() (uint64, error) {
+ value, err := strconv.ParseUint(f.metadata[f.key], 10, 64)
+ if err != nil {
+ err = MalformedHeaderError{Key: f.prefix + f.key, ParseError: err}
+ }
+ return value, err
+}
+
+//Set writes a new value for this key into the original AccountHeaders,
+//ContainerHeaders or ObjectHeaders instance.
+func (f UnsignedIntField) Set(value uint64) {
+ f.metadata[f.key] = strconv.FormatUint(value, 10)
+}
+
+//Clear removes this key from the original AccountHeaders, ContainerHeaders or
+//ObjectHeaders instance.
+func (f UnsignedIntField) Clear() {
+ if f.clearByDeleting {
+ delete(f.metadata, f.key)
+ } else {
+ f.metadata[f.key] = ""
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// generic parsing functions
+
+func parseHeaders(hdr http.Header, target interface{}) error {
+ return foreachField(target, func(fieldPtr interface{}, info fieldInfo) error {
+ //populate the .Raw field that all input types share
+ if ptr, ok := fieldPtr.(*http.Header); ok {
+ *ptr = hdr
+ return nil
+ }
+
+ //skip over fields without schwift field tag
+ if info.HeaderName == "" {
+ return nil
+ }
+
+ //decode header value into field depending on type
+ switch fieldPtr := fieldPtr.(type) {
+ case *string:
+ *fieldPtr = hdr.Get(info.HeaderName)
+ case *uint64:
+ value, err := strconv.ParseUint(hdr.Get(info.HeaderName), 10, 64)
+ if err != nil {
+ return MalformedHeaderError{info.HeaderName, err}
+ }
+ *fieldPtr = value
+ case *map[string]string:
+ //collect all headers with a prefix equal to `headerName`
+ values := make(map[string]string)
+ for key, value := range hdr {
+ if len(value) > 0 && strings.HasPrefix(key, info.HeaderName) {
+ key = strings.TrimPrefix(key, info.HeaderName)
+ values[key] = value[0]
+ }
+ }
+ *fieldPtr = values
+ default:
+ panic(fmt.Sprintf("parseHeaders: cannot handle field type %T", fieldPtr))
+ }
+
+ return nil
+ })
+}
+
+func compileHeaders(headers interface{}) map[string]string {
+ panic("TODO")
+}
+
+type fieldInfo struct {
+ Access string
+ HeaderName string
+ RemoveHeaderName string
+}
+
+func foreachField(value interface{}, callback func(fieldPtr interface{}, info fieldInfo) error) error {
+ rv := reflect.ValueOf(value)
+ //unpack pointer type if necessary
+ if rv.Type().Kind() == reflect.Ptr {
+ rv = rv.Elem()
+ }
+
+ //iterate over the struct fields
+ for idx := 0; idx < rv.NumField(); idx++ {
+ fieldType := rv.Type().Field(idx)
+ fieldPtr := rv.Field(idx).Addr().Interface()
+
+ //decode schwift:"<access>,<header-name>" tag
+ tagValues := strings.SplitN(fieldType.Tag.Get("schwift"), ",", 3)
+ var fieldInfo fieldInfo
+ if len(tagValues) >= 2 {
+ fieldInfo.Access = tagValues[0]
+ fieldInfo.HeaderName = tagValues[1]
+ if len(tagValues) >= 3 {
+ fieldInfo.RemoveHeaderName = tagValues[2]
+ }
+ }
+
+ err := callback(fieldPtr, fieldInfo)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}