From 1a1187e086097b1dd75360460767b2de85fd5362 Mon Sep 17 00:00:00 2001 From: Akshay Date: Sat, 8 Mar 2025 17:45:28 +0000 Subject: [PATCH] add stars to tangled --- api/tangled/cbor_gen.go | 1380 +++++++++-------- api/tangled/feedstar.go | 23 + appview/db/db.go | 10 + appview/db/follow.go | 17 +- appview/db/issues.go | 49 +- appview/db/repos.go | 31 +- appview/db/star.go | 150 ++ appview/db/timeline.go | 26 +- appview/pages/funcmap.go | 7 + appview/pages/pages.go | 59 +- appview/pages/templates/fragments/follow.html | 17 + appview/pages/templates/fragments/star.html | 36 + appview/pages/templates/layouts/repobase.html | 30 +- appview/pages/templates/timeline.html | 50 +- appview/pages/templates/user/profile.html | 16 +- appview/state/follow.go | 33 +- appview/state/jetstream.go | 20 + appview/state/repo.go | 168 +- appview/state/star.go | 115 ++ appview/state/state.go | 11 +- cmd/gen.go | 11 +- lexicons/star.json | 31 + 22 files changed, 1486 insertions(+), 804 deletions(-) create mode 100644 api/tangled/feedstar.go create mode 100644 appview/db/star.go create mode 100644 appview/pages/templates/fragments/follow.html create mode 100644 appview/pages/templates/fragments/star.html create mode 100644 appview/state/star.go create mode 100644 lexicons/star.json diff --git a/api/tangled/cbor_gen.go b/api/tangled/cbor_gen.go index 6c4c89dd..31c209f3 100644 --- a/api/tangled/cbor_gen.go +++ b/api/tangled/cbor_gen.go @@ -18,7 +18,7 @@ var _ = cid.Undef var _ = math.E var _ = sort.Sort -func (t *PublicKey) MarshalCBOR(w io.Writer) error { +func (t *FeedStar) MarshalCBOR(w io.Writer) error { if t == nil { _, err := w.Write(cbg.CborNull) return err @@ -26,53 +26,171 @@ func (t *PublicKey) MarshalCBOR(w io.Writer) error { cw := cbg.NewCborWriter(w) - if _, err := cw.Write([]byte{164}); err != nil { + if _, err := cw.Write([]byte{163}); err != nil { return err } - // t.Key (string) (string) - if len("key") > 1000000 { - return xerrors.Errorf("Value in field \"key\" was too long") + // t.LexiconTypeID (string) (string) + if len("$type") > 1000000 { + return xerrors.Errorf("Value in field \"$type\" was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("key"))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("$type"))); err != nil { return err } - if _, err := cw.WriteString(string("key")); err != nil { + if _, err := cw.WriteString(string("$type")); err != nil { return err } - if len(t.Key) > 1000000 { - return xerrors.Errorf("Value in field t.Key was too long") + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.feed.star"))); err != nil { + return err + } + if _, err := cw.WriteString(string("sh.tangled.feed.star")); err != nil { + return err } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Key))); err != nil { + // t.Subject (string) (string) + if len("subject") > 1000000 { + return xerrors.Errorf("Value in field \"subject\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("subject"))); err != nil { return err } - if _, err := cw.WriteString(string(t.Key)); err != nil { + if _, err := cw.WriteString(string("subject")); err != nil { return err } - // t.Name (string) (string) - if len("name") > 1000000 { - return xerrors.Errorf("Value in field \"name\" was too long") + if len(t.Subject) > 1000000 { + return xerrors.Errorf("Value in field t.Subject was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("name"))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Subject))); err != nil { return err } - if _, err := cw.WriteString(string("name")); err != nil { + if _, err := cw.WriteString(string(t.Subject)); err != nil { return err } - if len(t.Name) > 1000000 { - return xerrors.Errorf("Value in field t.Name was too long") + // t.CreatedAt (string) (string) + if len("createdAt") > 1000000 { + return xerrors.Errorf("Value in field \"createdAt\" was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Name))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("createdAt"))); err != nil { return err } - if _, err := cw.WriteString(string(t.Name)); err != nil { + if _, err := cw.WriteString(string("createdAt")); err != nil { + return err + } + + if len(t.CreatedAt) > 1000000 { + return xerrors.Errorf("Value in field t.CreatedAt was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.CreatedAt))); err != nil { + return err + } + if _, err := cw.WriteString(string(t.CreatedAt)); err != nil { + return err + } + return nil +} + +func (t *FeedStar) UnmarshalCBOR(r io.Reader) (err error) { + *t = FeedStar{} + + cr := cbg.NewCborReader(r) + + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + defer func() { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + }() + + if maj != cbg.MajMap { + return fmt.Errorf("cbor input should be of type map") + } + + if extra > cbg.MaxLength { + return fmt.Errorf("FeedStar: map struct too large (%d)", extra) + } + + n := extra + + nameBuf := make([]byte, 9) + for i := uint64(0); i < n; i++ { + nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) + if err != nil { + return err + } + + if !ok { + // Field doesn't exist on this type, so ignore it + if err := cbg.ScanForLinks(cr, func(cid.Cid) {}); err != nil { + return err + } + continue + } + + switch string(nameBuf[:nameLen]) { + // t.LexiconTypeID (string) (string) + case "$type": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.LexiconTypeID = string(sval) + } + // t.Subject (string) (string) + case "subject": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Subject = string(sval) + } + // t.CreatedAt (string) (string) + case "createdAt": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.CreatedAt = string(sval) + } + + default: + // Field doesn't exist on this type, so ignore it + if err := cbg.ScanForLinks(r, func(cid.Cid) {}); err != nil { + return err + } + } + } + + return nil +} +func (t *GraphFollow) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + + if _, err := cw.Write([]byte{163}); err != nil { return err } @@ -88,40 +206,63 @@ func (t *PublicKey) MarshalCBOR(w io.Writer) error { return err } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.publicKey"))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.graph.follow"))); err != nil { return err } - if _, err := cw.WriteString(string("sh.tangled.publicKey")); err != nil { + if _, err := cw.WriteString(string("sh.tangled.graph.follow")); err != nil { return err } - // t.Created (string) (string) - if len("created") > 1000000 { - return xerrors.Errorf("Value in field \"created\" was too long") + // t.Subject (string) (string) + if len("subject") > 1000000 { + return xerrors.Errorf("Value in field \"subject\" was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("created"))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("subject"))); err != nil { return err } - if _, err := cw.WriteString(string("created")); err != nil { + if _, err := cw.WriteString(string("subject")); err != nil { return err } - if len(t.Created) > 1000000 { - return xerrors.Errorf("Value in field t.Created was too long") + if len(t.Subject) > 1000000 { + return xerrors.Errorf("Value in field t.Subject was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Created))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Subject))); err != nil { return err } - if _, err := cw.WriteString(string(t.Created)); err != nil { + if _, err := cw.WriteString(string(t.Subject)); err != nil { + return err + } + + // t.CreatedAt (string) (string) + if len("createdAt") > 1000000 { + return xerrors.Errorf("Value in field \"createdAt\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("createdAt"))); err != nil { + return err + } + if _, err := cw.WriteString(string("createdAt")); err != nil { + return err + } + + if len(t.CreatedAt) > 1000000 { + return xerrors.Errorf("Value in field t.CreatedAt was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.CreatedAt))); err != nil { + return err + } + if _, err := cw.WriteString(string(t.CreatedAt)); err != nil { return err } return nil } -func (t *PublicKey) UnmarshalCBOR(r io.Reader) (err error) { - *t = PublicKey{} +func (t *GraphFollow) UnmarshalCBOR(r io.Reader) (err error) { + *t = GraphFollow{} cr := cbg.NewCborReader(r) @@ -140,12 +281,12 @@ func (t *PublicKey) UnmarshalCBOR(r io.Reader) (err error) { } if extra > cbg.MaxLength { - return fmt.Errorf("PublicKey: map struct too large (%d)", extra) + return fmt.Errorf("GraphFollow: map struct too large (%d)", extra) } n := extra - nameBuf := make([]byte, 7) + nameBuf := make([]byte, 9) for i := uint64(0); i < n; i++ { nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) if err != nil { @@ -161,19 +302,8 @@ func (t *PublicKey) UnmarshalCBOR(r io.Reader) (err error) { } switch string(nameBuf[:nameLen]) { - // t.Key (string) (string) - case "key": - - { - sval, err := cbg.ReadStringWithMax(cr, 1000000) - if err != nil { - return err - } - - t.Key = string(sval) - } - // t.Name (string) (string) - case "name": + // t.LexiconTypeID (string) (string) + case "$type": { sval, err := cbg.ReadStringWithMax(cr, 1000000) @@ -181,10 +311,10 @@ func (t *PublicKey) UnmarshalCBOR(r io.Reader) (err error) { return err } - t.Name = string(sval) + t.LexiconTypeID = string(sval) } - // t.LexiconTypeID (string) (string) - case "$type": + // t.Subject (string) (string) + case "subject": { sval, err := cbg.ReadStringWithMax(cr, 1000000) @@ -192,10 +322,10 @@ func (t *PublicKey) UnmarshalCBOR(r io.Reader) (err error) { return err } - t.LexiconTypeID = string(sval) + t.Subject = string(sval) } - // t.Created (string) (string) - case "created": + // t.CreatedAt (string) (string) + case "createdAt": { sval, err := cbg.ReadStringWithMax(cr, 1000000) @@ -203,7 +333,7 @@ func (t *PublicKey) UnmarshalCBOR(r io.Reader) (err error) { return err } - t.Created = string(sval) + t.CreatedAt = string(sval) } default: @@ -438,7 +568,7 @@ func (t *KnotMember) UnmarshalCBOR(r io.Reader) (err error) { return nil } -func (t *GraphFollow) MarshalCBOR(w io.Writer) error { +func (t *PublicKey) MarshalCBOR(w io.Writer) error { if t == nil { _, err := w.Write(cbg.CborNull) return err @@ -446,79 +576,102 @@ func (t *GraphFollow) MarshalCBOR(w io.Writer) error { cw := cbg.NewCborWriter(w) - if _, err := cw.Write([]byte{163}); err != nil { + if _, err := cw.Write([]byte{164}); err != nil { return err } - // t.LexiconTypeID (string) (string) - if len("$type") > 1000000 { - return xerrors.Errorf("Value in field \"$type\" was too long") + // t.Key (string) (string) + if len("key") > 1000000 { + return xerrors.Errorf("Value in field \"key\" was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("$type"))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("key"))); err != nil { return err } - if _, err := cw.WriteString(string("$type")); err != nil { + if _, err := cw.WriteString(string("key")); err != nil { return err } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.graph.follow"))); err != nil { + if len(t.Key) > 1000000 { + return xerrors.Errorf("Value in field t.Key was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Key))); err != nil { return err } - if _, err := cw.WriteString(string("sh.tangled.graph.follow")); err != nil { + if _, err := cw.WriteString(string(t.Key)); err != nil { return err } - // t.Subject (string) (string) - if len("subject") > 1000000 { - return xerrors.Errorf("Value in field \"subject\" was too long") + // t.Name (string) (string) + if len("name") > 1000000 { + return xerrors.Errorf("Value in field \"name\" was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("subject"))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("name"))); err != nil { return err } - if _, err := cw.WriteString(string("subject")); err != nil { + if _, err := cw.WriteString(string("name")); err != nil { return err } - if len(t.Subject) > 1000000 { - return xerrors.Errorf("Value in field t.Subject was too long") + if len(t.Name) > 1000000 { + return xerrors.Errorf("Value in field t.Name was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Subject))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Name))); err != nil { return err } - if _, err := cw.WriteString(string(t.Subject)); err != nil { + if _, err := cw.WriteString(string(t.Name)); err != nil { return err } - // t.CreatedAt (string) (string) - if len("createdAt") > 1000000 { - return xerrors.Errorf("Value in field \"createdAt\" was too long") + // t.LexiconTypeID (string) (string) + if len("$type") > 1000000 { + return xerrors.Errorf("Value in field \"$type\" was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("createdAt"))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("$type"))); err != nil { return err } - if _, err := cw.WriteString(string("createdAt")); err != nil { + if _, err := cw.WriteString(string("$type")); err != nil { return err } - if len(t.CreatedAt) > 1000000 { - return xerrors.Errorf("Value in field t.CreatedAt was too long") + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.publicKey"))); err != nil { + return err + } + if _, err := cw.WriteString(string("sh.tangled.publicKey")); err != nil { + return err } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.CreatedAt))); err != nil { + // t.Created (string) (string) + if len("created") > 1000000 { + return xerrors.Errorf("Value in field \"created\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("created"))); err != nil { return err } - if _, err := cw.WriteString(string(t.CreatedAt)); err != nil { + if _, err := cw.WriteString(string("created")); err != nil { + return err + } + + if len(t.Created) > 1000000 { + return xerrors.Errorf("Value in field t.Created was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Created))); err != nil { + return err + } + if _, err := cw.WriteString(string(t.Created)); err != nil { return err } return nil } -func (t *GraphFollow) UnmarshalCBOR(r io.Reader) (err error) { - *t = GraphFollow{} +func (t *PublicKey) UnmarshalCBOR(r io.Reader) (err error) { + *t = PublicKey{} cr := cbg.NewCborReader(r) @@ -537,12 +690,12 @@ func (t *GraphFollow) UnmarshalCBOR(r io.Reader) (err error) { } if extra > cbg.MaxLength { - return fmt.Errorf("GraphFollow: map struct too large (%d)", extra) + return fmt.Errorf("PublicKey: map struct too large (%d)", extra) } n := extra - nameBuf := make([]byte, 9) + nameBuf := make([]byte, 7) for i := uint64(0); i < n; i++ { nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) if err != nil { @@ -558,8 +711,8 @@ func (t *GraphFollow) UnmarshalCBOR(r io.Reader) (err error) { } switch string(nameBuf[:nameLen]) { - // t.LexiconTypeID (string) (string) - case "$type": + // t.Key (string) (string) + case "key": { sval, err := cbg.ReadStringWithMax(cr, 1000000) @@ -567,10 +720,10 @@ func (t *GraphFollow) UnmarshalCBOR(r io.Reader) (err error) { return err } - t.LexiconTypeID = string(sval) + t.Key = string(sval) } - // t.Subject (string) (string) - case "subject": + // t.Name (string) (string) + case "name": { sval, err := cbg.ReadStringWithMax(cr, 1000000) @@ -578,10 +731,10 @@ func (t *GraphFollow) UnmarshalCBOR(r io.Reader) (err error) { return err } - t.Subject = string(sval) + t.Name = string(sval) } - // t.CreatedAt (string) (string) - case "createdAt": + // t.LexiconTypeID (string) (string) + case "$type": { sval, err := cbg.ReadStringWithMax(cr, 1000000) @@ -589,7 +742,18 @@ func (t *GraphFollow) UnmarshalCBOR(r io.Reader) (err error) { return err } - t.CreatedAt = string(sval) + t.LexiconTypeID = string(sval) + } + // t.Created (string) (string) + case "created": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Created = string(sval) } default: @@ -602,67 +766,101 @@ func (t *GraphFollow) UnmarshalCBOR(r io.Reader) (err error) { return nil } -func (t *Repo) MarshalCBOR(w io.Writer) error { +func (t *RepoIssueComment) MarshalCBOR(w io.Writer) error { if t == nil { _, err := w.Write(cbg.CborNull) return err } cw := cbg.NewCborWriter(w) - fieldCount := 5 + fieldCount := 7 - if t.AddedAt == nil { + if t.Body == nil { fieldCount-- } - if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(fieldCount))); err != nil { - return err + if t.CommentId == nil { + fieldCount-- } - // t.Knot (string) (string) - if len("knot") > 1000000 { - return xerrors.Errorf("Value in field \"knot\" was too long") + if t.CreatedAt == nil { + fieldCount-- } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("knot"))); err != nil { - return err - } - if _, err := cw.WriteString(string("knot")); err != nil { - return err + if t.Owner == nil { + fieldCount-- } - if len(t.Knot) > 1000000 { - return xerrors.Errorf("Value in field t.Knot was too long") + if t.Repo == nil { + fieldCount-- } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Knot))); err != nil { - return err - } - if _, err := cw.WriteString(string(t.Knot)); err != nil { + if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(fieldCount))); err != nil { return err } - // t.Name (string) (string) - if len("name") > 1000000 { - return xerrors.Errorf("Value in field \"name\" was too long") - } + // t.Body (string) (string) + if t.Body != nil { - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("name"))); err != nil { - return err - } - if _, err := cw.WriteString(string("name")); err != nil { - return err - } + if len("body") > 1000000 { + return xerrors.Errorf("Value in field \"body\" was too long") + } - if len(t.Name) > 1000000 { - return xerrors.Errorf("Value in field t.Name was too long") - } + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("body"))); err != nil { + return err + } + if _, err := cw.WriteString(string("body")); err != nil { + return err + } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Name))); err != nil { - return err + if t.Body == nil { + if _, err := cw.Write(cbg.CborNull); err != nil { + return err + } + } else { + if len(*t.Body) > 1000000 { + return xerrors.Errorf("Value in field t.Body was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.Body))); err != nil { + return err + } + if _, err := cw.WriteString(string(*t.Body)); err != nil { + return err + } + } } - if _, err := cw.WriteString(string(t.Name)); err != nil { - return err + + // t.Repo (string) (string) + if t.Repo != nil { + + if len("repo") > 1000000 { + return xerrors.Errorf("Value in field \"repo\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("repo"))); err != nil { + return err + } + if _, err := cw.WriteString(string("repo")); err != nil { + return err + } + + if t.Repo == nil { + if _, err := cw.Write(cbg.CborNull); err != nil { + return err + } + } else { + if len(*t.Repo) > 1000000 { + return xerrors.Errorf("Value in field t.Repo was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.Repo))); err != nil { + return err + } + if _, err := cw.WriteString(string(*t.Repo)); err != nil { + return err + } + } } // t.LexiconTypeID (string) (string) @@ -677,79 +875,143 @@ func (t *Repo) MarshalCBOR(w io.Writer) error { return err } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.repo"))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.repo.issue.comment"))); err != nil { return err } - if _, err := cw.WriteString(string("sh.tangled.repo")); err != nil { + if _, err := cw.WriteString(string("sh.tangled.repo.issue.comment")); err != nil { return err } - // t.Owner (string) (string) - if len("owner") > 1000000 { - return xerrors.Errorf("Value in field \"owner\" was too long") + // t.Issue (string) (string) + if len("issue") > 1000000 { + return xerrors.Errorf("Value in field \"issue\" was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("owner"))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("issue"))); err != nil { return err } - if _, err := cw.WriteString(string("owner")); err != nil { + if _, err := cw.WriteString(string("issue")); err != nil { return err } - if len(t.Owner) > 1000000 { - return xerrors.Errorf("Value in field t.Owner was too long") + if len(t.Issue) > 1000000 { + return xerrors.Errorf("Value in field t.Issue was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Owner))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Issue))); err != nil { return err } - if _, err := cw.WriteString(string(t.Owner)); err != nil { + if _, err := cw.WriteString(string(t.Issue)); err != nil { return err } - // t.AddedAt (string) (string) - if t.AddedAt != nil { + // t.Owner (string) (string) + if t.Owner != nil { - if len("addedAt") > 1000000 { - return xerrors.Errorf("Value in field \"addedAt\" was too long") + if len("owner") > 1000000 { + return xerrors.Errorf("Value in field \"owner\" was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("addedAt"))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("owner"))); err != nil { return err } - if _, err := cw.WriteString(string("addedAt")); err != nil { + if _, err := cw.WriteString(string("owner")); err != nil { return err } - if t.AddedAt == nil { + if t.Owner == nil { if _, err := cw.Write(cbg.CborNull); err != nil { return err } } else { - if len(*t.AddedAt) > 1000000 { - return xerrors.Errorf("Value in field t.AddedAt was too long") + if len(*t.Owner) > 1000000 { + return xerrors.Errorf("Value in field t.Owner was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.AddedAt))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.Owner))); err != nil { return err } - if _, err := cw.WriteString(string(*t.AddedAt)); err != nil { + if _, err := cw.WriteString(string(*t.Owner)); err != nil { return err } } } - return nil -} -func (t *Repo) UnmarshalCBOR(r io.Reader) (err error) { - *t = Repo{} + // t.CommentId (int64) (int64) + if t.CommentId != nil { - cr := cbg.NewCborReader(r) + if len("commentId") > 1000000 { + return xerrors.Errorf("Value in field \"commentId\" was too long") + } - maj, extra, err := cr.ReadHeader() - if err != nil { - return err - } + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("commentId"))); err != nil { + return err + } + if _, err := cw.WriteString(string("commentId")); err != nil { + return err + } + + if t.CommentId == nil { + if _, err := cw.Write(cbg.CborNull); err != nil { + return err + } + } else { + if *t.CommentId >= 0 { + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(*t.CommentId)); err != nil { + return err + } + } else { + if err := cw.WriteMajorTypeHeader(cbg.MajNegativeInt, uint64(-*t.CommentId-1)); err != nil { + return err + } + } + } + + } + + // t.CreatedAt (string) (string) + if t.CreatedAt != nil { + + if len("createdAt") > 1000000 { + return xerrors.Errorf("Value in field \"createdAt\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("createdAt"))); err != nil { + return err + } + if _, err := cw.WriteString(string("createdAt")); err != nil { + return err + } + + if t.CreatedAt == nil { + if _, err := cw.Write(cbg.CborNull); err != nil { + return err + } + } else { + if len(*t.CreatedAt) > 1000000 { + return xerrors.Errorf("Value in field t.CreatedAt was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.CreatedAt))); err != nil { + return err + } + if _, err := cw.WriteString(string(*t.CreatedAt)); err != nil { + return err + } + } + } + return nil +} + +func (t *RepoIssueComment) UnmarshalCBOR(r io.Reader) (err error) { + *t = RepoIssueComment{} + + cr := cbg.NewCborReader(r) + + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } defer func() { if err == io.EOF { err = io.ErrUnexpectedEOF @@ -761,12 +1023,12 @@ func (t *Repo) UnmarshalCBOR(r io.Reader) (err error) { } if extra > cbg.MaxLength { - return fmt.Errorf("Repo: map struct too large (%d)", extra) + return fmt.Errorf("RepoIssueComment: map struct too large (%d)", extra) } n := extra - nameBuf := make([]byte, 7) + nameBuf := make([]byte, 9) for i := uint64(0); i < n; i++ { nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) if err != nil { @@ -782,27 +1044,47 @@ func (t *Repo) UnmarshalCBOR(r io.Reader) (err error) { } switch string(nameBuf[:nameLen]) { - // t.Knot (string) (string) - case "knot": + // t.Body (string) (string) + case "body": { - sval, err := cbg.ReadStringWithMax(cr, 1000000) + b, err := cr.ReadByte() if err != nil { return err } + if b != cbg.CborNull[0] { + if err := cr.UnreadByte(); err != nil { + return err + } - t.Knot = string(sval) + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Body = (*string)(&sval) + } } - // t.Name (string) (string) - case "name": + // t.Repo (string) (string) + case "repo": { - sval, err := cbg.ReadStringWithMax(cr, 1000000) + b, err := cr.ReadByte() if err != nil { return err } + if b != cbg.CborNull[0] { + if err := cr.UnreadByte(); err != nil { + return err + } - t.Name = string(sval) + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Repo = (*string)(&sval) + } } // t.LexiconTypeID (string) (string) case "$type": @@ -815,19 +1097,264 @@ func (t *Repo) UnmarshalCBOR(r io.Reader) (err error) { t.LexiconTypeID = string(sval) } + // t.Issue (string) (string) + case "issue": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Issue = string(sval) + } // t.Owner (string) (string) case "owner": + { + b, err := cr.ReadByte() + if err != nil { + return err + } + if b != cbg.CborNull[0] { + if err := cr.UnreadByte(); err != nil { + return err + } + + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.Owner = (*string)(&sval) + } + } + // t.CommentId (int64) (int64) + case "commentId": + { + + b, err := cr.ReadByte() + if err != nil { + return err + } + if b != cbg.CborNull[0] { + if err := cr.UnreadByte(); err != nil { + return err + } + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + var extraI int64 + switch maj { + case cbg.MajUnsignedInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 positive overflow") + } + case cbg.MajNegativeInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 negative overflow") + } + extraI = -1 - extraI + default: + return fmt.Errorf("wrong type for int64 field: %d", maj) + } + + t.CommentId = (*int64)(&extraI) + } + } + // t.CreatedAt (string) (string) + case "createdAt": + + { + b, err := cr.ReadByte() + if err != nil { + return err + } + if b != cbg.CborNull[0] { + if err := cr.UnreadByte(); err != nil { + return err + } + + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.CreatedAt = (*string)(&sval) + } + } + + default: + // Field doesn't exist on this type, so ignore it + if err := cbg.ScanForLinks(r, func(cid.Cid) {}); err != nil { + return err + } + } + } + + return nil +} +func (t *RepoIssueState) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + fieldCount := 3 + + if t.State == nil { + fieldCount-- + } + + if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(fieldCount))); err != nil { + return err + } + + // t.LexiconTypeID (string) (string) + if len("$type") > 1000000 { + return xerrors.Errorf("Value in field \"$type\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("$type"))); err != nil { + return err + } + if _, err := cw.WriteString(string("$type")); err != nil { + return err + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.repo.issue.state"))); err != nil { + return err + } + if _, err := cw.WriteString(string("sh.tangled.repo.issue.state")); err != nil { + return err + } + + // t.Issue (string) (string) + if len("issue") > 1000000 { + return xerrors.Errorf("Value in field \"issue\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("issue"))); err != nil { + return err + } + if _, err := cw.WriteString(string("issue")); err != nil { + return err + } + + if len(t.Issue) > 1000000 { + return xerrors.Errorf("Value in field t.Issue was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Issue))); err != nil { + return err + } + if _, err := cw.WriteString(string(t.Issue)); err != nil { + return err + } + + // t.State (string) (string) + if t.State != nil { + + if len("state") > 1000000 { + return xerrors.Errorf("Value in field \"state\" was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("state"))); err != nil { + return err + } + if _, err := cw.WriteString(string("state")); err != nil { + return err + } + + if t.State == nil { + if _, err := cw.Write(cbg.CborNull); err != nil { + return err + } + } else { + if len(*t.State) > 1000000 { + return xerrors.Errorf("Value in field t.State was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.State))); err != nil { + return err + } + if _, err := cw.WriteString(string(*t.State)); err != nil { + return err + } + } + } + return nil +} + +func (t *RepoIssueState) UnmarshalCBOR(r io.Reader) (err error) { + *t = RepoIssueState{} + + cr := cbg.NewCborReader(r) + + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + defer func() { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + }() + + if maj != cbg.MajMap { + return fmt.Errorf("cbor input should be of type map") + } + + if extra > cbg.MaxLength { + return fmt.Errorf("RepoIssueState: map struct too large (%d)", extra) + } + + n := extra + + nameBuf := make([]byte, 5) + for i := uint64(0); i < n; i++ { + nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) + if err != nil { + return err + } + + if !ok { + // Field doesn't exist on this type, so ignore it + if err := cbg.ScanForLinks(cr, func(cid.Cid) {}); err != nil { + return err + } + continue + } + + switch string(nameBuf[:nameLen]) { + // t.LexiconTypeID (string) (string) + case "$type": + + { + sval, err := cbg.ReadStringWithMax(cr, 1000000) + if err != nil { + return err + } + + t.LexiconTypeID = string(sval) + } + // t.Issue (string) (string) + case "issue": + { sval, err := cbg.ReadStringWithMax(cr, 1000000) if err != nil { return err } - t.Owner = string(sval) + t.Issue = string(sval) } - // t.AddedAt (string) (string) - case "addedAt": + // t.State (string) (string) + case "state": { b, err := cr.ReadByte() @@ -844,7 +1371,7 @@ func (t *Repo) UnmarshalCBOR(r io.Reader) (err error) { return err } - t.AddedAt = (*string)(&sval) + t.State = (*string)(&sval) } } @@ -1219,16 +1746,16 @@ func (t *RepoIssue) UnmarshalCBOR(r io.Reader) (err error) { return nil } -func (t *RepoIssueState) MarshalCBOR(w io.Writer) error { +func (t *Repo) MarshalCBOR(w io.Writer) error { if t == nil { _, err := w.Write(cbg.CborNull) return err } cw := cbg.NewCborWriter(w) - fieldCount := 3 + fieldCount := 5 - if t.State == nil { + if t.AddedAt == nil { fieldCount-- } @@ -1236,272 +1763,50 @@ func (t *RepoIssueState) MarshalCBOR(w io.Writer) error { return err } - // t.LexiconTypeID (string) (string) - if len("$type") > 1000000 { - return xerrors.Errorf("Value in field \"$type\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("$type"))); err != nil { - return err - } - if _, err := cw.WriteString(string("$type")); err != nil { - return err - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.repo.issue.state"))); err != nil { - return err - } - if _, err := cw.WriteString(string("sh.tangled.repo.issue.state")); err != nil { - return err - } - - // t.Issue (string) (string) - if len("issue") > 1000000 { - return xerrors.Errorf("Value in field \"issue\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("issue"))); err != nil { - return err - } - if _, err := cw.WriteString(string("issue")); err != nil { - return err - } - - if len(t.Issue) > 1000000 { - return xerrors.Errorf("Value in field t.Issue was too long") + // t.Knot (string) (string) + if len("knot") > 1000000 { + return xerrors.Errorf("Value in field \"knot\" was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Issue))); err != nil { - return err - } - if _, err := cw.WriteString(string(t.Issue)); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("knot"))); err != nil { return err } - - // t.State (string) (string) - if t.State != nil { - - if len("state") > 1000000 { - return xerrors.Errorf("Value in field \"state\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("state"))); err != nil { - return err - } - if _, err := cw.WriteString(string("state")); err != nil { - return err - } - - if t.State == nil { - if _, err := cw.Write(cbg.CborNull); err != nil { - return err - } - } else { - if len(*t.State) > 1000000 { - return xerrors.Errorf("Value in field t.State was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.State))); err != nil { - return err - } - if _, err := cw.WriteString(string(*t.State)); err != nil { - return err - } - } - } - return nil -} - -func (t *RepoIssueState) UnmarshalCBOR(r io.Reader) (err error) { - *t = RepoIssueState{} - - cr := cbg.NewCborReader(r) - - maj, extra, err := cr.ReadHeader() - if err != nil { + if _, err := cw.WriteString(string("knot")); err != nil { return err } - defer func() { - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - }() - - if maj != cbg.MajMap { - return fmt.Errorf("cbor input should be of type map") - } - - if extra > cbg.MaxLength { - return fmt.Errorf("RepoIssueState: map struct too large (%d)", extra) - } - - n := extra - - nameBuf := make([]byte, 5) - for i := uint64(0); i < n; i++ { - nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) - if err != nil { - return err - } - - if !ok { - // Field doesn't exist on this type, so ignore it - if err := cbg.ScanForLinks(cr, func(cid.Cid) {}); err != nil { - return err - } - continue - } - - switch string(nameBuf[:nameLen]) { - // t.LexiconTypeID (string) (string) - case "$type": - - { - sval, err := cbg.ReadStringWithMax(cr, 1000000) - if err != nil { - return err - } - - t.LexiconTypeID = string(sval) - } - // t.Issue (string) (string) - case "issue": - - { - sval, err := cbg.ReadStringWithMax(cr, 1000000) - if err != nil { - return err - } - - t.Issue = string(sval) - } - // t.State (string) (string) - case "state": - - { - b, err := cr.ReadByte() - if err != nil { - return err - } - if b != cbg.CborNull[0] { - if err := cr.UnreadByte(); err != nil { - return err - } - - sval, err := cbg.ReadStringWithMax(cr, 1000000) - if err != nil { - return err - } - - t.State = (*string)(&sval) - } - } - default: - // Field doesn't exist on this type, so ignore it - if err := cbg.ScanForLinks(r, func(cid.Cid) {}); err != nil { - return err - } - } + if len(t.Knot) > 1000000 { + return xerrors.Errorf("Value in field t.Knot was too long") } - return nil -} -func (t *RepoIssueComment) MarshalCBOR(w io.Writer) error { - if t == nil { - _, err := w.Write(cbg.CborNull) + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Knot))); err != nil { return err } - - cw := cbg.NewCborWriter(w) - fieldCount := 7 - - if t.Body == nil { - fieldCount-- - } - - if t.CommentId == nil { - fieldCount-- - } - - if t.CreatedAt == nil { - fieldCount-- - } - - if t.Owner == nil { - fieldCount-- - } - - if t.Repo == nil { - fieldCount-- - } - - if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(fieldCount))); err != nil { + if _, err := cw.WriteString(string(t.Knot)); err != nil { return err } - // t.Body (string) (string) - if t.Body != nil { - - if len("body") > 1000000 { - return xerrors.Errorf("Value in field \"body\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("body"))); err != nil { - return err - } - if _, err := cw.WriteString(string("body")); err != nil { - return err - } - - if t.Body == nil { - if _, err := cw.Write(cbg.CborNull); err != nil { - return err - } - } else { - if len(*t.Body) > 1000000 { - return xerrors.Errorf("Value in field t.Body was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.Body))); err != nil { - return err - } - if _, err := cw.WriteString(string(*t.Body)); err != nil { - return err - } - } - } - - // t.Repo (string) (string) - if t.Repo != nil { - - if len("repo") > 1000000 { - return xerrors.Errorf("Value in field \"repo\" was too long") - } + // t.Name (string) (string) + if len("name") > 1000000 { + return xerrors.Errorf("Value in field \"name\" was too long") + } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("repo"))); err != nil { - return err - } - if _, err := cw.WriteString(string("repo")); err != nil { - return err - } + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("name"))); err != nil { + return err + } + if _, err := cw.WriteString(string("name")); err != nil { + return err + } - if t.Repo == nil { - if _, err := cw.Write(cbg.CborNull); err != nil { - return err - } - } else { - if len(*t.Repo) > 1000000 { - return xerrors.Errorf("Value in field t.Repo was too long") - } + if len(t.Name) > 1000000 { + return xerrors.Errorf("Value in field t.Name was too long") + } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.Repo))); err != nil { - return err - } - if _, err := cw.WriteString(string(*t.Repo)); err != nil { - return err - } - } + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Name))); err != nil { + return err + } + if _, err := cw.WriteString(string(t.Name)); err != nil { + return err } // t.LexiconTypeID (string) (string) @@ -1516,127 +1821,63 @@ func (t *RepoIssueComment) MarshalCBOR(w io.Writer) error { return err } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.repo.issue.comment"))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.repo"))); err != nil { return err } - if _, err := cw.WriteString(string("sh.tangled.repo.issue.comment")); err != nil { + if _, err := cw.WriteString(string("sh.tangled.repo")); err != nil { return err } - // t.Issue (string) (string) - if len("issue") > 1000000 { - return xerrors.Errorf("Value in field \"issue\" was too long") + // t.Owner (string) (string) + if len("owner") > 1000000 { + return xerrors.Errorf("Value in field \"owner\" was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("issue"))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("owner"))); err != nil { return err } - if _, err := cw.WriteString(string("issue")); err != nil { + if _, err := cw.WriteString(string("owner")); err != nil { return err } - if len(t.Issue) > 1000000 { - return xerrors.Errorf("Value in field t.Issue was too long") + if len(t.Owner) > 1000000 { + return xerrors.Errorf("Value in field t.Owner was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Issue))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Owner))); err != nil { return err } - if _, err := cw.WriteString(string(t.Issue)); err != nil { + if _, err := cw.WriteString(string(t.Owner)); err != nil { return err } - // t.Owner (string) (string) - if t.Owner != nil { - - if len("owner") > 1000000 { - return xerrors.Errorf("Value in field \"owner\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("owner"))); err != nil { - return err - } - if _, err := cw.WriteString(string("owner")); err != nil { - return err - } - - if t.Owner == nil { - if _, err := cw.Write(cbg.CborNull); err != nil { - return err - } - } else { - if len(*t.Owner) > 1000000 { - return xerrors.Errorf("Value in field t.Owner was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.Owner))); err != nil { - return err - } - if _, err := cw.WriteString(string(*t.Owner)); err != nil { - return err - } - } - } - - // t.CommentId (int64) (int64) - if t.CommentId != nil { - - if len("commentId") > 1000000 { - return xerrors.Errorf("Value in field \"commentId\" was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("commentId"))); err != nil { - return err - } - if _, err := cw.WriteString(string("commentId")); err != nil { - return err - } - - if t.CommentId == nil { - if _, err := cw.Write(cbg.CborNull); err != nil { - return err - } - } else { - if *t.CommentId >= 0 { - if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(*t.CommentId)); err != nil { - return err - } - } else { - if err := cw.WriteMajorTypeHeader(cbg.MajNegativeInt, uint64(-*t.CommentId-1)); err != nil { - return err - } - } - } - - } - - // t.CreatedAt (string) (string) - if t.CreatedAt != nil { + // t.AddedAt (string) (string) + if t.AddedAt != nil { - if len("createdAt") > 1000000 { - return xerrors.Errorf("Value in field \"createdAt\" was too long") + if len("addedAt") > 1000000 { + return xerrors.Errorf("Value in field \"addedAt\" was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("createdAt"))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("addedAt"))); err != nil { return err } - if _, err := cw.WriteString(string("createdAt")); err != nil { + if _, err := cw.WriteString(string("addedAt")); err != nil { return err } - if t.CreatedAt == nil { + if t.AddedAt == nil { if _, err := cw.Write(cbg.CborNull); err != nil { return err } } else { - if len(*t.CreatedAt) > 1000000 { - return xerrors.Errorf("Value in field t.CreatedAt was too long") + if len(*t.AddedAt) > 1000000 { + return xerrors.Errorf("Value in field t.AddedAt was too long") } - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.CreatedAt))); err != nil { + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.AddedAt))); err != nil { return err } - if _, err := cw.WriteString(string(*t.CreatedAt)); err != nil { + if _, err := cw.WriteString(string(*t.AddedAt)); err != nil { return err } } @@ -1644,8 +1885,8 @@ func (t *RepoIssueComment) MarshalCBOR(w io.Writer) error { return nil } -func (t *RepoIssueComment) UnmarshalCBOR(r io.Reader) (err error) { - *t = RepoIssueComment{} +func (t *Repo) UnmarshalCBOR(r io.Reader) (err error) { + *t = Repo{} cr := cbg.NewCborReader(r) @@ -1664,12 +1905,12 @@ func (t *RepoIssueComment) UnmarshalCBOR(r io.Reader) (err error) { } if extra > cbg.MaxLength { - return fmt.Errorf("RepoIssueComment: map struct too large (%d)", extra) + return fmt.Errorf("Repo: map struct too large (%d)", extra) } n := extra - nameBuf := make([]byte, 9) + nameBuf := make([]byte, 7) for i := uint64(0); i < n; i++ { nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) if err != nil { @@ -1685,47 +1926,27 @@ func (t *RepoIssueComment) UnmarshalCBOR(r io.Reader) (err error) { } switch string(nameBuf[:nameLen]) { - // t.Body (string) (string) - case "body": + // t.Knot (string) (string) + case "knot": { - b, err := cr.ReadByte() + sval, err := cbg.ReadStringWithMax(cr, 1000000) if err != nil { return err } - if b != cbg.CborNull[0] { - if err := cr.UnreadByte(); err != nil { - return err - } - - sval, err := cbg.ReadStringWithMax(cr, 1000000) - if err != nil { - return err - } - t.Body = (*string)(&sval) - } + t.Knot = string(sval) } - // t.Repo (string) (string) - case "repo": + // t.Name (string) (string) + case "name": { - b, err := cr.ReadByte() + sval, err := cbg.ReadStringWithMax(cr, 1000000) if err != nil { return err } - if b != cbg.CborNull[0] { - if err := cr.UnreadByte(); err != nil { - return err - } - - sval, err := cbg.ReadStringWithMax(cr, 1000000) - if err != nil { - return err - } - t.Repo = (*string)(&sval) - } + t.Name = string(sval) } // t.LexiconTypeID (string) (string) case "$type": @@ -1738,76 +1959,19 @@ func (t *RepoIssueComment) UnmarshalCBOR(r io.Reader) (err error) { t.LexiconTypeID = string(sval) } - // t.Issue (string) (string) - case "issue": - - { - sval, err := cbg.ReadStringWithMax(cr, 1000000) - if err != nil { - return err - } - - t.Issue = string(sval) - } // t.Owner (string) (string) case "owner": { - b, err := cr.ReadByte() - if err != nil { - return err - } - if b != cbg.CborNull[0] { - if err := cr.UnreadByte(); err != nil { - return err - } - - sval, err := cbg.ReadStringWithMax(cr, 1000000) - if err != nil { - return err - } - - t.Owner = (*string)(&sval) - } - } - // t.CommentId (int64) (int64) - case "commentId": - { - - b, err := cr.ReadByte() + sval, err := cbg.ReadStringWithMax(cr, 1000000) if err != nil { return err } - if b != cbg.CborNull[0] { - if err := cr.UnreadByte(); err != nil { - return err - } - maj, extra, err := cr.ReadHeader() - if err != nil { - return err - } - var extraI int64 - switch maj { - case cbg.MajUnsignedInt: - extraI = int64(extra) - if extraI < 0 { - return fmt.Errorf("int64 positive overflow") - } - case cbg.MajNegativeInt: - extraI = int64(extra) - if extraI < 0 { - return fmt.Errorf("int64 negative overflow") - } - extraI = -1 - extraI - default: - return fmt.Errorf("wrong type for int64 field: %d", maj) - } - t.CommentId = (*int64)(&extraI) - } + t.Owner = string(sval) } - // t.CreatedAt (string) (string) - case "createdAt": + // t.AddedAt (string) (string) + case "addedAt": { b, err := cr.ReadByte() @@ -1824,7 +1988,7 @@ func (t *RepoIssueComment) UnmarshalCBOR(r io.Reader) (err error) { return err } - t.CreatedAt = (*string)(&sval) + t.AddedAt = (*string)(&sval) } } diff --git a/api/tangled/feedstar.go b/api/tangled/feedstar.go new file mode 100644 index 00000000..e5910aff --- /dev/null +++ b/api/tangled/feedstar.go @@ -0,0 +1,23 @@ +// Code generated by cmd/lexgen (see Makefile's lexgen); DO NOT EDIT. + +package tangled + +// schema: sh.tangled.feed.star + +import ( + "github.com/bluesky-social/indigo/lex/util" +) + +const ( + FeedStarNSID = "sh.tangled.feed.star" +) + +func init() { + util.RegisterType("sh.tangled.feed.star", &FeedStar{}) +} // +// RECORDTYPE: FeedStar +type FeedStar struct { + LexiconTypeID string `json:"$type,const=sh.tangled.feed.star" cborgen:"$type,const=sh.tangled.feed.star"` + CreatedAt string `json:"createdAt" cborgen:"createdAt"` + Subject string `json:"subject" cborgen:"subject"` +} diff --git a/appview/db/db.go b/appview/db/db.go index 8e5862f0..e7311c69 100644 --- a/appview/db/db.go +++ b/appview/db/db.go @@ -112,6 +112,16 @@ func Make(dbPath string) (*DB, error) { next_issue_id integer not null default 1 ); + create table if not exists stars ( + id integer primary key autoincrement, + starred_by_did text not null, + repo_at text not null, + rkey text not null, + created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), + foreign key (repo_at) references repos(at_uri) on delete cascade, + unique(starred_by_did, repo_at) + ); + `) if err != nil { return nil, err diff --git a/appview/db/follow.go b/appview/db/follow.go index 795fc485..0437a110 100644 --- a/appview/db/follow.go +++ b/appview/db/follow.go @@ -9,7 +9,7 @@ type Follow struct { UserDid string SubjectDid string FollowedAt time.Time - RKey string + Rkey string } func AddFollow(e Execer, userDid, subjectDid, rkey string) error { @@ -25,7 +25,7 @@ func GetFollow(e Execer, userDid, subjectDid string) (*Follow, error) { var follow Follow var followedAt string - err := row.Scan(&follow.UserDid, &follow.SubjectDid, &followedAt, &follow.RKey) + err := row.Scan(&follow.UserDid, &follow.SubjectDid, &followedAt, &follow.Rkey) if err != nil { return nil, err } @@ -41,7 +41,7 @@ func GetFollow(e Execer, userDid, subjectDid string) (*Follow, error) { return &follow, nil } -// Get a follow record +// Remove a follow func DeleteFollow(e Execer, userDid, subjectDid string) error { _, err := e.Exec(`delete from follows where user_did = ? and subject_did = ?`, userDid, subjectDid) return err @@ -91,10 +91,15 @@ func GetFollowStatus(e Execer, userDid, subjectDid string) FollowStatus { } } -func GetAllFollows(e Execer) ([]Follow, error) { +func GetAllFollows(e Execer, limit int) ([]Follow, error) { var follows []Follow - rows, err := e.Query(`select user_did, subject_did, followed_at, rkey from follows`) + rows, err := e.Query(` + select user_did, subject_did, followed_at, rkey + from follows + order by followed_at desc + limit ?`, limit, + ) if err != nil { return nil, err } @@ -103,7 +108,7 @@ func GetAllFollows(e Execer) ([]Follow, error) { for rows.Next() { var follow Follow var followedAt string - if err := rows.Scan(&follow.UserDid, &follow.SubjectDid, &followedAt, &follow.RKey); err != nil { + if err := rows.Scan(&follow.UserDid, &follow.SubjectDid, &followedAt, &follow.Rkey); err != nil { return nil, err } diff --git a/appview/db/issues.go b/appview/db/issues.go index 122a9686..c6447330 100644 --- a/appview/db/issues.go +++ b/appview/db/issues.go @@ -3,10 +3,12 @@ package db import ( "database/sql" "time" + + "github.com/bluesky-social/indigo/atproto/syntax" ) type Issue struct { - RepoAt string + RepoAt syntax.ATURI OwnerDid string IssueId int IssueAt string @@ -18,7 +20,7 @@ type Issue struct { type Comment struct { OwnerDid string - RepoAt string + RepoAt syntax.ATURI CommentAt string Issue int CommentId int @@ -65,30 +67,30 @@ func NewIssue(tx *sql.Tx, issue *Issue) error { return nil } -func SetIssueAt(e Execer, repoAt string, issueId int, issueAt string) error { +func SetIssueAt(e Execer, repoAt syntax.ATURI, issueId int, issueAt string) error { _, err := e.Exec(`update issues set issue_at = ? where repo_at = ? and issue_id = ?`, issueAt, repoAt, issueId) return err } -func GetIssueAt(e Execer, repoAt string, issueId int) (string, error) { +func GetIssueAt(e Execer, repoAt syntax.ATURI, issueId int) (string, error) { var issueAt string err := e.QueryRow(`select issue_at from issues where repo_at = ? and issue_id = ?`, repoAt, issueId).Scan(&issueAt) return issueAt, err } -func GetIssueId(e Execer, repoAt string) (int, error) { +func GetIssueId(e Execer, repoAt syntax.ATURI) (int, error) { var issueId int err := e.QueryRow(`select next_issue_id from repo_issue_seqs where repo_at = ?`, repoAt).Scan(&issueId) return issueId - 1, err } -func GetIssueOwnerDid(e Execer, repoAt string, issueId int) (string, error) { +func GetIssueOwnerDid(e Execer, repoAt syntax.ATURI, issueId int) (string, error) { var ownerDid string err := e.QueryRow(`select owner_did from issues where repo_at = ? and issue_id = ?`, repoAt, issueId).Scan(&ownerDid) return ownerDid, err } -func GetIssues(e Execer, repoAt string) ([]Issue, error) { +func GetIssues(e Execer, repoAt syntax.ATURI) ([]Issue, error) { var issues []Issue rows, err := e.Query(`select owner_did, issue_id, created, title, body, open from issues where repo_at = ? order by created desc`, repoAt) @@ -121,7 +123,7 @@ func GetIssues(e Execer, repoAt string) ([]Issue, error) { return issues, nil } -func GetIssue(e Execer, repoAt string, issueId int) (*Issue, error) { +func GetIssue(e Execer, repoAt syntax.ATURI, issueId int) (*Issue, error) { query := `select owner_did, created, title, body, open from issues where repo_at = ? and issue_id = ?` row := e.QueryRow(query, repoAt, issueId) @@ -141,7 +143,7 @@ func GetIssue(e Execer, repoAt string, issueId int) (*Issue, error) { return &issue, nil } -func GetIssueWithComments(e Execer, repoAt string, issueId int) (*Issue, []Comment, error) { +func GetIssueWithComments(e Execer, repoAt syntax.ATURI, issueId int) (*Issue, []Comment, error) { query := `select owner_did, issue_id, created, title, body, open from issues where repo_at = ? and issue_id = ?` row := e.QueryRow(query, repoAt, issueId) @@ -180,7 +182,7 @@ func NewComment(e Execer, comment *Comment) error { return err } -func GetComments(e Execer, repoAt string, issueId int) ([]Comment, error) { +func GetComments(e Execer, repoAt syntax.ATURI, issueId int) ([]Comment, error) { var comments []Comment rows, err := e.Query(`select owner_did, issue_id, comment_id, comment_at, body, created from comments where repo_at = ? and issue_id = ? order by created asc`, repoAt, issueId) @@ -216,12 +218,35 @@ func GetComments(e Execer, repoAt string, issueId int) ([]Comment, error) { return comments, nil } -func CloseIssue(e Execer, repoAt string, issueId int) error { +func CloseIssue(e Execer, repoAt syntax.ATURI, issueId int) error { _, err := e.Exec(`update issues set open = 0 where repo_at = ? and issue_id = ?`, repoAt, issueId) return err } -func ReopenIssue(e Execer, repoAt string, issueId int) error { +func ReopenIssue(e Execer, repoAt syntax.ATURI, issueId int) error { _, err := e.Exec(`update issues set open = 1 where repo_at = ? and issue_id = ?`, repoAt, issueId) return err } + +type IssueCount struct { + Open int + Closed int +} + +func GetIssueCount(e Execer, repoAt syntax.ATURI) (IssueCount, error) { + row := e.QueryRow(` + select + count(case when open = 1 then 1 end) as open_count, + count(case when open = 0 then 1 end) as closed_count + from issues + where repo_at = ?`, + repoAt, + ) + + var count IssueCount + if err := row.Scan(&count.Open, &count.Closed); err != nil { + return IssueCount{0, 0}, err + } + + return count, nil +} diff --git a/appview/db/repos.go b/appview/db/repos.go index 6927614b..c7e75d41 100644 --- a/appview/db/repos.go +++ b/appview/db/repos.go @@ -14,10 +14,17 @@ type Repo struct { AtUri string } -func GetAllRepos(e Execer) ([]Repo, error) { +func GetAllRepos(e Execer, limit int) ([]Repo, error) { var repos []Repo - rows, err := e.Query(`select did, name, knot, rkey, created from repos`) + rows, err := e.Query( + `select did, name, knot, rkey, created + from repos + order by created desc + limit ? + `, + limit, + ) if err != nil { return nil, err } @@ -79,6 +86,21 @@ func GetRepo(e Execer, did, name string) (*Repo, error) { return &repo, nil } +func GetRepoByAtUri(e Execer, atUri string) (*Repo, error) { + var repo Repo + + row := e.QueryRow(`select did, name, knot, created, at_uri from repos where at_uri = ?`, atUri) + + var createdAt string + if err := row.Scan(&repo.Did, &repo.Name, &repo.Knot, &createdAt, &repo.AtUri); err != nil { + return nil, err + } + createdAtTime, _ := time.Parse(time.RFC3339, createdAt) + repo.Created = createdAtTime + + return &repo, nil +} + func AddRepo(e Execer, repo *Repo) error { _, err := e.Exec(`insert into repos (did, name, knot, rkey, at_uri) values (?, ?, ?, ?, ?)`, repo.Did, repo.Name, repo.Knot, repo.Rkey, repo.AtUri) return err @@ -122,6 +144,11 @@ func CollaboratingIn(e Execer, collaborator string) ([]Repo, error) { return repos, nil } +type RepoStats struct { + StarCount int + IssueCount IssueCount +} + func scanRepo(rows *sql.Rows, did, name, knot, rkey *string, created *time.Time) error { var createdAt string if err := rows.Scan(did, name, knot, rkey, &createdAt); err != nil { diff --git a/appview/db/star.go b/appview/db/star.go new file mode 100644 index 00000000..84b04e73 --- /dev/null +++ b/appview/db/star.go @@ -0,0 +1,150 @@ +package db + +import ( + "log" + "time" + + "github.com/bluesky-social/indigo/atproto/syntax" +) + +type Star struct { + StarredByDid string + RepoAt syntax.ATURI + Repo *Repo + Created time.Time + Rkey string +} + +func (star *Star) ResolveRepo(e Execer) error { + if star.Repo != nil { + return nil + } + + repo, err := GetRepoByAtUri(e, star.RepoAt.String()) + if err != nil { + return err + } + + star.Repo = repo + return nil +} + +func AddStar(e Execer, starredByDid string, repoAt syntax.ATURI, rkey string) error { + query := `insert or ignore into stars (starred_by_did, repo_at, rkey) values (?, ?, ?)` + _, err := e.Exec(query, starredByDid, repoAt, rkey) + return err +} + +// Get a star record +func GetStar(e Execer, starredByDid string, repoAt syntax.ATURI) (*Star, error) { + query := ` + select starred_by_did, repo_at, created, rkey + from stars + where starred_by_did = ? and repo_at = ?` + row := e.QueryRow(query, starredByDid, repoAt) + + var star Star + var created string + err := row.Scan(&star.StarredByDid, &star.RepoAt, &created, &star.Rkey) + if err != nil { + return nil, err + } + + createdAtTime, err := time.Parse(time.RFC3339, created) + if err != nil { + log.Println("unable to determine followed at time") + star.Created = time.Now() + } else { + star.Created = createdAtTime + } + + return &star, nil +} + +// Remove a star +func DeleteStar(e Execer, starredByDid string, repoAt syntax.ATURI) error { + _, err := e.Exec(`delete from stars where starred_by_did = ? and repo_at = ?`, starredByDid, repoAt) + return err +} + +func GetStarCount(e Execer, repoAt syntax.ATURI) (int, error) { + stars := 0 + err := e.QueryRow( + `select count(starred_by_did) from stars where repo_at = ?`, repoAt).Scan(&stars) + if err != nil { + return 0, err + } + return stars, nil +} + +func GetStarStatus(e Execer, userDid string, repoAt syntax.ATURI) bool { + if _, err := GetStar(e, userDid, repoAt); err != nil { + return false + } else { + return true + } +} + +func GetAllStars(e Execer, limit int) ([]Star, error) { + var stars []Star + + rows, err := e.Query(` + select + s.starred_by_did, + s.repo_at, + s.rkey, + s.created, + r.did, + r.name, + r.knot, + r.rkey, + r.created, + r.at_uri + from stars s + join repos r on s.repo_at = r.at_uri + `) + + if err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + var star Star + var repo Repo + var starCreatedAt, repoCreatedAt string + + if err := rows.Scan( + &star.StarredByDid, + &star.RepoAt, + &star.Rkey, + &starCreatedAt, + &repo.Did, + &repo.Name, + &repo.Knot, + &repo.Rkey, + &repoCreatedAt, + &repo.AtUri, + ); err != nil { + return nil, err + } + + star.Created, err = time.Parse(time.RFC3339, starCreatedAt) + if err != nil { + star.Created = time.Now() + } + repo.Created, err = time.Parse(time.RFC3339, repoCreatedAt) + if err != nil { + repo.Created = time.Now() + } + star.Repo = &repo + + stars = append(stars, star) + } + + if err := rows.Err(); err != nil { + return nil, err + } + + return stars, nil +} diff --git a/appview/db/timeline.go b/appview/db/timeline.go index 3eed91ea..961db89b 100644 --- a/appview/db/timeline.go +++ b/appview/db/timeline.go @@ -8,18 +8,27 @@ import ( type TimelineEvent struct { *Repo *Follow + *Star EventAt time.Time } +// TODO: this gathers heterogenous events from different sources and aggregates +// them in code; if we did this entirely in sql, we could order and limit and paginate easily func MakeTimeline(e Execer) ([]TimelineEvent, error) { var events []TimelineEvent + limit := 50 - repos, err := GetAllRepos(e) + repos, err := GetAllRepos(e, limit) if err != nil { return nil, err } - follows, err := GetAllFollows(e) + follows, err := GetAllFollows(e, limit) + if err != nil { + return nil, err + } + + stars, err := GetAllStars(e, limit) if err != nil { return nil, err } @@ -27,26 +36,31 @@ func MakeTimeline(e Execer) ([]TimelineEvent, error) { for _, repo := range repos { events = append(events, TimelineEvent{ Repo: &repo, - Follow: nil, EventAt: repo.Created, }) } for _, follow := range follows { events = append(events, TimelineEvent{ - Repo: nil, Follow: &follow, EventAt: follow.FollowedAt, }) } + for _, star := range stars { + events = append(events, TimelineEvent{ + Star: &star, + EventAt: star.Created, + }) + } + sort.Slice(events, func(i, j int) bool { return events[i].EventAt.After(events[j].EventAt) }) // Limit the slice to 100 events - if len(events) > 50 { - events = events[:50] + if len(events) > limit { + events = events[:limit] } return events, nil diff --git a/appview/pages/funcmap.go b/appview/pages/funcmap.go index a331fa92..c0693e99 100644 --- a/appview/pages/funcmap.go +++ b/appview/pages/funcmap.go @@ -106,5 +106,12 @@ func funcMap() template.FuncMap { "markdown": func(text string) template.HTML { return template.HTML(renderMarkdown(text)) }, + "isNil": func(t any) bool { + // returns false for other "zero" values + return t == nil + }, + "not": func(t bool) bool { + return !t + }, } } diff --git a/appview/pages/pages.go b/appview/pages/pages.go index 35a2584c..a183af04 100644 --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -17,6 +17,7 @@ import ( chromahtml "github.com/alecthomas/chroma/v2/formatters/html" "github.com/alecthomas/chroma/v2/lexers" "github.com/alecthomas/chroma/v2/styles" + "github.com/bluesky-social/indigo/atproto/syntax" "github.com/microcosm-cc/bluemonday" "github.com/sotangled/tangled/appview/auth" "github.com/sotangled/tangled/appview/db" @@ -43,11 +44,26 @@ func NewPages() *Pages { name := strings.TrimPrefix(path, "templates/") name = strings.TrimSuffix(name, ".html") - if !strings.HasPrefix(path, "templates/layouts/") { + // add fragments as templates + if strings.HasPrefix(path, "templates/fragments/") { + tmpl, err := template.New(name). + Funcs(funcMap()). + ParseFS(files, path) + if err != nil { + return fmt.Errorf("setting up fragment: %w", err) + } + + templates[name] = tmpl + log.Printf("loaded fragment: %s", name) + } + + // layouts and fragments are applied first + if !strings.HasPrefix(path, "templates/layouts/") && + !strings.HasPrefix(path, "templates/fragments/") { // Add the page template on top of the base tmpl, err := template.New(name). Funcs(funcMap()). - ParseFS(files, "templates/layouts/*.html", path) + ParseFS(files, "templates/layouts/*.html", "templates/fragments/*.html", path) if err != nil { return fmt.Errorf("setting up template: %w", err) } @@ -159,13 +175,35 @@ func (p *Pages) ProfilePage(w io.Writer, params ProfilePageParams) error { return p.execute("user/profile", w, params) } +type FollowFragmentParams struct { + UserDid string + FollowStatus db.FollowStatus +} + +func (p *Pages) FollowFragment(w io.Writer, params FollowFragmentParams) error { + return p.executePlain("fragments/follow", w, params) +} + +type StarFragmentParams struct { + IsStarred bool + RepoAt syntax.ATURI + Stats db.RepoStats +} + +func (p *Pages) StarFragment(w io.Writer, params StarFragmentParams) error { + return p.executePlain("fragments/star", w, params) +} + type RepoInfo struct { Name string OwnerDid string OwnerHandle string Description string Knot string + RepoAt syntax.ATURI SettingsAllowed bool + IsStarred bool + Stats db.RepoStats } func (r RepoInfo) OwnerWithAt() string { @@ -194,6 +232,23 @@ func (r RepoInfo) GetTabs() [][]string { return tabs } +// each tab on a repo could have some metadata: +// +// issues -> number of open issues etc. +// settings -> a warning icon to setup branch protection? idk +// +// we gather these bits of info here, because go templates +// are difficult to program in +func (r RepoInfo) TabMetadata() map[string]any { + meta := make(map[string]any) + + meta["issues"] = r.Stats.IssueCount.Open + + // more stuff? + + return meta +} + type RepoIndexParams struct { LoggedInUser *auth.User RepoInfo RepoInfo diff --git a/appview/pages/templates/fragments/follow.html b/appview/pages/templates/fragments/follow.html new file mode 100644 index 00000000..20481da5 --- /dev/null +++ b/appview/pages/templates/fragments/follow.html @@ -0,0 +1,17 @@ +{{ define "fragments/follow" }} + +{{ end }} diff --git a/appview/pages/templates/fragments/star.html b/appview/pages/templates/fragments/star.html new file mode 100644 index 00000000..b14acc96 --- /dev/null +++ b/appview/pages/templates/fragments/star.html @@ -0,0 +1,36 @@ +{{ define "fragments/star" }} + + +{{ end }} + diff --git a/appview/pages/templates/layouts/repobase.html b/appview/pages/templates/layouts/repobase.html index 770e2e46..9c3866b8 100644 --- a/appview/pages/templates/layouts/repobase.html +++ b/appview/pages/templates/layouts/repobase.html @@ -2,27 +2,32 @@ {{ define "content" }}
+

- {{ .RepoInfo.OwnerWithAt }} - / - {{ .RepoInfo.Name }} + {{ .RepoInfo.OwnerWithAt }} + / + {{ .RepoInfo.Name }}

- - {{ if .RepoInfo.Description }} - {{ .RepoInfo.Description }} - {{ else }} - this repo has no description - {{ end }} - + {{ template "fragments/star" .RepoInfo }} +
+ + {{ if .RepoInfo.Description }} + {{ .RepoInfo.Description }} + {{ else }} + this repo has no description + {{ end }} +