aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--field_metadata.go4
-rw-r--r--field_string.go6
-rw-r--r--field_test.go12
-rw-r--r--field_time.go121
-rw-r--r--field_uint64.go28
-rw-r--r--generated.go149
-rw-r--r--generated.go.in19
7 files changed, 287 insertions, 52 deletions
diff --git a/field_metadata.go b/field_metadata.go
index cebd2af..05dee32 100644
--- a/field_metadata.go
+++ b/field_metadata.go
@@ -19,8 +19,8 @@
package schwift
//FieldMetadata is a helper type that provides safe access to the metadata headers
-//in a SomethingHeaders instance. It cannot be directly constructed, but each
-//SomethingHeaders type has a method "Metadata" returning this type. For example:
+//in a headers instance. It cannot be directly constructed, but each headers
+//type has a method "Metadata" returning this type. For example:
//
// hdr := make(ObjectHeaders)
// //the following two statements are equivalent
diff --git a/field_string.go b/field_string.go
index d44d517..f12d2d5 100644
--- a/field_string.go
+++ b/field_string.go
@@ -41,19 +41,19 @@ func (f FieldString) Get() string {
return f.h.Get(f.k)
}
-//Set writes a new value for this header into the corresponding schwift.Headers
+//Set writes a new value for this header into the corresponding headers
//instance.
func (f FieldString) Set(value string) {
f.h.Set(f.k, value)
}
-//Del removes this key from the original schwift.Headers instance, so that the
+//Del removes this key from the original headers instance, so that the
//key will remain unchanged on the server during Update().
func (f FieldString) Del() {
f.h.Del(f.k)
}
-//Clear sets this key to an empty string in the original schwift.Headers
+//Clear sets this key to an empty string in the original headers
//instance, so that the key will be removed on the server during Update().
func (f FieldString) Clear() {
f.h.Clear(f.k)
diff --git a/field_test.go b/field_test.go
index bea85ca..c931108 100644
--- a/field_test.go
+++ b/field_test.go
@@ -64,21 +64,21 @@ func TestFieldTimestamp(t *testing.T) {
return
}
- expectBool(t, hdr.Timestamp().Exists(), true)
+ expectBool(t, hdr.CreatedAt().Exists(), true)
- actual := float64(hdr.Timestamp().Get().UnixNano()) / 1e9
+ actual := float64(hdr.CreatedAt().Get().UnixNano()) / 1e9
expected, _ := strconv.ParseFloat(hdr["X-Timestamp"], 64)
expectFloat64(t, actual, expected)
})
hdr := make(AccountHeaders)
- expectBool(t, hdr.Timestamp().Exists(), false)
- expectBool(t, hdr.Timestamp().Get().IsZero(), true)
+ expectBool(t, hdr.CreatedAt().Exists(), false)
+ expectBool(t, hdr.CreatedAt().Get().IsZero(), true)
expectError(t, hdr.Validate(), "")
hdr["X-Timestamp"] = "wtf"
- expectBool(t, hdr.Timestamp().Exists(), true)
- expectBool(t, hdr.Timestamp().Get().IsZero(), true)
+ expectBool(t, hdr.CreatedAt().Exists(), true)
+ expectBool(t, hdr.CreatedAt().Get().IsZero(), true)
expectError(t, hdr.Validate(), `Bad header X-Timestamp: strconv.ParseFloat: parsing "wtf": invalid syntax`)
}
diff --git a/field_time.go b/field_time.go
index 25a2c1e..2180d56 100644
--- a/field_time.go
+++ b/field_time.go
@@ -19,40 +19,95 @@
package schwift
import (
+ "fmt"
"strconv"
"time"
)
-//FieldUnixTimeReadonly is a helper type that provides type-safe access to a
-//Swift header whose value is a UNIX timestamp. It cannot be directly
-//constructed, but methods on the Headers types return this type. For example:
+//FieldHTTPTimeReadonly is a helper type that provides type-safe access to a
+//readonly Swift header whose value is a HTTP timestamp like this:
+//
+// Mon, 02 Jan 2006 15:04:05 GMT
+//
+//It cannot be directly constructed, but methods on the Headers types return
+//this type. For example:
+//
+// //suppose you have:
+// hdr, err := obj.Headers()
+//
+// //you could do this:
+// time, err := time.Parse(time.RFC1123, hdr.Get("Last-Modified"))
+//
+// //or you can just:
+// time := hdr.UpdatedAt().Get()
+//
+//Don't worry about the missing `err` in the last line. When the header fails
+//to parse, Object.Headers() already returns the corresponding
+//MalformedHeaderError.
+type FieldHTTPTimeReadonly struct {
+ h headerInterface
+ k string
+}
+
+//Exists checks whether there is a value for this header.
+func (f FieldHTTPTimeReadonly) Exists() bool {
+ return f.h.Get(f.k) != ""
+}
+
+//Get returns the value for this header, or the zero value if there is no value
+//(or if it is not a valid timestamp).
+func (f FieldHTTPTimeReadonly) Get() time.Time {
+ v, err := strconv.ParseFloat(f.h.Get(f.k), 64)
+ if err != nil {
+ return time.Time{}
+ }
+ return time.Unix(0, int64(1e9*v))
+}
+
+func (f FieldHTTPTimeReadonly) validate() error {
+ val := f.h.Get(f.k)
+ if val == "" {
+ return nil
+ }
+ _, err := strconv.ParseFloat(val, 64)
+ if err == nil {
+ return nil
+ }
+ return MalformedHeaderError{f.k, err}
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+//FieldUnixTime is a helper type that provides type-safe access to a Swift
+//header whose value is a UNIX timestamp. It cannot be directly constructed,
+//but methods on the Headers types return this type. For example:
//
// //suppose you have:
// hdr, err := obj.Headers()
//
// //you could do all this:
-// sec, err := strconv.ParseFloat(hdr.Get("X-Timestamp"), 64)
-// time := time.Unix(int64(sec), int64(1e9 * (sec - math.Floor(sec))))
+// sec, err := strconv.ParseFloat(hdr.Get("X-Delete-At"), 64)
+// time := time.Unix(0, int64(1e9 * sec))
//
// //or you can just:
-// time := hdr.Timestamp().Get()
+// time := hdr.ExpiresAt().Get()
//
-//Don't worry about the missing `err` in the last line. When the X-Timestamp
-//header fails to parse, Object.Headers() already returns the corresponding
+//Don't worry about the missing `err` in the last line. When the header fails
+//to parse, Object.Headers() already returns the corresponding
//MalformedHeaderError.
-type FieldUnixTimeReadonly struct {
+type FieldUnixTime struct {
h headerInterface
k string
}
//Exists checks whether there is a value for this header.
-func (f FieldUnixTimeReadonly) Exists() bool {
+func (f FieldUnixTime) Exists() bool {
return f.h.Get(f.k) != ""
}
//Get returns the value for this header, or the zero value if there is no value
//(or if it is not a valid timestamp).
-func (f FieldUnixTimeReadonly) Get() time.Time {
+func (f FieldUnixTime) Get() time.Time {
v, err := strconv.ParseFloat(f.h.Get(f.k), 64)
if err != nil {
return time.Time{}
@@ -60,7 +115,25 @@ func (f FieldUnixTimeReadonly) Get() time.Time {
return time.Unix(0, int64(1e9*v))
}
-func (f FieldUnixTimeReadonly) validate() error {
+//Set writes a new value for this header into the corresponding headers
+//instance.
+func (f FieldUnixTime) Set(value time.Time) {
+ f.h.Set(f.k, fmt.Sprintf("%.9f", float64(value.UnixNano())/1e9))
+}
+
+//Del removes this key from the original headers instance, so that the key will
+//remain unchanged on the server during Update().
+func (f FieldUnixTime) Del() {
+ f.h.Del(f.k)
+}
+
+//Clear sets this key to an empty string in the original headers instance, so
+//that the key will be removed on the server during Update().
+func (f FieldUnixTime) Clear() {
+ f.h.Clear(f.k)
+}
+
+func (f FieldUnixTime) validate() error {
val := f.h.Get(f.k)
if val == "" {
return nil
@@ -71,3 +144,27 @@ func (f FieldUnixTimeReadonly) validate() error {
}
return MalformedHeaderError{f.k, err}
}
+
+////////////////////////////////////////////////////////////////////////////////
+
+//FieldUnixTimeReadonly is a readonly variant of FieldUnixTime. It is used for
+//fields that cannot be set by the client.
+type FieldUnixTimeReadonly struct {
+ h headerInterface
+ k string
+}
+
+//Exists checks whether there is a value for this header.
+func (f FieldUnixTimeReadonly) Exists() bool {
+ return f.h.Get(f.k) != ""
+}
+
+//Get returns the value for this header, or the zero value if there is no value
+//(or if it is not a valid timestamp).
+func (f FieldUnixTimeReadonly) Get() time.Time {
+ return FieldUnixTime{f.h, f.k}.Get()
+}
+
+func (f FieldUnixTimeReadonly) validate() error {
+ return FieldUnixTime{f.h, f.k}.validate()
+}
diff --git a/field_uint64.go b/field_uint64.go
index 4655478..a14f558 100644
--- a/field_uint64.go
+++ b/field_uint64.go
@@ -50,26 +50,24 @@ func (f FieldUint64) Get() uint64 {
return v
}
-//Set writes a new value for this header into the corresponding schwift.Headers
+//Set writes a new value for this header into the corresponding headers
//instance.
func (f FieldUint64) Set(value uint64) {
f.h.Set(f.k, strconv.FormatUint(value, 10))
}
-//Del removes this key from the original schwift.Headers instance, so that the
-//key will remain unchanged on the server during Update().
+//Del removes this key from the original headers instance, so that the key will
+//remain unchanged on the server during Update().
func (f FieldUint64) Del() {
f.h.Del(f.k)
}
-//Clear sets this key to an empty string in the original schwift.Headers
-//instance, so that the key will be removed on the server during Update().
+//Clear sets this key to an empty string in the original headers instance, so
+//that the key will be removed on the server during Update().
func (f FieldUint64) Clear() {
f.h.Clear(f.k)
}
-//validate is only used internally, but needs to be exported to cross package
-//boundaries.
func (f FieldUint64) validate() error {
val := f.h.Get(f.k)
if val == "" {
@@ -99,21 +97,9 @@ func (f FieldUint64Readonly) Exists() bool {
//Get returns the value for this header, or 0 if there is no value (or if it is
//not a valid uint64).
func (f FieldUint64Readonly) Get() uint64 {
- v, err := strconv.ParseUint(f.h.Get(f.k), 10, 64)
- if err != nil {
- return 0
- }
- return v
+ return FieldUint64{f.h, f.k}.Get()
}
func (f FieldUint64Readonly) validate() error {
- val := f.h.Get(f.k)
- if val == "" {
- return nil
- }
- _, err := strconv.ParseUint(val, 10, 64)
- if err == nil {
- return nil
- }
- return MalformedHeaderError{f.k, err}
+ return FieldUint64{f.h, f.k}.validate()
}
diff --git a/generated.go b/generated.go
index 31233ea..a50c418 100644
--- a/generated.go
+++ b/generated.go
@@ -73,7 +73,7 @@ func (h AccountHeaders) Validate() error {
if err := h.ObjectCount().validate(); err != nil {
return err
}
- if err := h.Timestamp().validate(); err != nil {
+ if err := h.CreatedAt().validate(); err != nil {
return err
}
return evadeGolintComplaint1()
@@ -114,8 +114,8 @@ func (h AccountHeaders) ObjectCount() FieldUint64Readonly {
return FieldUint64Readonly{h, "X-Account-Object-Count"}
}
-//Timestamp provides type-safe access to X-Timestamp headers.
-func (h AccountHeaders) Timestamp() FieldUnixTimeReadonly {
+//CreatedAt provides type-safe access to X-Timestamp headers.
+func (h AccountHeaders) CreatedAt() FieldUnixTimeReadonly {
return FieldUnixTimeReadonly{h, "X-Timestamp"}
}
@@ -200,7 +200,7 @@ func (h ContainerHeaders) Validate() error {
if err := h.StoragePolicy().validate(); err != nil {
return err
}
- if err := h.Timestamp().validate(); err != nil {
+ if err := h.CreatedAt().validate(); err != nil {
return err
}
if err := h.VersionsLocation().validate(); err != nil {
@@ -274,8 +274,8 @@ func (h ContainerHeaders) StoragePolicy() FieldString {
return FieldString{h, "X-Storage-Policy"}
}
-//Timestamp provides type-safe access to X-Timestamp headers.
-func (h ContainerHeaders) Timestamp() FieldUnixTimeReadonly {
+//CreatedAt provides type-safe access to X-Timestamp headers.
+func (h ContainerHeaders) CreatedAt() FieldUnixTimeReadonly {
return FieldUnixTimeReadonly{h, "X-Timestamp"}
}
@@ -284,6 +284,143 @@ func (h ContainerHeaders) VersionsLocation() FieldString {
return FieldString{h, "X-Versions-Location"}
}
+//ObjectHeaders contains the headers for a schwift.Object instance.
+//
+//To read and write well-known headers, use the methods on this type.
+//To read and write arbitary headers, use the http.Header-like methods Get(),
+//Set(), Clear(), Del().
+type ObjectHeaders map[string]string
+
+//Clear sets the value for the specified header to the empty string. When the
+//Headers instance is then sent to the server with Update(), the server will
+//delete the value for that header; cf. Del().
+func (h ObjectHeaders) Clear(key string) {
+ h[textproto.CanonicalMIMEHeaderKey(key)] = ""
+}
+
+//Del deletes a key from the Headers instance. When the Headers instance
+//is then sent to the server with Update(), Del() has different effects
+//depending on context because of Swift's inconsistent API:
+//
+//For most writable attributes, a key which has been deleted with Del() will
+//remain unchanged on the server. To remove the key on the server, use Clear()
+//instead.
+//
+//For object metadata (but not other object attributes), deleting a key will
+//cause that key to be deleted on the server. Del() is identical to Clear() in
+//this case.
+func (h ObjectHeaders) Del(key string) {
+ delete(h, textproto.CanonicalMIMEHeaderKey(key))
+}
+
+//Get returns the value for the specified header.
+func (h ObjectHeaders) Get(key string) string {
+ return h[textproto.CanonicalMIMEHeaderKey(key)]
+}
+
+//Set sets a new value for the specified header. Any existing value will be
+//overwritten.
+func (h ObjectHeaders) Set(key, value string) {
+ h[textproto.CanonicalMIMEHeaderKey(key)] = value
+}
+
+//Validate returns MalformedHeaderError if the value of any well-known header
+//does not conform to its data type. This is called automatically by Schwift
+//when preparing an ObjectHeaders instance from a GET/HEAD response, so you
+//usually do not need to do it yourself. You will get the validation error from
+//the Object method doing the request, e.g. Headers().
+func (h ObjectHeaders) Validate() error {
+ if err := h.ContentDisposition().validate(); err != nil {
+ return err
+ }
+ if err := h.ContentEncoding().validate(); err != nil {
+ return err
+ }
+ if err := h.SizeBytes().validate(); err != nil {
+ return err
+ }
+ if err := h.ContentType().validate(); err != nil {
+ return err
+ }
+ if err := h.Etag().validate(); err != nil {
+ return err
+ }
+ if err := h.UpdatedAt().validate(); err != nil {
+ return err
+ }
+ if err := h.ExpiresAt().validate(); err != nil {
+ return err
+ }
+ if err := h.Metadata().validate(); err != nil {
+ return err
+ }
+ if err := h.SymlinkTargetAccount().validate(); err != nil {
+ return err
+ }
+ if err := h.SymlinkTarget().validate(); err != nil {
+ return err
+ }
+ if err := h.CreatedAt().validate(); err != nil {
+ return err
+ }
+ return evadeGolintComplaint1()
+}
+
+//ContentDisposition provides type-safe access to Content-Disposition headers.
+func (h ObjectHeaders) ContentDisposition() FieldString {
+ return FieldString{h, "Content-Disposition"}
+}
+
+//ContentEncoding provides type-safe access to Content-Encoding headers.
+func (h ObjectHeaders) ContentEncoding() FieldString {
+ return FieldString{h, "Content-Encoding"}
+}
+
+//SizeBytes provides type-safe access to Content-Length headers.
+func (h ObjectHeaders) SizeBytes() FieldUint64 {
+ return FieldUint64{h, "Content-Length"}
+}
+
+//ContentType provides type-safe access to Content-Type headers.
+func (h ObjectHeaders) ContentType() FieldString {
+ return FieldString{h, "Content-Type"}
+}
+
+//Etag provides type-safe access to Etag headers.
+func (h ObjectHeaders) Etag() FieldString {
+ return FieldString{h, "Etag"}
+}
+
+//UpdatedAt provides type-safe access to Last-Modified headers.
+func (h ObjectHeaders) UpdatedAt() FieldHTTPTimeReadonly {
+ return FieldHTTPTimeReadonly{h, "Last-Modified"}
+}
+
+//ExpiresAt provides type-safe access to X-Delete-At headers.
+func (h ObjectHeaders) ExpiresAt() FieldUnixTime {
+ return FieldUnixTime{h, "X-Delete-At"}
+}
+
+//Metadata provides type-safe access to X-Object-Meta- headers.
+func (h ObjectHeaders) Metadata() FieldMetadata {
+ return FieldMetadata{h, "X-Object-Meta-"}
+}
+
+//SymlinkTargetAccount provides type-safe access to X-Symlink-Target-Account headers.
+func (h ObjectHeaders) SymlinkTargetAccount() FieldString {
+ return FieldString{h, "X-Symlink-Target-Account"}
+}
+
+//SymlinkTarget provides type-safe access to X-Symlink-Target headers.
+func (h ObjectHeaders) SymlinkTarget() FieldString {
+ return FieldString{h, "X-Symlink-Target"}
+}
+
+//CreatedAt provides type-safe access to X-Timestamp headers.
+func (h ObjectHeaders) CreatedAt() FieldUnixTimeReadonly {
+ return FieldUnixTimeReadonly{h, "X-Timestamp"}
+}
+
func evadeGolintComplaint1() error {
return nil
}
diff --git a/generated.go.in b/generated.go.in
index 680b551..4a831d2 100644
--- a/generated.go.in
+++ b/generated.go.in
@@ -8,7 +8,7 @@
{ "Header": "X-Account-Meta-Temp-URL-Key-2", "Attribute": "TempURLKey2", "Type": "String" },
{ "Header": "X-Account-Meta-Temp-URL-Key", "Attribute": "TempURLKey", "Type": "String" },
{ "Header": "X-Account-Object-Count", "Attribute": "ObjectCount", "Type": "Uint64Readonly" },
- { "Header": "X-Timestamp", "Attribute": "Timestamp", "Type": "UnixTimeReadonly" }
+ { "Header": "X-Timestamp", "Attribute": "CreatedAt", "Type": "UnixTimeReadonly" }
]
},
"Container": {
@@ -26,9 +26,24 @@
{ "Header": "X-Container-Write", "Attribute": "WriteACL", "Type": "String" },
{ "Header": "X-History-Location", "Attribute": "HistoryLocation", "Type": "String" },
{ "Header": "X-Storage-Policy", "Attribute": "StoragePolicy", "Type": "String" },
- { "Header": "X-Timestamp", "Attribute": "Timestamp", "Type": "UnixTimeReadonly" },
+ { "Header": "X-Timestamp", "Attribute": "CreatedAt", "Type": "UnixTimeReadonly" },
{ "Header": "X-Versions-Location", "Attribute": "VersionsLocation", "Type": "String" }
]
+ },
+ "Object": {
+ "Fields": [
+ { "Header": "Content-Disposition", "Attribute": "ContentDisposition", "Type": "String" },
+ { "Header": "Content-Encoding", "Attribute": "ContentEncoding", "Type": "String" },
+ { "Header": "Content-Length", "Attribute": "SizeBytes", "Type": "Uint64" },
+ { "Header": "Content-Type", "Attribute": "ContentType", "Type": "String" },
+ { "Header": "Etag", "Attribute": "Etag", "Type": "String" },
+ { "Header": "Last-Modified", "Attribute": "UpdatedAt", "Type": "HTTPTimeReadonly" },
+ { "Header": "X-Delete-At", "Attribute": "ExpiresAt", "Type": "UnixTime" },
+ { "Header": "X-Object-Meta-", "Attribute": "Metadata", "Type": "Metadata" },
+ { "Header": "X-Symlink-Target-Account", "Attribute": "SymlinkTargetAccount", "Type": "String" },
+ { "Header": "X-Symlink-Target", "Attribute": "SymlinkTarget", "Type": "String" },
+ { "Header": "X-Timestamp", "Attribute": "CreatedAt", "Type": "UnixTimeReadonly" }
+ ]
}
}
---