332 lines
8.0 KiB
Go
332 lines
8.0 KiB
Go
package twt_avro
|
|
|
|
import (
|
|
"io"
|
|
"time"
|
|
|
|
"github.com/hamba/avro"
|
|
"go.yarn.social/lextwt"
|
|
"go.yarn.social/types"
|
|
)
|
|
|
|
const (
|
|
keySubject = "social.yarn.lextwt.subject"
|
|
keyCode = "social.yarn.lextwt.code"
|
|
keyMention = "social.yarn.lextwt.mention"
|
|
keyHashtag = "social.yarn.lextwt.hashtag"
|
|
keyLink = "social.yarn.lextwt.link"
|
|
keyBangmention = "social.yarn.lextwt.bangmention"
|
|
keyLinesep = "social.yarn.lextwt.linesep"
|
|
keyComment = "social.yarn.lextwt.comment"
|
|
keyText = "social.yarn.lextwt.text"
|
|
keyTwt = "social.yarn.lextwt.twt"
|
|
keyTwter = "social.yarn.lextwt.twter"
|
|
keyRegistry = "social.yarn.lextwt.registry"
|
|
keyFeed = "social.yarn.lextwt.feed"
|
|
|
|
)
|
|
|
|
func Register() {
|
|
avro.Register(keyText, "")
|
|
avro.Register(keySubject, Subject{})
|
|
avro.Register(keyCode, Code{})
|
|
avro.Register(keyMention, Mention{})
|
|
avro.Register(keyHashtag, Hashtag{})
|
|
avro.Register(keyLink, Link{})
|
|
avro.Register(keyBangmention, Bangmention{})
|
|
avro.Register(keyLinesep, Linesep{})
|
|
avro.Register(keyComment, Comment{})
|
|
avro.Register(keyTwt, Twt{})
|
|
avro.Register(keyTwter, Twter{})
|
|
avro.Register(keyRegistry, Registry{})
|
|
avro.Register(keyFeed, Feed{})
|
|
|
|
}
|
|
|
|
func Elem(o any) any {
|
|
key := "unknown"
|
|
switch o.(type) {
|
|
case Subject:
|
|
key = keySubject
|
|
case Code:
|
|
key = keyCode
|
|
case Mention:
|
|
key = keyMention
|
|
case Hashtag:
|
|
key = keyHashtag
|
|
case Link:
|
|
key = keyLink
|
|
case Bangmention:
|
|
key = keyBangmention
|
|
case Linesep:
|
|
key = keyLinesep
|
|
case Comment:
|
|
key = keyComment
|
|
case string:
|
|
return o
|
|
}
|
|
|
|
return map[string]any{key: o}
|
|
}
|
|
|
|
func Msg(items ...any) []any {
|
|
return items
|
|
}
|
|
|
|
func FromTwt(twt types.Twt) Twt {
|
|
return FromLextwt(twt.(*lextwt.Twt))
|
|
}
|
|
|
|
func FromLextwt(twt *lextwt.Twt) Twt {
|
|
if twt == nil {
|
|
return Twt{}
|
|
}
|
|
|
|
ts := twt.Created()
|
|
zone, offset := ts.Zone()
|
|
l := Twt{
|
|
Nick: twt.Twter().Nick,
|
|
URI: twt.Twter().URI,
|
|
Created: ts.UnixMilli(),
|
|
CreatedOffset: offset,
|
|
CreatedZone: zone,
|
|
}
|
|
|
|
for _, e := range twt.Elems() {
|
|
if e == nil {
|
|
continue
|
|
}
|
|
|
|
if e == lextwt.LineSeparator {
|
|
l.Msg = append(l.Msg, Elem(Linesep{}))
|
|
continue
|
|
}
|
|
|
|
switch e := e.(type) {
|
|
case *lextwt.Subject:
|
|
l.Msg = append(l.Msg, Elem(Subject{Subject: e.Subject(), Tag: e.Tag().Text(), Target: e.Tag().Target()}))
|
|
case *lextwt.Code:
|
|
l.Msg = append(l.Msg, Elem(Code{Code: e.Text(), Codetype: int(e.CodeType())}))
|
|
case *lextwt.Mention:
|
|
l.Msg = append(l.Msg, Elem(Mention{Name: e.Name(), Domain: e.Domain(), Target: e.Target()}))
|
|
case *lextwt.Tag:
|
|
l.Msg = append(l.Msg, Elem(Hashtag{Tag: e.Text(), Target: e.Target()}))
|
|
case *lextwt.Link:
|
|
l.Msg = append(l.Msg, Elem(Link{LinkType: int(e.LinkType()), Text: e.Text(), Target: e.Target(), Title: e.Title()}))
|
|
case *lextwt.BangMention:
|
|
l.Msg = append(l.Msg, Elem(Bangmention{Name: e.Name(), Target: e.Target()}))
|
|
case *lextwt.Comment:
|
|
l.Msg = append(l.Msg, Elem(Comment{Comment: e.Text(), Key: e.Key(), Value: e.Value()}))
|
|
case *lextwt.Text:
|
|
l.Msg = append(l.Msg, Elem(e.Literal()))
|
|
}
|
|
}
|
|
|
|
return l
|
|
}
|
|
|
|
func (l Twt) ToTwt() types.Twt {
|
|
return l.ToLextwt()
|
|
}
|
|
|
|
func (lx Twt) ToLextwt() *lextwt.Twt {
|
|
twter := types.Twter{
|
|
Nick: lx.Nick,
|
|
URI: lx.URI,
|
|
}
|
|
|
|
ts := time.UnixMilli(lx.Created)
|
|
|
|
if tz := time.FixedZone(lx.CreatedZone, lx.CreatedOffset); tz != nil {
|
|
ts = ts.In(tz)
|
|
}
|
|
dt := lextwt.NewDateTime(ts, ts.Format(time.RFC3339))
|
|
|
|
elems := make([]lextwt.Elem, 0, len(lx.Msg))
|
|
|
|
for _, e := range lx.Msg {
|
|
|
|
switch e := e.(type) {
|
|
case map[string]any:
|
|
if text, ok := e["string"].(string); ok {
|
|
elems = append(elems, lextwt.NewText(text))
|
|
}
|
|
|
|
if e, ok := e[keySubject].(map[string]any); ok {
|
|
subject := read[string](e, "subject")
|
|
tag := read[string](e, "tag")
|
|
target := read[string](e, "target")
|
|
|
|
if subject == "" {
|
|
elems = append(elems, lextwt.NewSubjectTag(tag, target))
|
|
} else {
|
|
elems = append(elems, lextwt.NewSubject(subject))
|
|
}
|
|
}
|
|
|
|
if m, ok := e[keyCode].(map[string]any); ok {
|
|
code := read[string](m, "code")
|
|
codeType := lextwt.CodeType(read[int8](m, "codetype"))
|
|
elems = append(elems, lextwt.NewCode(code, codeType))
|
|
}
|
|
|
|
if e, ok := e[keyMention].(map[string]any); ok {
|
|
name := read[string](e, "name")
|
|
target := read[string](e, "target")
|
|
elems = append(elems, lextwt.NewMention(name, target))
|
|
}
|
|
|
|
if e, ok := e[keyHashtag].(map[string]any); ok {
|
|
tag := read[string](e, "tag")
|
|
target := read[string](e, "target")
|
|
elems = append(elems, lextwt.NewTag(tag, target))
|
|
}
|
|
|
|
if e, ok := e[keyLink].(map[string]any); ok {
|
|
text := read[string](e, "text")
|
|
target := read[string](e, "target")
|
|
linkType := lextwt.LinkType(read[int](e, "linkType"))
|
|
elems = append(elems, lextwt.NewLink(text, target, linkType))
|
|
}
|
|
|
|
if e, ok := e[keyBangmention].(map[string]any); ok {
|
|
name := read[string](e, "name")
|
|
target := read[string](e, "target")
|
|
elems = append(elems, lextwt.NewBangMention(name, target))
|
|
}
|
|
|
|
if e, ok := e[keyComment].(map[string]any); ok {
|
|
comment := read[string](e, "comment")
|
|
key := read[string](e, "key")
|
|
value := read[string](e, "value")
|
|
|
|
if key != "" {
|
|
elems = append(elems, lextwt.NewCommentValue(comment, key, value))
|
|
} else {
|
|
elems = append(elems, lextwt.NewComment(comment))
|
|
}
|
|
}
|
|
|
|
if _, ok := e[keyLinesep].(map[string]any); ok {
|
|
elems = append(elems, lextwt.LineSeparator)
|
|
}
|
|
}
|
|
}
|
|
|
|
return lextwt.NewTwt(twter, dt, elems...)
|
|
}
|
|
|
|
func read[T any](m map[string]any, k string) T {
|
|
val, ok := m[k].(T)
|
|
if !ok {
|
|
var zero T
|
|
return zero
|
|
}
|
|
return val
|
|
}
|
|
|
|
type TwtRegistry interface {
|
|
Twters() []*types.Twter
|
|
Preamble() lextwt.Comments
|
|
Twts() types.Twts
|
|
WriteTo(w io.Writer) (int64, error)
|
|
}
|
|
|
|
func EncodeRegistry(r TwtRegistry) ([]byte, error) {
|
|
out := Registry{}
|
|
|
|
for _, comment := range r.Preamble() {
|
|
if comment.Key() != "" {
|
|
out.Preamble = append(out.Preamble, Comment{Key: comment.Key(), Value: comment.Value()})
|
|
continue
|
|
}
|
|
out.Preamble = append(out.Preamble, Comment{Comment: comment.Text()})
|
|
}
|
|
|
|
for _, twt := range r.Twts() {
|
|
out.Twts = append(out.Twts, FromTwt(twt))
|
|
}
|
|
|
|
return out.Marshal()
|
|
}
|
|
|
|
func DecodeRegistry(b []byte) (TwtRegistry, error) {
|
|
var out Registry
|
|
if err := out.Unmarshal(b); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
preamble := make(lextwt.Comments, 0, len(out.Preamble))
|
|
for _, comment := range out.Preamble {
|
|
if comment.Key != "" {
|
|
preamble = append(preamble, lextwt.NewCommentValue(comment.Comment, comment.Key, comment.Value))
|
|
} else {
|
|
preamble = append(preamble, lextwt.NewComment(comment.Comment))
|
|
}
|
|
}
|
|
|
|
twts := make(types.Twts, 0, len(out.Twts))
|
|
for _, twt := range out.Twts {
|
|
twts = append(twts, twt.ToTwt())
|
|
}
|
|
|
|
return lextwt.NewTwtRegistry(preamble, twts), nil
|
|
}
|
|
|
|
type TwtFeed interface {
|
|
Twter() *types.Twter
|
|
Preamble() lextwt.Comments
|
|
Twts() types.Twts
|
|
WriteTo(w io.Writer) (int64, error)
|
|
}
|
|
|
|
|
|
func EncodeFeed(r TwtFeed) ([]byte, error) {
|
|
out := Feed{}
|
|
|
|
out.Twter.Nick = r.Twter().Nick
|
|
out.Twter.URI = r.Twter().URI
|
|
|
|
for _, comment := range r.Preamble() {
|
|
if comment.Key() != "" {
|
|
out.Preamble = append(out.Preamble, Comment{Key: comment.Key(), Value: comment.Value()})
|
|
continue
|
|
}
|
|
out.Preamble = append(out.Preamble, Comment{Comment: comment.Text()})
|
|
}
|
|
|
|
for _, twt := range r.Twts() {
|
|
out.Twts = append(out.Twts, FromTwt(twt))
|
|
}
|
|
|
|
return out.Marshal()
|
|
}
|
|
|
|
func DecodeFeed(b []byte) (TwtFeed, error) {
|
|
var out Feed
|
|
if err := out.Unmarshal(b); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
twter := types.Twter{
|
|
Nick: out.Twter.Nick,
|
|
URI: out.Twter.URI,
|
|
}
|
|
|
|
preamble := make(lextwt.Comments, 0, len(out.Preamble))
|
|
for _, comment := range out.Preamble {
|
|
if comment.Key != "" {
|
|
preamble = append(preamble, lextwt.NewCommentValue(comment.Comment, comment.Key, comment.Value))
|
|
} else {
|
|
preamble = append(preamble, lextwt.NewComment(comment.Comment))
|
|
}
|
|
}
|
|
|
|
twts := make(types.Twts, 0, len(out.Twts))
|
|
for _, twt := range out.Twts {
|
|
twts = append(twts, twt.ToTwt())
|
|
}
|
|
|
|
return lextwt.NewTwtFile(twter, preamble, twts), nil
|
|
}
|