aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--objects/object_snapshot.go111
-rw-r--r--objects/object_snapshot_test.go62
2 files changed, 173 insertions, 0 deletions
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)
+ }
+ }
+}