package twt_fbs import ( "io" "time" flatbuffers "github.com/google/flatbuffers/go" "go.yarn.social/lextwt" "go.yarn.social/types" fbs "go.sour.is/lextwt-encoding/twt-fbs/lextwt" ) type TwtRegistry interface { Twters() []*types.Twter Preamble() lextwt.Comments Twts() types.Twts WriteTo(w io.Writer) (int64, error) } func EncodeTwt(twt types.Twt) []byte { lx, ok := twt.(*lextwt.Twt) if !ok { return nil } return EncodeLextwt(lx) } func EncodeLextwt(twt *lextwt.Twt) []byte { b := flatbuffers.NewBuilder(0) b.Finish(createTwt(b, twt)) return b.FinishedBytes() } func EncodeRegistry(lis TwtRegistry) []byte { b := flatbuffers.NewBuilder(len(lis.Twts())) b.Finish(createRegistry(b, lis)) return b.FinishedBytes() } func createRegistry(b *flatbuffers.Builder, r TwtRegistry) flatbuffers.UOffsetT { preamble := make([]flatbuffers.UOffsetT, len(r.Preamble())) twters := make([]flatbuffers.UOffsetT, len(r.Twters())) twts := make([]flatbuffers.UOffsetT, len(r.Twts())) for i, twt := range r.Twts() { if twt, ok := twt.(*lextwt.Twt); ok { twts[i] = createTwt(b, twt) } } fbs.TwtRegistryStartTwtsVector(b, len(twts)) for _, offset := range twts { b.PrependUOffsetT(offset) } oTwts := b.EndVector(len(twts)) for _, twter := range r.Twters() { twters = append(twters, createTwter(b, twter)) } fbs.TwtRegistryStartTwtersVector(b, len(twters)) for _, offset := range twters { b.PrependUOffsetT(offset) } oTwters := b.EndVector(len(r.Twters())) for _, comment := range r.Preamble() { preamble = append(preamble, createComment(b, comment)) } fbs.TwtRegistryStartPreambleVector(b, len(preamble)) for _, offset := range preamble { b.PrependUOffsetT(offset) } oPreamble := b.EndVector(len(preamble)) fbs.TwtRegistryStart(b) fbs.TwtRegistryAddTwts(b, oTwts) fbs.TwtRegistryAddTwters(b, oTwters) fbs.TwtRegistryAddPreamble(b, oPreamble) return fbs.TwtRegistryEnd(b) } func createTwt(b *flatbuffers.Builder, twt *lextwt.Twt) flatbuffers.UOffsetT { twter := twt.Twter() oTwter := createTwter(b, &twter) oCreated := createDateTime(b, twt.Created()) oElems := createMsg(b, twt.Elems()) fbs.TwtStart(b) fbs.TwtAddTwter(b, oTwter) fbs.TwtAddCreated(b, oCreated) fbs.TwtAddMsg(b, oElems) return fbs.TwtEnd(b) } func createTwter(b *flatbuffers.Builder, twter *types.Twter) flatbuffers.UOffsetT { nick := b.CreateString(twter.Nick) uri := b.CreateString(twter.URI) fbs.TwterStart(b) fbs.TwterAddNick(b, nick) fbs.TwterAddUri(b, uri) return fbs.TwterEnd(b) } func createDateTime(b *flatbuffers.Builder, ts time.Time) flatbuffers.UOffsetT { _, offset := ts.Zone() fbs.DateTimeStart(b) fbs.DateTimeAddTimestamp(b, ts.UnixMilli()) fbs.DateTimeAddOffset(b, int16(offset)) return fbs.DateTimeEnd(b) } func createMsg(b *flatbuffers.Builder, msgs []lextwt.Elem) flatbuffers.UOffsetT { offsets := make([]flatbuffers.UOffsetT, len(msgs)) for i, msg := range msgs { offsets[i] = createElem(b, msg) } fbs.TwtStartMsgVector(b, len(msgs)) for _, offset := range offsets { b.PrependUOffsetT(offset) } return b.EndVector(len(msgs)) } func createElem(b *flatbuffers.Builder, elem lextwt.Elem) flatbuffers.UOffsetT { if elem == lextwt.LineSeparator { offset := createLineSeparator(b) fbs.MsgStart(b) fbs.MsgAddElemType(b, fbs.ElemLineSeparator) fbs.MsgAddElem(b, offset) return fbs.MsgEnd(b) } var oElem flatbuffers.UOffsetT var oType fbs.Elem switch e := elem.(type) { case *lextwt.BangMention: oType = fbs.ElemBangMention oElem = createBangMention(b, e) case *lextwt.Code: oType = fbs.ElemCode oElem = createCode(b, e) case *lextwt.Comment: oType = fbs.ElemComment oElem = createComment(b, e) case *lextwt.Link: oType = fbs.ElemLink oElem = createLink(b, e) case *lextwt.Mention: oType = fbs.ElemMention oElem = createMention(b, e) case *lextwt.Subject: oType = fbs.ElemSubject oElem = createSubject(b, e) case *lextwt.Tag: oType = fbs.ElemTag oElem = createTag(b, e) case *lextwt.Text: oType = fbs.ElemText oElem = createText(b, e) default: fbs.MsgStart(b) fbs.MsgAddElemType(b, fbs.ElemNONE) return fbs.MsgEnd(b) } fbs.MsgStart(b) fbs.MsgAddElemType(b, oType) fbs.MsgAddElem(b, oElem) return fbs.MsgEnd(b) } func createLineSeparator(b *flatbuffers.Builder) flatbuffers.UOffsetT { fbs.LineSeparatorStart(b) return fbs.LineSeparatorEnd(b) } func createBangMention(b *flatbuffers.Builder, mention *lextwt.BangMention) flatbuffers.UOffsetT { name := b.CreateString(mention.Name()) target := b.CreateString(mention.Target()) fbs.BangMentionStart(b) fbs.BangMentionAddName(b, name) fbs.BangMentionAddTarget(b, target) return fbs.BangMentionEnd(b) } func createCode(b *flatbuffers.Builder, code *lextwt.Code) flatbuffers.UOffsetT { typ := fbs.CodeType(code.CodeType()) value := b.CreateString(code.Text()) fbs.CodeStart(b) fbs.CodeAddType(b, typ) fbs.CodeAddCode(b, value) return fbs.CodeEnd(b) } func createComment(b *flatbuffers.Builder, comment *lextwt.Comment) flatbuffers.UOffsetT { text := b.CreateString(comment.Text()) var key, value flatbuffers.UOffsetT if comment.Key() != "" { key = b.CreateString(comment.Key()) value = b.CreateString(comment.Value()) } fbs.CommentStart(b) fbs.CommentAddComment(b, text) if comment.Key() != "" { fbs.CommentAddKey(b, key) fbs.CommentAddValue(b, value) } return fbs.CommentEnd(b) } func createLink(b *flatbuffers.Builder, link *lextwt.Link) flatbuffers.UOffsetT { linkType := fbs.LinkType(link.LinkType()) alt := b.CreateString(link.Text()) target := b.CreateString(link.Target()) title := b.CreateString(link.Title()) fbs.LinkStart(b) fbs.LinkAddType(b, linkType) fbs.LinkAddAlt(b, alt) fbs.LinkAddTarget(b, target) fbs.LinkAddTitle(b, title) return fbs.LinkEnd(b) } func createMention(b *flatbuffers.Builder, mention *lextwt.Mention) flatbuffers.UOffsetT { name := b.CreateString(mention.Name()) target := b.CreateString(mention.Target()) fbs.MentionStart(b) fbs.MentionAddName(b, name) fbs.MentionAddTarget(b, target) return fbs.MentionEnd(b) } func createSubject(b *flatbuffers.Builder, subject *lextwt.Subject) flatbuffers.UOffsetT { var oTag flatbuffers.UOffsetT if tag, ok := subject.Tag().(*lextwt.Tag); ok && tag != nil { oTag = createTag(b, tag) } oSubject := b.CreateString(subject.Subject()) fbs.SubjectStart(b) fbs.SubjectAddSubject(b, oSubject) if oTag != 0 { fbs.SubjectAddTag(b, oTag) } return fbs.SubjectEnd(b) } func createTag(b *flatbuffers.Builder, tag *lextwt.Tag) flatbuffers.UOffsetT { text := b.CreateString(tag.Text()) target := b.CreateString(tag.Target()) fbs.TagStart(b) fbs.TagAddTag(b, text) fbs.TagAddTarget(b, target) return fbs.TagEnd(b) } func createText(b *flatbuffers.Builder, text *lextwt.Text) flatbuffers.UOffsetT { literal := b.CreateString(text.Literal()) fbs.TextStart(b) fbs.TextAddText(b, literal) return fbs.TextEnd(b) } func DecodeTwt(in []byte) (types.Twt, error) { return DecodeLextwt(in) } func DecodeLextwt(in []byte) (*lextwt.Twt, error) { o := fbs.GetRootAsTwt(in, 0) return fromTwt(o), nil } func fromTwt(o *fbs.Twt) *lextwt.Twt { elems := make([]lextwt.Elem, o.MsgLength()) for i := 0; i < o.MsgLength(); i++ { var m fbs.Msg if o.Msg(&m, i) { elems[len(elems)-i-1] = fromMsg(&m) } } return lextwt.NewTwt( *fromTwter(o.Twter(nil)), fromDateTime(o.Created(nil)), elems..., ) } func DecodeRegistry(in []byte) (TwtRegistry, error) { root := fbs.GetRootAsTwtRegistry(in, 0) return fromRegistry(root), nil } func fromRegistry(r *fbs.TwtRegistry) TwtRegistry { twts := make([]types.Twt, r.TwtsLength()) for i := 0; i < r.TwtsLength(); i++ { twt := &fbs.Twt{} r.Twts(twt, i) twts[len(twts)-i-1] = fromTwt(twt) } twters := make([]*types.Twter, r.TwtersLength()) for i := 0; i < r.TwtersLength(); i++ { twter := &fbs.Twter{} if r.Twters(twter, i) { twters[len(twters)-i-1] = fromTwter(twter) } } preamble := make(lextwt.Comments, r.PreambleLength()) for i := 0; i < r.PreambleLength(); i++ { comment := &fbs.Comment{} if r.Preamble(comment, i) { preamble[len(preamble)-i-1] = fromComment(comment) } } return lextwt.NewTwtRegistry(twters, preamble, twts) } func fromDateTime(o *fbs.DateTime) *lextwt.DateTime { if o == nil { return nil } t := time.UnixMilli(o.Timestamp()) t.Add(time.Duration(o.Offset()) * time.Second) return lextwt.NewDateTime(t, t.Format(time.RFC3339)) } func fromTwter(o *fbs.Twter) *types.Twter { t := &types.Twter{} if o != nil { t.Nick = string(o.Nick()) t.URI = string(o.Uri()) } return t } func fromMsg(o *fbs.Msg) lextwt.Elem { elem := &flatbuffers.Table{} if o.Elem(elem) { switch o.ElemType() { case fbs.ElemText: return from(elem, fromText) case fbs.ElemSubject: return from(elem, fromSubject) case fbs.ElemCode: return from(elem, fromCode) case fbs.ElemMention: return from(elem, fromMention) case fbs.ElemLink: return from(elem, fromLink) case fbs.ElemComment: return from(elem, fromComment) case fbs.ElemBangMention: return from(elem, fromBangMention) case fbs.ElemTag: return from(elem, fromTag) case fbs.ElemLineSeparator: return lextwt.LineSeparator } } return nil } type PA[T any] interface { Init([]byte, flatbuffers.UOffsetT) *T } func from[T, A any, U PA[A]](tbl *flatbuffers.Table, fn func(U) T) T { var elem U = new(A) elem.Init(tbl.Bytes, tbl.Pos) return fn(elem) } func fromText(o *fbs.Text) *lextwt.Text { if o == nil { return nil } return lextwt.NewText(string(o.Text())) } func fromLink(o *fbs.Link) *lextwt.Link { if o == nil { return nil } if o.Type() == fbs.LinkTypeMedia { return lextwt.NewMedia(string(o.Alt()), string(o.Target()), string(o.Title())) } return lextwt.NewLink(string(o.Alt()), string(o.Target()), lextwt.LinkType(o.Type())) } func fromComment(o *fbs.Comment) *lextwt.Comment { if o == nil { return nil } if len(o.Key()) > 0 { return lextwt.NewCommentValue(string(o.Comment()), string(o.Key()), string(o.Value())) } return lextwt.NewComment(string(o.Comment())) } func fromTag(o *fbs.Tag) *lextwt.Tag { if o == nil { return nil } return lextwt.NewTag(string(o.Tag()), string(o.Target())) } func fromSubject(o *fbs.Subject) *lextwt.Subject { if o == nil { return nil } if tag := o.Tag(nil); tag != nil { return lextwt.NewSubjectTag(string(tag.Tag()), string(tag.Target())) } return lextwt.NewSubject(string(o.Subject())) } func fromCode(o *fbs.Code) *lextwt.Code { if o == nil { return nil } return lextwt.NewCode(string(o.Code()), lextwt.CodeType(o.Type())) } func fromMention(o *fbs.Mention) *lextwt.Mention { if o == nil { return nil } return lextwt.NewMention(string(o.Name()), string(o.Target())) } func fromBangMention(o *fbs.BangMention) *lextwt.BangMention { if o == nil { return nil } return lextwt.NewBangMention(string(o.Name()), string(o.Target())) }