github.com/emcfarlane/larking@v0.0.0-20220605172417-1704b45ee6c3/starlib/starlarkblob/blob.go (about)

     1  // Copyright 2021 Edward McFarlane. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package blob provides access to blob objects within a storage location.
     6  package starlarkblob
     7  
     8  import (
     9  	"fmt"
    10  	"sort"
    11  
    12  	"github.com/emcfarlane/larking/starlib/starext"
    13  	"github.com/emcfarlane/larking/starlib/starlarkstruct"
    14  	"github.com/emcfarlane/larking/starlib/starlarkthread"
    15  
    16  	starlarktime "go.starlark.net/lib/time"
    17  	"go.starlark.net/starlark"
    18  	"gocloud.dev/blob"
    19  )
    20  
    21  func NewModule() *starlarkstruct.Module {
    22  	return &starlarkstruct.Module{
    23  		Name: "blob",
    24  		Members: starlark.StringDict{
    25  			"open": starext.MakeBuiltin("blob.open", Open),
    26  		},
    27  	}
    28  }
    29  
    30  func Open(thread *starlark.Thread, fnname string, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
    31  	var name string
    32  	if err := starlark.UnpackPositionalArgs(fnname, args, kwargs, 1, &name); err != nil {
    33  		return nil, err
    34  	}
    35  
    36  	ctx := starlarkthread.GetContext(thread)
    37  	bkt, err := blob.OpenBucket(ctx, name)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	b := &Bucket{name: name, bkt: bkt}
    43  	if err := starlarkthread.AddResource(thread, b); err != nil {
    44  		return nil, err
    45  	}
    46  	return b, nil
    47  }
    48  
    49  type Bucket struct {
    50  	name string
    51  	bkt  *blob.Bucket
    52  }
    53  
    54  func (b *Bucket) Close() error          { return b.bkt.Close() }
    55  func (b *Bucket) String() string        { return fmt.Sprintf("<bucket %q>", b.name) }
    56  func (b *Bucket) Type() string          { return "blob.bucket" }
    57  func (b *Bucket) Freeze()               {} // concurrent safe
    58  func (b *Bucket) Truth() starlark.Bool  { return b.bkt != nil }
    59  func (b *Bucket) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable type: %s", b.Type()) }
    60  
    61  type bucketAttr func(b *Bucket) starlark.Value
    62  
    63  var bucketAttrs = map[string]bucketAttr{
    64  	"attributes": func(b *Bucket) starlark.Value { return starext.MakeMethod(b, "attributes", b.attributes) },
    65  	"write_all":  func(b *Bucket) starlark.Value { return starext.MakeMethod(b, "write_all", b.writeAll) },
    66  	"read_all":   func(b *Bucket) starlark.Value { return starext.MakeMethod(b, "read_all", b.readAll) },
    67  	"delete":     func(b *Bucket) starlark.Value { return starext.MakeMethod(b, "delete", b.delete) },
    68  	"close":      func(b *Bucket) starlark.Value { return starext.MakeMethod(b, "close", b.close) },
    69  }
    70  
    71  func (v *Bucket) Attr(name string) (starlark.Value, error) {
    72  	if a := bucketAttrs[name]; a != nil {
    73  		return a(v), nil
    74  	}
    75  	return nil, nil
    76  }
    77  func (v *Bucket) AttrNames() []string {
    78  	names := make([]string, 0, len(bucketAttrs))
    79  	for name := range bucketAttrs {
    80  		names = append(names, name)
    81  	}
    82  	sort.Strings(names)
    83  	return names
    84  }
    85  
    86  func (v *Bucket) attributes(thread *starlark.Thread, fnname string, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
    87  	var (
    88  		key string
    89  	)
    90  	if err := starlark.UnpackPositionalArgs(fnname, args, kwargs, 1, &key); err != nil {
    91  		return nil, err
    92  	}
    93  
    94  	ctx := starlarkthread.GetContext(thread)
    95  	p, err := v.bkt.Attributes(ctx, key)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	return starlarkstruct.FromKeyValues(
   100  		starlarkstruct.Default,
   101  		"cache_control", starlark.String(p.CacheControl),
   102  		"content_disposition", starlark.String(p.ContentDisposition),
   103  		"content_encoding", starlark.String(p.ContentEncoding),
   104  		"content_language", starlark.String(p.ContentLanguage),
   105  		"content_type", starlark.String(p.ContentType),
   106  		"metadata", starext.ToDict(p.Metadata),
   107  		"mod_time", starlarktime.Time(p.ModTime),
   108  		"sie", starlark.MakeInt64(p.Size),
   109  		"md5", starlark.String(p.MD5),
   110  		"etag", starlark.String(p.ETag),
   111  	), nil
   112  
   113  }
   114  
   115  func (v *Bucket) writeAll(thread *starlark.Thread, fnname string, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
   116  	var (
   117  		key   string
   118  		bytes string
   119  		opts  blob.WriterOptions // TODO
   120  
   121  		//
   122  		contentMD5  string // []byte
   123  		metadata    starlark.Dict
   124  		beforeWrite starlark.Callable
   125  	)
   126  
   127  	if err := starlark.UnpackArgs(fnname, args, kwargs,
   128  		"key", &key,
   129  		"bytes", &bytes,
   130  		"buffer_size?", &opts.BufferSize, // int
   131  		"cache_control?", &opts.CacheControl, //  string
   132  		"content_disposition?", &opts.ContentDisposition, //  string
   133  		"content_encoding?", &opts.ContentEncoding, // string
   134  		"content_language?", &opts.ContentLanguage, // string
   135  		"content_type?", &opts.ContentType, // string
   136  		"content_md5?", &contentMD5, // []byte
   137  		"metadata?", &metadata, // map[string]string
   138  		"before_write?", &beforeWrite, // func(asFunc func(interface{}) bool) error
   139  
   140  	); err != nil {
   141  		return nil, err
   142  	}
   143  	opts.ContentMD5 = []byte(contentMD5)
   144  	if items := metadata.Items(); len(items) > 0 {
   145  		opts.Metadata = make(map[string]string, len(items))
   146  
   147  		for _, item := range items {
   148  			key, ok := starlark.AsString(item[0])
   149  			if !ok {
   150  				return nil, fmt.Errorf("invalid metadata key: %v", item[0])
   151  			}
   152  			val, ok := starlark.AsString(item[1])
   153  			if !ok {
   154  				return nil, fmt.Errorf("invalid metadata key: %v, value: %v", item[0], item[1])
   155  			}
   156  			opts.Metadata[key] = val
   157  		}
   158  	}
   159  	// TODO: handle As method beforeWrite.
   160  
   161  	ctx := starlarkthread.GetContext(thread)
   162  	if err := v.bkt.WriteAll(ctx, key, []byte(bytes), &opts); err != nil {
   163  		return nil, err
   164  	}
   165  	return starlark.None, nil
   166  }
   167  
   168  func (v *Bucket) readAll(thread *starlark.Thread, fnname string, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
   169  	var (
   170  		key string
   171  		//opts blob.ReaderOptions
   172  	)
   173  	if err := starlark.UnpackPositionalArgs(fnname, args, kwargs, 1, &key); err != nil {
   174  		return nil, err
   175  	}
   176  
   177  	ctx := starlarkthread.GetContext(thread)
   178  	p, err := v.bkt.ReadAll(ctx, key) // &opts)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	return starlark.Bytes(p), nil
   183  }
   184  
   185  func (v *Bucket) delete(thread *starlark.Thread, fnname string, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
   186  	var (
   187  		key string
   188  	)
   189  	if err := starlark.UnpackPositionalArgs(fnname, args, kwargs, 1, &key); err != nil {
   190  		return nil, err
   191  	}
   192  
   193  	ctx := starlarkthread.GetContext(thread)
   194  	if err := v.bkt.Delete(ctx, key); err != nil {
   195  		return nil, err
   196  	}
   197  	return starlark.None, nil
   198  }
   199  
   200  func (v *Bucket) close(_ *starlark.Thread, fnname string, _ starlark.Tuple, _ []starlark.Tuple) (starlark.Value, error) {
   201  	if err := v.bkt.Close(); err != nil {
   202  		return nil, err
   203  	}
   204  	return starlark.None, nil
   205  }