package twt_pb import ( "io" "iter" "slices" "time" "go.yarn.social/lextwt" "go.yarn.social/types" "google.golang.org/protobuf/proto" ) type twtRegistry interface { Twters() []*types.Twter Preamble() lextwt.Comments Twts() types.Twts WriteTo(w io.Writer) (int64, error) } func EncodeRegistry(r twtRegistry) ([]byte, error) { o := &TwtRegistry{} o.FromLextwt(r) return proto.Marshal(o) } func DecodeRegistry(b []byte) (twtRegistry, error) { o := &TwtRegistry{} err := proto.Unmarshal(b, o) if err != nil { return nil, err } return o.ToLextwt(), nil } func (r *TwtRegistry) FromLextwt(twt twtRegistry) error { preamble := twt.Preamble() r.Preamble = apply( slices.All(preamble), make([]*Comment, len(preamble)), fromComment) twts := twt.Twts() r.Twts = apply( slices.All(twts), make([]*Twt, len(twts)), fromTwt) return nil } func (r *TwtRegistry) ToLextwt() twtRegistry { return lextwt.NewTwtRegistry( apply(slices.All(r.Preamble), make(lextwt.Comments, len(r.Preamble)), func(c *Comment) *lextwt.Comment { return c.ToLextwt() }), apply(slices.All(r.Twts), make(types.Twts, len(r.Twts)), func(c *Twt) types.Twt { return c.ToLextwt() }), ) } type twtFeed interface { Twter() *types.Twter Preamble() lextwt.Comments Twts() types.Twts WriteTo(w io.Writer) (int64, error) } func EncodeFeed(r twtFeed) ([]byte, error) { o := &TwtFeed{} o.FromLextwt(r) return proto.Marshal(o) } func DecodeFeed(b []byte) (twtFeed, error) { o := &TwtFeed{} err := proto.Unmarshal(b, o) if err != nil { return nil, err } return o.ToLextwt(), nil } func (r *TwtFeed) FromLextwt(twt twtFeed) error { r.Twter = fromTwter(twt.Twter()) preamble := twt.Preamble() r.Preamble = apply( slices.All(preamble), make([]*Comment, len(preamble)), fromComment) twts := twt.Twts() r.Twts = apply( slices.All(twts), make([]*Twt, len(twts)), fromTwt) return nil } func (r *TwtFeed) ToLextwt() twtFeed { return lextwt.NewTwtFile( *r.Twter.ToLextwt(), apply(slices.All(r.Preamble), make(lextwt.Comments, len(r.Preamble)), func(c *Comment) *lextwt.Comment { return c.ToLextwt() }), apply(slices.All(r.Twts), make(types.Twts, len(r.Twts)), func(c *Twt) types.Twt { return c.ToLextwt() }), ) } func apply[U, V any](u iter.Seq2[int, U], v []V, fn func(U) V) []V { for i, u := range u { v[i] = fn(u) } return v } func (e *Twter) FromLextwt(twt *types.Twter) { e.Nick = twt.Nick e.Uri = twt.URI } func fromTwter(twt *types.Twter) *Twter { return &Twter{ Nick: twt.Nick, Uri: twt.URI, } } func (e *Twter) ToLextwt() *types.Twter { return &types.Twter{ Nick: e.Nick, URI: e.Uri, } } func (e *Twt) FromLextwt(twt *lextwt.Twt) { twter := twt.Twter() e.Twter.FromLextwt(&twter) e.Created = fromDateTime(twt.Created()) e.Msg = apply( slices.All(twt.Elems()), make([]*Elem, len(twt.Elems())), fromElem, ) } func fromTwt(twt types.Twt) *Twt { twter := twt.Twter() lx, ok := twt.(*lextwt.Twt) if !ok { return &Twt{} } msg := lx.Elems() return &Twt{ Twter: fromTwter(&twter), Created: fromDateTime(twt.Created()), Msg: apply( slices.All(msg), make([]*Elem, len(msg)), fromElem, ), } } func (e *Twt) ToLextwt() *lextwt.Twt { return lextwt.NewTwt( *e.Twter.ToLextwt(), e.Created.ToLextwt(), apply( slices.All(e.Msg), make([]lextwt.Elem, len(e.Msg)), func(e *Elem) lextwt.Elem { return e.ToLextwt() }, )..., ) } func fromDateTime(t time.Time) *DateTime { zone, offset := t.Zone() return &DateTime{ Timestamp: t.UnixMicro(), Offset: int32(offset), Zone: zone, } } func (c *DateTime) ToLextwt() *lextwt.DateTime { dt := time.UnixMicro(c.Timestamp) dt = dt.In(time.FixedZone(c.Zone, int(c.Offset))) return lextwt.NewDateTime(dt, dt.Format(time.RFC3339)) } func fromElem(e lextwt.Elem) *Elem { if e == lextwt.LineSeparator { return &Elem{Elem: &Elem_Lineseparator{fromLineSeparator()}} } switch e := e.(type) { case *lextwt.BangMention: return &Elem{Elem: &Elem_Bangmention{fromBangMention(e)}} case *lextwt.Code: return &Elem{Elem: &Elem_Code{fromCode(e)}} case *lextwt.Comment: return &Elem{Elem: &Elem_Comment{fromComment(e)}} case *lextwt.Mention: return &Elem{Elem: &Elem_Mention{fromMention(e)}} case *lextwt.Subject: return &Elem{Elem: &Elem_Subject{fromSubject(e)}} case *lextwt.Tag: return &Elem{Elem: &Elem_Tag{fromTag(e)}} case *lextwt.Link: return &Elem{Elem: &Elem_Link{fromLink(e)}} case *lextwt.Text: return &Elem{Elem: &Elem_Text{fromText(e)}} } return &Elem{} } func (c *Elem) ToLextwt() lextwt.Elem { switch e := c.Elem.(type) { case *Elem_Bangmention: return e.Bangmention.ToLextwt() case *Elem_Code: return e.Code.ToLextwt() case *Elem_Comment: return e.Comment.ToLextwt() case *Elem_Lineseparator: return e.Lineseparator.ToLextwt() case *Elem_Link: return e.Link.ToLextwt() case *Elem_Mention: return e.Mention.ToLextwt() case *Elem_Subject: return e.Subject.ToLextwt() case *Elem_Tag: return e.Tag.ToLextwt() case *Elem_Text: return e.Text.ToLextwt() } return nil } func fromCode(c *lextwt.Code) *Code { return &Code{ Type: Code_Type(c.CodeType()), Code: c.Text(), } } func (c *Code) ToLextwt() *lextwt.Code { return lextwt.NewCode(c.Code, lextwt.CodeType(c.Type)) } func fromComment(c *lextwt.Comment) *Comment { return &Comment{ Comment: c.Text(), Key: c.Key(), Value: c.Value(), } } func (c *Comment) ToLextwt() *lextwt.Comment { if c.Key != "" { return lextwt.NewCommentValue(c.Comment, c.Key, c.Value) } return lextwt.NewComment(c.Comment) } func fromBangMention(c *lextwt.BangMention) *BangMention { return &BangMention{ Name: c.Name(), Target: c.Target(), } } func (c *BangMention) ToLextwt() *lextwt.BangMention { return lextwt.NewBangMention(c.Name, c.Target) } func fromMention(c *lextwt.Mention) *Mention { return &Mention{ Name: c.Name(), Target: c.Target(), } } func (c *Mention) ToLextwt() *lextwt.Mention { return lextwt.NewMention(c.Name, c.Target) } func fromTag(c *lextwt.Tag) *Tag { return &Tag{ Tag: c.Text(), Target: c.Target(), } } func (c *Tag) ToLextwt() *lextwt.Tag { return lextwt.NewTag(c.Tag, c.Target) } func fromLineSeparator() *LineSeparator { return &LineSeparator{} } func (c *LineSeparator) ToLextwt() lextwt.Elem { return lextwt.LineSeparator } func fromLink(c *lextwt.Link) *Link { return &Link{ Type: Link_Type(c.LinkType()), Alt: c.Text(), Target: c.Target(), Title: c.Title(), } } func (c *Link) ToLextwt() *lextwt.Link { if c.Type == Link_Type(lextwt.LinkMedia) { return lextwt.NewMedia(c.Alt, c.Target, c.Title) } return lextwt.NewLink(c.Alt, c.Target, lextwt.LinkType(c.Type)) } func fromSubject(c *lextwt.Subject) *Subject { if tag, ok := c.Tag().(*lextwt.Tag); ok { return &Subject{ Tag: fromTag(tag), } } return &Subject{ Subject: c.Text(), } } func (c *Subject) ToLextwt() *lextwt.Subject { if c.Tag != nil { return lextwt.NewSubjectTag(c.Tag.Tag, c.Tag.Target) } return lextwt.NewSubject(c.Subject) } func fromText(c *lextwt.Text) *Text { return &Text{ Text: c.Literal(), } } func (c *Text) ToLextwt() *lextwt.Text { return lextwt.NewText(c.Text) }