Codebase list golang-github-jacobsa-gcloud / HEAD gcs / conversions.go
HEAD

Tree @HEAD (Download .tar.gz)

conversions.go @HEADraw · history · blame

// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 gcs

import (
	"crypto/md5"
	"encoding/base64"
	"fmt"
	"time"

	storagev1 "google.golang.org/api/storage/v1"
)

////////////////////////////////////////////////////////////////////////
// To our types
////////////////////////////////////////////////////////////////////////

func toTime(s string) (t time.Time, err error) {
	if s != "" {
		t, err = time.Parse(time.RFC3339, s)
	}

	return
}

func toObjects(in []*storagev1.Object) (out []*Object, err error) {
	for _, rawObject := range in {
		var o *Object
		o, err = toObject(rawObject)
		if err != nil {
			err = fmt.Errorf("toObject(%q): %v", o.Name, err)
			return
		}

		out = append(out, o)
	}

	return
}

func toListing(in *storagev1.Objects) (out *Listing, err error) {
	out = &Listing{
		CollapsedRuns:     in.Prefixes,
		ContinuationToken: in.NextPageToken,
	}

	out.Objects, err = toObjects(in.Items)
	if err != nil {
		err = fmt.Errorf("toObjects: %v", err)
		return
	}

	return
}

func toObject(in *storagev1.Object) (out *Object, err error) {
	// Convert the easy fields.
	out = &Object{
		Name:            in.Name,
		ContentType:     in.ContentType,
		ContentLanguage: in.ContentLanguage,
		CacheControl:    in.CacheControl,
		ContentEncoding: in.ContentEncoding,
		ComponentCount:  in.ComponentCount,
		Size:            in.Size,
		MediaLink:       in.MediaLink,
		Metadata:        in.Metadata,
		Generation:      in.Generation,
		MetaGeneration:  in.Metageneration,
		StorageClass:    in.StorageClass,
	}

	// Work around Google-internal bug 21572928. See notes on the ComponentCount
	// field.
	if out.ComponentCount == 0 {
		out.ComponentCount = 1
	}

	// Owner
	if in.Owner != nil {
		out.Owner = in.Owner.Entity
	}

	// Deletion time
	if out.Deleted, err = toTime(in.TimeDeleted); err != nil {
		err = fmt.Errorf("Decoding TimeDeleted field: %v", err)
		return
	}

	// Update time
	if out.Updated, err = toTime(in.Updated); err != nil {
		err = fmt.Errorf("Decoding Updated field: %v", err)
		return
	}

	// MD5
	if in.Md5Hash != "" {
		var md5Slice []byte
		md5Slice, err = base64.StdEncoding.DecodeString(in.Md5Hash)
		if err != nil {
			err = fmt.Errorf("Decoding Md5Hash field: %v", err)
			return
		}

		out.MD5 = new([md5.Size]byte)
		if len(md5Slice) != len(out.MD5) {
			err = fmt.Errorf("Unexpected Md5Hash field: %q", in.Md5Hash)
			return
		}

		copy(out.MD5[:], md5Slice)
	}

	// CRC32C
	crc32cString, err := base64.StdEncoding.DecodeString(in.Crc32c)
	if err != nil {
		err = fmt.Errorf("Decoding Crc32c field: %v", err)
		return
	}

	if len(crc32cString) != 4 {
		err = fmt.Errorf(
			"Wrong length for decoded Crc32c field: %d",
			len(crc32cString))

		return
	}

	out.CRC32C =
		uint32(crc32cString[0])<<24 |
			uint32(crc32cString[1])<<16 |
			uint32(crc32cString[2])<<8 |
			uint32(crc32cString[3])<<0

	return
}

////////////////////////////////////////////////////////////////////////
// From our types
////////////////////////////////////////////////////////////////////////

func toRawObject(
	bucketName string,
	in *CreateObjectRequest) (out *storagev1.Object, err error) {
	out = &storagev1.Object{
		Bucket:          bucketName,
		Name:            in.Name,
		ContentType:     in.ContentType,
		ContentLanguage: in.ContentLanguage,
		ContentEncoding: in.ContentEncoding,
		CacheControl:    in.CacheControl,
		Metadata:        in.Metadata,
	}

	if in.CRC32C != nil {
		buf := []byte{
			byte(*in.CRC32C >> 24),
			byte(*in.CRC32C >> 16),
			byte(*in.CRC32C >> 8),
			byte(*in.CRC32C >> 0),
		}

		out.Crc32c = base64.StdEncoding.EncodeToString(buf)
	}

	if in.MD5 != nil {
		out.Md5Hash = base64.StdEncoding.EncodeToString(in.MD5[:])
	}

	return
}