package tag import ( "encoding/json" "net/url" "reflect" "github.com/mleku/transit/chk" "github.com/mleku/transit/errorf" "github.com/mleku/transit/id" "github.com/mleku/transit/pubkey" ) type T [2]any type U struct{ T } // MarshalTypes is a list of types of tags that are valid. Note that this is all of the types that are // valid. Additional tag keys may be added in the future but should never be taken away. var MarshalTypes = map[string]reflect.Type{ "root": reflect.TypeOf(&id.I{}), "parent": reflect.TypeOf(&id.I{}), "event": reflect.TypeOf(&id.I{}), "user": reflect.TypeOf(&pubkey.P{}), "hashtag": reflect.TypeOf(""), "url": reflect.TypeOf(""), } // UnmarshalTypes is a list of types of tags that are valid. Note that this is all of the types that are // valid. Additional tag keys may be added in the future but should never be taken away. var UnmarshalTypes = map[string]func() any{ "root": func() any { return id.I{} }, "parent": func() any { return id.I{} }, "event": func() any { return id.I{} }, "user": func() any { return pubkey.P{} }, "hashtag": func() any { return "" }, "url": func() any { return "" }, } func New(key string, val any) (t *U) { return &U{T{key, val}} } // GetId returns the correctly typed value or the ok value is false. func (t *U) GetId() (i *id.I, ok bool) { i, ok = t.T[1].(*id.I); return } // GetPubkey returns the correctly typed value or the ok value is false. func (t *U) GetPubkey() (p *pubkey.P, ok bool) { p, ok = t.T[1].(*pubkey.P) return } // GetString returns the correctly typed value or the ok value is false. func (t *U) GetString() (s string, ok bool) { s, ok = t.T[1].(string) return } func (t *U) MarshalJSON() (b []byte, err error) { // first value must be a string k, ok := t.T[0].(string) if !ok { err = errorf.E("key must be a string, got '%v'", k) return } // second value must be one of MarshalTypes. var found bool for _, v := range MarshalTypes { if v == reflect.TypeOf(t.T[1]) { found = true break } } // url is a special case because it needs to be rendered using a stringer as it lacks a // json.MarshalJSON implementation. if k == "url" { var ur string if ur, ok = t.T[1].(string); !ok { err = errorf.E("url key does not have a value of type string: %v", reflect.TypeOf(t.T[1])) return } if _, err = url.Parse(ur); chk.E(err) { return } t.T[1] = ur } if k == "hashtag" { var ht string if ht, ok = t.T[1].(string); !ok { err = errorf.E("hashtag key does not have a value of type []byte: %v", reflect.TypeOf(t.T[1])) return } t.T[1] = ht } if !found { err = errorf.E("tag value is unknown type key: %s value: %v", t.T[0], reflect.TypeOf(t.T[1])) return } // marshal array into tag tt := t.T if b, err = json.Marshal(&tt); chk.E(err) { return } return } func (t *U) UnmarshalJSON(b []byte) (err error) { var kv []any if err = json.Unmarshal(b, &kv); chk.E(err) { return } // we only do the above to get the key name var ok bool var k string if k, ok = kv[0].(string); !ok { err = errorf.E("key must be a string, got '%v'", k) return } t.T[0] = k var vt func() any if vt, ok = UnmarshalTypes[k]; !ok { err = errorf.E("unknown key type '%s'", k) return } tt := vt() var vs string if vs, ok = kv[1].(string); !ok { err = errorf.E("value must be a string, got '%v'", k) return } // decode in accordance with the key switch ttt := tt.(type) { case id.I: vs = `"` + vs + `"` if err = ttt.UnmarshalJSON([]byte(vs)); chk.E(err) { return } t.T[1] = &ttt case pubkey.P: vs = `"` + vs + `"` if err = ttt.UnmarshalJSON([]byte(vs)); chk.E(err) { return } t.T[1] = &ttt case string: t.T[1] = vs case *url.URL: if ttt, err = url.Parse(vs); chk.E(err) { return } t.T[1] = vs } return }