Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit 5bc5637

Browse files
committed
Merge pull request #34 from scjalliance/object-reader-writer
Refactor to use core.ObjectReader and core.ObjectWriter
2 parents 07ca1ac + 0d999e1 commit 5bc5637

File tree

18 files changed

+237
-54
lines changed

18 files changed

+237
-54
lines changed

blame.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,10 @@ func (c *Commit) Blame(path string) (*Blame, error) {
8282
if err != nil {
8383
return nil, err
8484
}
85-
finalLines := file.Lines()
85+
finalLines, err := file.Lines()
86+
if err != nil {
87+
return nil, err
88+
}
8689

8790
lines, err := newLines(finalLines, b.sliceGraph(len(b.graph)-1))
8891
if err != nil {
@@ -153,7 +156,10 @@ func (b *blame) fillGraphAndData() error {
153156
if err != nil {
154157
return nil
155158
}
156-
b.data[i] = file.Contents()
159+
b.data[i], err = file.Contents()
160+
if err != nil {
161+
return err
162+
}
157163
nLines := countLines(b.data[i])
158164
// create a node for each line
159165
b.graph[i] = make([]*Commit, nLines)
@@ -221,7 +227,10 @@ func (b *blame) GoString() string {
221227
if err != nil {
222228
panic("PrettyPrint: internal error in repo.Data")
223229
}
224-
contents := file.Contents()
230+
contents, err := file.Contents()
231+
if err != nil {
232+
panic("PrettyPrint: internal error in repo.Data")
233+
}
225234

226235
lines := strings.Split(contents, "\n")
227236
// max line number length

blame_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ func (s *BlameCommon) mockBlame(t blameTest, c *C) (blame *Blame) {
5656

5757
file, err := commit.File(t.path)
5858
c.Assert(err, IsNil)
59-
lines := file.Lines()
59+
lines, err := file.Lines()
60+
c.Assert(err, IsNil)
6061
c.Assert(len(t.blames), Equals, len(lines), Commentf(
6162
"repo=%s, path=%s, rev=%s: the number of lines in the file and the number of expected blames differ (len(blames)=%d, len(lines)=%d)\nblames=%#q\nlines=%#q", t.repo, t.path, t.rev, len(t.blames), len(lines), t.blames, lines))
6263

commit.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,20 @@ func (c *Commit) File(path string) (file *File, err error) {
5050
}
5151

5252
// Decode transform an core.Object into a Blob struct
53-
func (c *Commit) Decode(o core.Object) error {
53+
func (c *Commit) Decode(o core.Object) (err error) {
5454
if o.Type() != core.CommitObject {
5555
return ErrUnsupportedObject
5656
}
5757

5858
c.Hash = o.Hash()
59-
r := bufio.NewReader(o.Reader())
59+
60+
reader, err := o.Reader()
61+
if err != nil {
62+
return err
63+
}
64+
defer close(reader, &err)
65+
66+
r := bufio.NewReader(reader)
6067

6168
var message bool
6269
for {

common.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package git
22

3-
import "strings"
3+
import (
4+
"io"
5+
"strings"
6+
)
47

58
// countLines returns the number of lines in a string à la git, this is
69
// The newline character is assumed to be '\n'. The empty string
@@ -18,3 +21,23 @@ func countLines(s string) int {
1821

1922
return nEOL + 1
2023
}
24+
25+
// close is used with defer to close the given io.Closer and check its
26+
// returned error value. If Close returns an error and the given *error
27+
// is not nil, *error is set to the error returned by Close.
28+
//
29+
// close is typically used with named return values like so:
30+
//
31+
// func do(obj *Object) (err error) {
32+
// w, err := obj.Writer()
33+
// if err != nil {
34+
// return nil
35+
// }
36+
// defer close(w, &err)
37+
// // work with w
38+
// }
39+
func close(c io.Closer, err *error) {
40+
if cerr := c.Close(); cerr != nil && *err == nil {
41+
*err = cerr
42+
}
43+
}

core/object.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,30 @@ var (
1010
ObjectNotFoundErr = errors.New("object not found")
1111
)
1212

13+
// TODO: Consider adding a Hash function to the ObjectReader and ObjectWriter
14+
// interfaces that returns the hash calculated for the reader or writer.
15+
16+
// ObjectReader is a generic representation of an object reader.
17+
//
18+
// ObjectReader implements io.ReadCloser. Close should be called when finished
19+
// with it.
20+
type ObjectReader io.ReadCloser
21+
22+
// ObjectWriter is a generic representation of an object writer.
23+
//
24+
// ObjectWriter implements io.WriterCloser. Close should be called when finished
25+
// with it.
26+
type ObjectWriter io.WriteCloser
27+
1328
// Object is a generic representation of any git object
1429
type Object interface {
1530
Hash() Hash
1631
Type() ObjectType
1732
SetType(ObjectType)
1833
Size() int64
1934
SetSize(int64)
20-
Reader() io.Reader
21-
Writer() io.Writer
35+
Reader() (ObjectReader, error)
36+
Writer() (ObjectWriter, error)
2237
}
2338

2439
// ObjectStorage generic storage of objects

file.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,35 @@ func newFile(name string, b *Blob) *File {
1818
}
1919

2020
// Contents returns the contents of a file as a string.
21-
func (f *File) Contents() string {
21+
func (f *File) Contents() (content string, err error) {
22+
reader, err := f.Reader()
23+
if err != nil {
24+
return "", err
25+
}
26+
defer close(reader, &err)
27+
2228
buf := new(bytes.Buffer)
23-
buf.ReadFrom(f.Reader())
29+
buf.ReadFrom(reader)
2430

25-
return buf.String()
31+
return buf.String(), nil
2632
}
2733

2834
// Lines returns a slice of lines from the contents of a file, stripping
2935
// all end of line characters. If the last line is empty (does not end
3036
// in an end of line), it is also stripped.
31-
func (f *File) Lines() []string {
32-
splits := strings.Split(f.Contents(), "\n")
37+
func (f *File) Lines() ([]string, error) {
38+
content, err := f.Contents()
39+
if err != nil {
40+
return nil, err
41+
}
42+
43+
splits := strings.Split(content, "\n")
3344
// remove the last line if it is empty
3445
if splits[len(splits)-1] == "" {
35-
return splits[:len(splits)-1]
46+
return splits[:len(splits)-1], nil
3647
}
3748

38-
return splits
49+
return splits, nil
3950
}
4051

4152
type FileIter struct {

file_test.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,9 @@ func (s *SuiteFile) TestContents(c *C) {
125125

126126
file, err := commit.File(t.path)
127127
c.Assert(err, IsNil)
128-
c.Assert(file.Contents(), Equals, t.contents, Commentf(
128+
content, err := file.Contents()
129+
c.Assert(err, IsNil)
130+
c.Assert(content, Equals, t.contents, Commentf(
129131
"subtest %d: commit=%s, path=%s", i, t.commit, t.path))
130132
}
131133
}
@@ -172,7 +174,9 @@ func (s *SuiteFile) TestLines(c *C) {
172174

173175
file, err := commit.File(t.path)
174176
c.Assert(err, IsNil)
175-
c.Assert(file.Lines(), DeepEquals, t.lines, Commentf(
177+
lines, err := file.Lines()
178+
c.Assert(err, IsNil)
179+
c.Assert(lines, DeepEquals, t.lines, Commentf(
176180
"subtest %d: commit=%s, path=%s", i, t.commit, t.path))
177181
}
178182
}
@@ -201,7 +205,7 @@ func (s *SuiteFile) TestIgnoreEmptyDirEntries(c *C) {
201205
iter := commit.Tree().Files()
202206
defer iter.Close()
203207
for file, err := iter.Next(); err == nil; file, err = iter.Next() {
204-
_ = file.Contents()
208+
_, _ = file.Contents()
205209
// this would probably panic if we are not ignoring empty dirs
206210
}
207211
}

formats/packfile/common.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,23 @@ func (t *trackingReader) ReadByte() (c byte, err error) {
4141
t.position++
4242
return p[0], nil
4343
}
44+
45+
// close is used with defer to close the given io.Closer and check its
46+
// returned error value. If Close returns an error and the given *error
47+
// is not nil, *error is set to the error returned by Close.
48+
//
49+
// close is typically used with named return values like so:
50+
//
51+
// func do(obj *Object) (err error) {
52+
// w, err := obj.Writer()
53+
// if err != nil {
54+
// return nil
55+
// }
56+
// defer close(w, &err)
57+
// // work with w
58+
// }
59+
func close(c io.Closer, err *error) {
60+
if cerr := c.Close(); cerr != nil && *err == nil {
61+
*err = cerr
62+
}
63+
}

formats/packfile/reader.go

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ func (r *Reader) newObject() (core.Object, error) {
187187
return raw, err
188188
}
189189

190-
func (r *Reader) readREFDelta(raw core.Object) error {
190+
func (r *Reader) readREFDelta(raw core.Object) (err error) {
191191
var ref core.Hash
192192
if _, err := io.ReadFull(r.r, ref[:]); err != nil {
193193
return err
@@ -206,28 +206,45 @@ func (r *Reader) readREFDelta(raw core.Object) error {
206206
return err
207207
}
208208

209-
d, _ := ioutil.ReadAll(referenced.Reader())
209+
reader, err := referenced.Reader()
210+
if err != nil {
211+
return err
212+
}
213+
defer close(reader, &err)
214+
215+
d, err := ioutil.ReadAll(reader)
216+
if err != nil {
217+
return err
218+
}
219+
210220
patched := patchDelta(d, buf.Bytes())
211221
if patched == nil {
212222
return PatchingErr.n("hash %q", ref)
213223
}
214224

215225
raw.SetType(referenced.Type())
216226
raw.SetSize(int64(len(patched)))
217-
raw.Writer().Write(patched)
227+
228+
writer, err := raw.Writer()
229+
if err != nil {
230+
return err
231+
}
232+
defer close(writer, &err)
233+
234+
writer.Write(patched)
218235

219236
return nil
220237
}
221238

222-
func (r *Reader) readOFSDelta(raw core.Object, steps int64) error {
239+
func (r *Reader) readOFSDelta(raw core.Object, steps int64) (err error) {
223240
start := r.r.position
224241
offset, err := decodeOffset(r.r, steps)
225242
if err != nil {
226243
return err
227244
}
228245

229246
buf := bytes.NewBuffer(nil)
230-
if err := r.inflate(buf); err != nil {
247+
if err = r.inflate(buf); err != nil {
231248
return err
232249
}
233250

@@ -236,22 +253,49 @@ func (r *Reader) readOFSDelta(raw core.Object, steps int64) error {
236253
return PackEntryNotFoundErr.n("offset %d", start+offset)
237254
}
238255

239-
referenced, _ := r.s.Get(ref) // FIXME: Handle error returned from Get()
240-
d, _ := ioutil.ReadAll(referenced.Reader())
256+
referenced, err := r.s.Get(ref)
257+
if err != nil {
258+
return err
259+
}
260+
261+
reader, err := referenced.Reader()
262+
if err != nil {
263+
return err
264+
}
265+
defer close(reader, &err)
266+
267+
d, err := ioutil.ReadAll(reader)
268+
if err != nil {
269+
return err
270+
}
271+
241272
patched := patchDelta(d, buf.Bytes())
242273
if patched == nil {
243274
return PatchingErr.n("hash %q", ref)
244275
}
245276

246277
raw.SetType(referenced.Type())
247278
raw.SetSize(int64(len(patched)))
248-
raw.Writer().Write(patched)
279+
280+
writer, err := raw.Writer()
281+
if err != nil {
282+
return err
283+
}
284+
defer close(writer, &err)
285+
286+
writer.Write(patched)
249287

250288
return nil
251289
}
252290

253-
func (r *Reader) readObject(raw core.Object) error {
254-
return r.inflate(raw.Writer())
291+
func (r *Reader) readObject(raw core.Object) (err error) {
292+
writer, err := raw.Writer()
293+
if err != nil {
294+
return err
295+
}
296+
defer close(writer, &err)
297+
298+
return r.inflate(writer)
255299
}
256300

257301
func (r *Reader) inflate(w io.Writer) error {

objects.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"bytes"
55
"errors"
66
"fmt"
7-
"io"
87
"strconv"
98
"time"
109

@@ -35,7 +34,7 @@ func (b *Blob) Decode(o core.Object) error {
3534
}
3635

3736
// Reader returns a reader allow the access to the content of the blob
38-
func (b *Blob) Reader() io.Reader {
37+
func (b *Blob) Reader() (core.ObjectReader, error) {
3938
return b.obj.Reader()
4039
}
4140

0 commit comments

Comments
 (0)