175 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
| // Metadata manipulation in and out of Headers
 | |
| 
 | |
| package swift
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| // Metadata stores account, container or object metadata.
 | |
| type Metadata map[string]string
 | |
| 
 | |
| // Metadata gets the Metadata starting with the metaPrefix out of the Headers.
 | |
| //
 | |
| // The keys in the Metadata will be converted to lower case
 | |
| func (h Headers) Metadata(metaPrefix string) Metadata {
 | |
| 	m := Metadata{}
 | |
| 	metaPrefix = http.CanonicalHeaderKey(metaPrefix)
 | |
| 	for key, value := range h {
 | |
| 		if strings.HasPrefix(key, metaPrefix) {
 | |
| 			metaKey := strings.ToLower(key[len(metaPrefix):])
 | |
| 			m[metaKey] = value
 | |
| 		}
 | |
| 	}
 | |
| 	return m
 | |
| }
 | |
| 
 | |
| // AccountMetadata converts Headers from account to a Metadata.
 | |
| //
 | |
| // The keys in the Metadata will be converted to lower case.
 | |
| func (h Headers) AccountMetadata() Metadata {
 | |
| 	return h.Metadata("X-Account-Meta-")
 | |
| }
 | |
| 
 | |
| // ContainerMetadata converts Headers from container to a Metadata.
 | |
| //
 | |
| // The keys in the Metadata will be converted to lower case.
 | |
| func (h Headers) ContainerMetadata() Metadata {
 | |
| 	return h.Metadata("X-Container-Meta-")
 | |
| }
 | |
| 
 | |
| // ObjectMetadata converts Headers from object to a Metadata.
 | |
| //
 | |
| // The keys in the Metadata will be converted to lower case.
 | |
| func (h Headers) ObjectMetadata() Metadata {
 | |
| 	return h.Metadata("X-Object-Meta-")
 | |
| }
 | |
| 
 | |
| // Headers convert the Metadata starting with the metaPrefix into a
 | |
| // Headers.
 | |
| //
 | |
| // The keys in the Metadata will be converted from lower case to http
 | |
| // Canonical (see http.CanonicalHeaderKey).
 | |
| func (m Metadata) Headers(metaPrefix string) Headers {
 | |
| 	h := Headers{}
 | |
| 	for key, value := range m {
 | |
| 		key = http.CanonicalHeaderKey(metaPrefix + key)
 | |
| 		h[key] = value
 | |
| 	}
 | |
| 	return h
 | |
| }
 | |
| 
 | |
| // AccountHeaders converts the Metadata for the account.
 | |
| func (m Metadata) AccountHeaders() Headers {
 | |
| 	return m.Headers("X-Account-Meta-")
 | |
| }
 | |
| 
 | |
| // ContainerHeaders converts the Metadata for the container.
 | |
| func (m Metadata) ContainerHeaders() Headers {
 | |
| 	return m.Headers("X-Container-Meta-")
 | |
| }
 | |
| 
 | |
| // ObjectHeaders converts the Metadata for the object.
 | |
| func (m Metadata) ObjectHeaders() Headers {
 | |
| 	return m.Headers("X-Object-Meta-")
 | |
| }
 | |
| 
 | |
| // Turns a number of ns into a floating point string in seconds
 | |
| //
 | |
| // Trims trailing zeros and guaranteed to be perfectly accurate
 | |
| func nsToFloatString(ns int64) string {
 | |
| 	if ns < 0 {
 | |
| 		return "-" + nsToFloatString(-ns)
 | |
| 	}
 | |
| 	result := fmt.Sprintf("%010d", ns)
 | |
| 	split := len(result) - 9
 | |
| 	result, decimals := result[:split], result[split:]
 | |
| 	decimals = strings.TrimRight(decimals, "0")
 | |
| 	if decimals != "" {
 | |
| 		result += "."
 | |
| 		result += decimals
 | |
| 	}
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| // Turns a floating point string in seconds into a ns integer
 | |
| //
 | |
| // Guaranteed to be perfectly accurate
 | |
| func floatStringToNs(s string) (int64, error) {
 | |
| 	const zeros = "000000000"
 | |
| 	if point := strings.IndexRune(s, '.'); point >= 0 {
 | |
| 		tail := s[point+1:]
 | |
| 		if fill := 9 - len(tail); fill < 0 {
 | |
| 			tail = tail[:9]
 | |
| 		} else {
 | |
| 			tail += zeros[:fill]
 | |
| 		}
 | |
| 		s = s[:point] + tail
 | |
| 	} else if len(s) > 0 { // Make sure empty string produces an error
 | |
| 		s += zeros
 | |
| 	}
 | |
| 	return strconv.ParseInt(s, 10, 64)
 | |
| }
 | |
| 
 | |
| // FloatStringToTime converts a floating point number string to a time.Time
 | |
| //
 | |
| // The string is floating point number of seconds since the epoch
 | |
| // (Unix time).  The number should be in fixed point format (not
 | |
| // exponential), eg "1354040105.123456789" which represents the time
 | |
| // "2012-11-27T18:15:05.123456789Z"
 | |
| //
 | |
| // Some care is taken to preserve all the accuracy in the time.Time
 | |
| // (which wouldn't happen with a naive conversion through float64) so
 | |
| // a round trip conversion won't change the data.
 | |
| //
 | |
| // If an error is returned then time will be returned as the zero time.
 | |
| func FloatStringToTime(s string) (t time.Time, err error) {
 | |
| 	ns, err := floatStringToNs(s)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	t = time.Unix(0, ns)
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // TimeToFloatString converts a time.Time object to a floating point string
 | |
| //
 | |
| // The string is floating point number of seconds since the epoch
 | |
| // (Unix time).  The number is in fixed point format (not
 | |
| // exponential), eg "1354040105.123456789" which represents the time
 | |
| // "2012-11-27T18:15:05.123456789Z".  Trailing zeros will be dropped
 | |
| // from the output.
 | |
| //
 | |
| // Some care is taken to preserve all the accuracy in the time.Time
 | |
| // (which wouldn't happen with a naive conversion through float64) so
 | |
| // a round trip conversion won't change the data.
 | |
| func TimeToFloatString(t time.Time) string {
 | |
| 	return nsToFloatString(t.UnixNano())
 | |
| }
 | |
| 
 | |
| // GetModTime reads a modification time (mtime) from a Metadata object
 | |
| //
 | |
| // This is a defacto standard (used in the official python-swiftclient
 | |
| // amongst others) for storing the modification time (as read using
 | |
| // os.Stat) for an object.  It is stored using the key 'mtime', which
 | |
| // for example when written to an object will be 'X-Object-Meta-Mtime'.
 | |
| //
 | |
| // If an error is returned then time will be returned as the zero time.
 | |
| func (m Metadata) GetModTime() (t time.Time, err error) {
 | |
| 	return FloatStringToTime(m["mtime"])
 | |
| }
 | |
| 
 | |
| // SetModTime writes an modification time (mtime) to a Metadata object
 | |
| //
 | |
| // This is a defacto standard (used in the official python-swiftclient
 | |
| // amongst others) for storing the modification time (as read using
 | |
| // os.Stat) for an object.  It is stored using the key 'mtime', which
 | |
| // for example when written to an object will be 'X-Object-Meta-Mtime'.
 | |
| func (m Metadata) SetModTime(t time.Time) {
 | |
| 	m["mtime"] = TimeToFloatString(t)
 | |
| }
 |