diff options
| -rw-r--r-- | field_metadata.go | 4 | ||||
| -rw-r--r-- | field_string.go | 6 | ||||
| -rw-r--r-- | field_test.go | 12 | ||||
| -rw-r--r-- | field_time.go | 121 | ||||
| -rw-r--r-- | field_uint64.go | 28 | ||||
| -rw-r--r-- | generated.go | 149 | ||||
| -rw-r--r-- | generated.go.in | 19 |
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" } + ] } } --- |
