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 }