From c642ac68ea86e233a390294f26a4264d0c797c33 Mon Sep 17 00:00:00 2001 From: Laria Carolin Chabowski Date: Wed, 28 Jun 2017 08:15:50 +0200 Subject: Add snapshot object --- objects/object_snapshot.go | 111 ++++++++++++++++++++++++++++++++++++++++ objects/object_snapshot_test.go | 62 ++++++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 objects/object_snapshot.go create mode 100644 objects/object_snapshot_test.go diff --git a/objects/object_snapshot.go b/objects/object_snapshot.go new file mode 100644 index 0000000..a161f27 --- /dev/null +++ b/objects/object_snapshot.go @@ -0,0 +1,111 @@ +package objects + +import ( + "bytes" + "errors" + "fmt" + "io" + "strings" + "time" +) + +type Snapshot struct { + Tree ObjectId + Date time.Time + Container string + Comment string +} + +func (s Snapshot) Type() ObjectType { + return OTSnapshot +} + +func appendKVPair(b []byte, k, v string) []byte { + b = append(b, []byte(k)...) + b = append(b, ' ') + b = append(b, []byte(v)...) + b = append(b, '\n') + return b +} + +func (s Snapshot) Payload() (out []byte) { + out = appendKVPair(out, "container", s.Container) + out = appendKVPair(out, "date", s.Date.Format(time.RFC3339)) + out = appendKVPair(out, "tree", s.Tree.String()) + + if s.Comment != "" { + out = append(out, '\n') + out = append(out, []byte(s.Comment)...) + } + + return out +} + +func (s *Snapshot) FromPayload(payload []byte) error { + r := bytes.NewBuffer(payload) + + seenContainer := false + seenDate := false + seenTree := false + + for { + line, err := r.ReadString('\n') + if err != nil && err != io.EOF { + return err + } + line = strings.TrimSpace(line) + + if line == "" { + break // Header read successfully + } + + parts := strings.SplitN(line, " ", 2) + if len(parts) != 2 { + return fmt.Errorf("Invalid header: %s", line) + } + + headerval := strings.TrimSpace(parts[1]) + switch parts[0] { + case "container": + s.Container = headerval + seenContainer = true + case "date": + d, err := time.Parse(time.RFC3339, headerval) + if err != nil { + return err + } + s.Date = d + seenDate = true + case "tree": + oid, err := ParseObjectId(headerval) + if err != nil { + return err + } + s.Tree = oid + seenTree = true + } + + if err == io.EOF { + break + } + } + + if !seenContainer || !seenDate || !seenTree { + return errors.New("Missing container, date or tree header") + } + + b := new(bytes.Buffer) + if _, err := io.Copy(b, r); err != nil { + return err + } + + s.Comment = strings.TrimSpace(string(b.Bytes())) + return nil +} + +func (a Snapshot) Equals(b Snapshot) bool { + return a.Tree.Equals(b.Tree) && + a.Container == b.Container && + a.Date.Equal(b.Date) && + a.Comment == b.Comment +} diff --git a/objects/object_snapshot_test.go b/objects/object_snapshot_test.go new file mode 100644 index 0000000..b610190 --- /dev/null +++ b/objects/object_snapshot_test.go @@ -0,0 +1,62 @@ +package objects + +import ( + "bytes" + "testing" + "time" +) + +var ( + testSnapshotObj = Snapshot{ + Comment: "foo\nbar\nbaz!", + Container: "foo", + Date: time.Date(2017, 07, 01, 21, 40, 00, 0, time.FixedZone("", 2*60*60)), + Tree: genId(0xff), + } + + testSnapshotSerialization = []byte("" + + "container foo\n" + + "date 2017-07-01T21:40:00+02:00\n" + + "tree sha3-256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\n" + + "\n" + + "foo\n" + + "bar\n" + + "baz!") +) + +func TestSerializeSnapshot(t *testing.T) { + have := testSnapshotObj.Payload() + + if !bytes.Equal(have, testSnapshotSerialization) { + t.Errorf("Unexpected serialization result: %s", have) + } +} + +func TestUnserializeSnapshot(t *testing.T) { + have := Snapshot{} + if err := have.FromPayload(testSnapshotSerialization); err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + if !have.Equals(testSnapshotObj) { + t.Errorf("Unexpeced unserialization result: %v", have) + } +} + +func TestUnserializeSnapshotFailure(t *testing.T) { + subtests := []struct{ name, payload string }{ + {"empty", ""}, + {"missing tree", "container foo\ndate 2017-07-01T22:02:00+02:00\n"}, + {"missing container", "date 2017-07-01T22:02:00+02:00\ntree sha3-256:0000000000000000000000000000000000000000000000000000000000000000\n"}, + {"missing date", "container foo\ntree sha3-256:0000000000000000000000000000000000000000000000000000000000000000\n"}, + {"invalid date", "container foo\ndate foobar\ntree sha3-256:0000000000000000000000000000000000000000000000000000000000000000\n"}, + } + + for _, subtest := range subtests { + have := Snapshot{} + err := have.FromPayload([]byte(subtest.payload)) + if err == nil { + t.Errorf("Unexpected unserialization success: %v", have) + } + } +} -- cgit v1.2.3-54-g00ecf