Skip to content

Commit 5e9bbd4

Browse files
committed
Final API implementation of RawRead returning an io.ReadCloser (file like) object. #13
1 parent 552a703 commit 5e9bbd4

File tree

4 files changed

+46
-33
lines changed

4 files changed

+46
-33
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,9 @@ Once the data package is loaded, we could use the [Resource.RawRead](https://god
238238

239239
```go
240240
so := pkg.GetResource("schemaorg")
241-
soContents, _ := so.RawRead()
241+
rc, _ := so.RawRead()
242+
defer rc.Close()
243+
contents, _ := ioutil.ReadAll(rc)
242244
// Use contents. For instance, one could validate the JSON-LD schema and unmarshal it into a data structure.
243245

244246
data := pkg.GetResource("data")

datapackage/package_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ func ExampleLoad_readRaw() {
7373
ioutil.WriteFile(resPath, resContent, 0666)
7474

7575
pkg, _ := Load(descriptorPath, validator.InMemoryLoader())
76-
contents, _ := pkg.GetResource("res1").RawRead()
76+
rc, _ := pkg.GetResource("res1").RawRead()
77+
defer rc.Close()
78+
contents, _ := ioutil.ReadAll(rc)
7779
fmt.Println(string(contents))
7880
// Output: {"@context": {"@vocab": "http://schema.org/"}}
7981
}

datapackage/resource.go

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -194,15 +194,7 @@ func (r *Resource) GetTable(opts ...csv.CreationOpts) (table.Table, error) {
194194
return nil, fmt.Errorf("only csv and string is supported for inlining data")
195195
}
196196
}
197-
buf, err := loadContents(r.basePath, r.path, csvLoadFunc)
198-
if err != nil {
199-
return nil, err
200-
}
201-
t, err := csv.NewTable(func() (io.ReadCloser, error) { return ioutil.NopCloser(bytes.NewReader(buf)), nil }, fullOpts...)
202-
if err != nil {
203-
return nil, err
204-
}
205-
return t, nil
197+
return csv.NewTable(func() (io.ReadCloser, error) { return loadContents(r.basePath, r.path, csvLoadFunc) }, fullOpts...)
206198
}
207199

208200
func csvLoadFunc(p string) func() (io.ReadCloser, error) {
@@ -233,23 +225,39 @@ func binaryLoadFunc(p string) func() (io.ReadCloser, error) {
233225
if err != nil {
234226
return nil, err
235227
}
236-
defer resp.Body.Close()
237-
b, err := ioutil.ReadAll(resp.Body)
238-
if err != nil {
239-
return nil, err
240-
}
241-
return ioutil.NopCloser(bytes.NewReader(b)), nil
228+
return resp.Body, nil
242229
}
243230
}
244231
return func() (io.ReadCloser, error) {
245232
return os.Open(p)
246233
}
247234
}
248235

249-
type loadFunc func(string) func() (io.ReadCloser, error)
236+
type multiReadCloser struct {
237+
io.Reader
238+
rcs []io.ReadCloser
239+
}
240+
241+
func (m *multiReadCloser) Close() error {
242+
var err error
243+
for _, rc := range m.rcs {
244+
if e := rc.Close(); e != nil {
245+
err = e
246+
}
247+
}
248+
return err
249+
}
250+
251+
func newMultiReadCloser(rcs []io.ReadCloser) io.ReadCloser {
252+
readers := make([]io.Reader, len(rcs))
253+
for i := range rcs {
254+
readers[i] = io.Reader(rcs[i])
255+
}
256+
return &multiReadCloser{io.MultiReader(readers...), rcs}
257+
}
250258

251-
func loadContents(basePath string, path []string, f loadFunc) ([]byte, error) {
252-
var buf bytes.Buffer
259+
func loadContents(basePath string, path []string, f func(string) func() (io.ReadCloser, error)) (io.ReadCloser, error) {
260+
var rcs []io.ReadCloser
253261
for _, p := range path {
254262
if basePath != "" {
255263
p = joinPaths(basePath, p)
@@ -258,17 +266,12 @@ func loadContents(basePath string, path []string, f loadFunc) ([]byte, error) {
258266
if err != nil {
259267
return nil, err
260268
}
261-
defer rc.Close()
262-
b, err := ioutil.ReadAll(rc)
263-
if err != nil {
264-
return nil, err
265-
}
266-
buf.Write(b)
269+
rcs = append(rcs, rc)
267270
if len(path) > 1 {
268-
buf.WriteRune('\n')
271+
rcs = append(rcs, ioutil.NopCloser(bytes.NewReader([]byte{'\n'})))
269272
}
270273
}
271-
return buf.Bytes(), nil
274+
return newMultiReadCloser(rcs), nil
272275
}
273276

274277
func joinPaths(basePath, path string) string {
@@ -289,11 +292,11 @@ func (r *Resource) ReadAll(opts ...csv.CreationOpts) ([][]string, error) {
289292
return t.ReadAll()
290293
}
291294

292-
// RawRead reads all resource contents and return it as byte slice.
295+
// RawRead returns an io.ReaderCloser associated to the resource contents.
293296
// It can be used to access the content of non-tabular resources.
294-
func (r *Resource) RawRead() ([]byte, error) {
297+
func (r *Resource) RawRead() (io.ReadCloser, error) {
295298
if r.data != nil {
296-
return []byte(r.data.(string)), nil
299+
return ioutil.NopCloser(bytes.NewReader([]byte(r.data.(string)))), nil
297300
}
298301
return loadContents(r.basePath, r.path, binaryLoadFunc)
299302
}

datapackage/resource_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,10 @@ func TestResource_RawRead(t *testing.T) {
434434
}`, ts.URL)
435435
res, err := NewResourceFromString(resStr, validator.MustInMemoryRegistry())
436436
is.NoErr(err)
437-
contents, err := res.RawRead()
437+
rc, err := res.RawRead()
438+
is.NoErr(err)
439+
defer rc.Close()
440+
contents, err := ioutil.ReadAll(rc)
438441
is.NoErr(err)
439442
is.Equal(string(contents), "1234")
440443
})
@@ -446,7 +449,10 @@ func TestResource_RawRead(t *testing.T) {
446449
}`
447450
res, err := NewResourceFromString(resStr, validator.MustInMemoryRegistry())
448451
is.NoErr(err)
449-
contents, err := res.RawRead()
452+
rc, err := res.RawRead()
453+
is.NoErr(err)
454+
defer rc.Close()
455+
contents, err := ioutil.ReadAll(rc)
450456
is.NoErr(err)
451457
is.Equal(string(contents), "{\"foo\":\"1234\"}")
452458
})

0 commit comments

Comments
 (0)