Compare commits

..

27 Commits

Author SHA1 Message Date
Charles Iliya Krempeaux 4e1137d17e upate dependencies 2024-08-08 20:40:45 -07:00
Charles Iliya Krempeaux 04cb2bbcce mstdn/ent.Application 2024-08-08 16:49:58 -07:00
Charles Iliya Krempeaux efc6351ccb go.* 2024-08-08 12:56:35 -07:00
Charles Iliya Krempeaux e38c886e3c README.md 2024-08-08 12:55:14 -07:00
Charles Iliya Krempeaux 82ebbc3c7e mstdn/ent.AccountHolder 2024-08-08 12:51:21 -07:00
Charles Iliya Krempeaux 7469ffdaeb mstdn/ent.Report 2024-08-08 12:50:09 -07:00
Charles Iliya Krempeaux d28393b1dc mstdn/ent.Application 2024-08-08 12:49:22 -07:00
Charles Iliya Krempeaux 91a4c8f6af mstdn/ent.Poll 2024-08-08 12:46:59 -07:00
Charles Iliya Krempeaux 4e25b5277e mstdn/ent.Role 2024-08-08 12:46:06 -07:00
Charles Iliya Krempeaux c70db317e0 mstdn/ent.Field 2024-08-08 12:27:55 -07:00
Charles Iliya Krempeaux 837edc6382 mstdn/ent.Account 2024-08-08 12:22:10 -07:00
Charles Iliya Krempeaux 5b28f7c14e mstdn/ent.Status 2024-08-08 11:24:05 -07:00
Charles Iliya Krempeaux 11de44e7dc mstdn/ent.CustomEmoji 2024-08-08 11:22:53 -07:00
Charles Iliya Krempeaux 422f33eb2c mstdn/ent.Poll 2024-08-08 09:07:30 -07:00
Charles Iliya Krempeaux 271f092397 mstdn/ent.Status 2024-08-08 09:04:20 -07:00
Charles Iliya Krempeaux e0d46bb7a6 mstdn/ent.Status 2024-08-08 08:59:00 -07:00
Charles Iliya Krempeaux 8da8d72789 mstdn/ent.Status.MarshalJSON() 2024-08-07 15:29:32 -07:00
Charles Iliya Krempeaux d8b920bae5 mstdn/ent.Status.MarshalJSON() 2024-08-07 13:06:16 -07:00
Charles Iliya Krempeaux 8556552b0f removed debug code 2024-08-07 10:58:31 -07:00
Charles Iliya Krempeaux bd0f5addf3 removed debug code 2024-08-07 10:55:47 -07:00
Charles Iliya Krempeaux 5b08301d9b /api/v1/streaming/public/local 2024-08-07 10:47:47 -07:00
Charles Iliya Krempeaux b03ab6e76d /api/v1/streaming/public/local 2024-08-07 08:09:22 -07:00
Charles Iliya Krempeaux 4daf030d7e /api/v1/streaming/public/local 2024-08-07 08:05:46 -07:00
Charles Iliya Krempeaux e380da4d0c made it so Application "vapid_key" is not mandatory anymore 2024-08-07 07:50:07 -07:00
Charles Iliya Krempeaux df08062999 correction 2024-08-06 16:11:52 -07:00
Charles Iliya Krempeaux 217a26bd82 go.* 2024-08-06 15:52:25 -07:00
Charles Iliya Krempeaux 5043c16726 /api/v1/streaming/public/local 2024-08-06 15:49:22 -07:00
26 changed files with 874 additions and 1125 deletions

View File

@ -0,0 +1,130 @@
package local
import (
"encoding/json"
"github.com/reiver/go-httpsse"
"github.com/reiver/go-opt"
)
// Client represenrs a client for interacting with the "/api/v1/streaming/public/local" API end-point.
//
// That API end-point will return a series of events.
//
// Client will let you iterate through those events.
type Client = httpsse.Client
type internalClient struct {
sseclient httpsse.Client
data []string
err error
}
var _ Client = &internalClient{}
func (receiver *internalClient) Close() error {
if nil == receiver {
return errNilReceiver
}
var sseclient httpsse.Client = receiver.sseclient
if nil == sseclient {
return errNilHTTPSSEClient
}
return sseclient.Close()
}
func (receiver *internalClient) Decode(dst interface{}) error {
if nil == dst {
return errNilDestination
}
var event *Event
{
var casted bool
event, casted = dst.(*Event)
if !casted {
}
}
if len(receiver.data) <= 0 {
}
var datum string
{
datum, receiver.data = receiver.data[0], receiver.data[1:]
}
if len(datum) <= 0 {
}
switch datum[0] {
case '0','1','2','3','4','5','6','7','8','9':
event.Name="delete"
event.Status.ID = opt.Something(datum)
default:
event.Name="update"
var bytes []byte = []byte(datum)
{
err := json.Unmarshal(bytes, &(event.Status))
if nil != err {
return err
}
}
}
return nil
}
func (receiver *internalClient) Err() error {
if nil == receiver {
return errNilReceiver
}
if nil != receiver.err {
return receiver.err
}
return receiver.sseclient.Err()
}
func (receiver *internalClient) Next() bool {
if nil == receiver {
return false
}
if 0 < len(receiver.data) {
return true
}
more := receiver.sseclient.Next()
if !more {
return false
}
var event httpsse.Event
{
err := receiver.sseclient.Decode(&event)
if nil != err {
receiver.err = err
return false
}
}
receiver.data = append(receiver.data, event.EventData()...)
//@TODO: what if event.EventData() returns an empty array?
// which could happen.
return true
}

View File

@ -0,0 +1,46 @@
package local
import (
"net/http"
"net/url"
"github.com/reiver/go-httpsse"
)
func DialHost(host string) (Client, error) {
var urloc = url.URL{
Scheme:"https",
Host:host,
Path:Path,
}
sseclient, err := httpsse.DialURL(urloc.String())
if nil != err {
return nil, err
}
return &internalClient{
sseclient:sseclient,
}, nil
}
func Dial(req *http.Request) (Client, error) {
if nil == req {
return nil, errNilHTTPRequest
}
if nil == req.URL {
req.URL = new(url.URL)
}
req.URL.Path = Path
sseclient, err :=httpsse.Dial(req)
if nil != err {
return nil, err
}
return &internalClient{
sseclient:sseclient,
}, nil
}

View File

@ -0,0 +1,12 @@
package local
import (
"github.com/reiver/go-erorr"
)
const (
errNilDestination = erorr.Error("mstdn: nil destination")
errNilHTTPRequest = erorr.Error("mstdn: nil http-request")
errNilHTTPSSEClient = erorr.Error("mstdn: nil http-sse-client")
errNilReceiver = erorr.Error("mstdn: nil receiver")
)

View File

@ -0,0 +1,24 @@
package local
import (
"github.com/reiver/go-mstdn/ent"
)
// The "/api/v1/streaming/public/local" API end-points returns a series of events.
// Event is used to represent one of those events.
//
// And event has a 'name'.
// Currently the possible names are:
//
// • "delete"
// • "update"
//
// And event also has a status.
//
// If the event is an "update", then all of (or most of) the 'status' should be filled in.
//
// If the event is a "delete", then only the 'ID' will be filled in.
type Event struct {
Name string
Status ent.Status
}

View File

@ -0,0 +1,3 @@
package local
const Path string = "/api/v1/streaming/public/local"

3
ent/README.md 100644
View File

@ -0,0 +1,3 @@
Use "github.com/reiver/go-json" rather than the Go built-in "encoding/json".
(Need it so `omitempty` works with more types.)

View File

@ -1,394 +1,42 @@
package ent package ent
import ( import (
"encoding/json"
"github.com/reiver/go-erorr"
"github.com/reiver/go-nul" "github.com/reiver/go-nul"
"github.com/reiver/go-jsonint" "github.com/reiver/go-jsonint"
"github.com/reiver/go-opt" "github.com/reiver/go-opt"
) )
var _ json.Marshaler = Account{}
// Account represents a Mastodon API "Account". // Account represents a Mastodon API "Account".
// //
// See: // See:
// https://docs.joinmastodon.org/entities/Account/ // https://docs.joinmastodon.org/entities/Account/
type Account struct { type Account struct {
ID opt.Optional[string] `json:"id"` ID opt.Optional[string] `json:"id"`
UserName opt.Optional[string] `json:"username"` UserName opt.Optional[string] `json:"username,omitempty"`
Acct opt.Optional[string] `json:"acct"` Acct opt.Optional[string] `json:"acct,omitempty"`
URL opt.Optional[string] `json:"url"` URL opt.Optional[string] `json:"url,omitempty"`
URI opt.Optional[string] `json:"uri"` // currently undocumented, but is included in in account JSON returned from the Mastodon API on mastodon.social. URI opt.Optional[string] `json:"uri,omitempty"` // currently undocumented, but is included in in account JSON returned from the Mastodon API on mastodon.social.
DisplayName opt.Optional[string] `json:"display_name"` DisplayName opt.Optional[string] `json:"display_name,omitempty"`
Note opt.Optional[string] `json:"note"` Note opt.Optional[string] `json:"note,omitempty"`
Avatar opt.Optional[string] `json:"avatar"` Avatar opt.Optional[string] `json:"avatar,omitempty"`
AvatarStatic opt.Optional[string] `json:"avatar_static"` AvatarStatic opt.Optional[string] `json:"avatar_static,omitempty"`
Header opt.Optional[string] `json:"header"` Header opt.Optional[string] `json:"header,omitempty"`
HeaderStatic opt.Optional[string] `json:"header_static"` HeaderStatic opt.Optional[string] `json:"header_static,omitempty"`
Fields []Field `json:"fields"` Fields []Field `json:"fields,omitempty"`
Emojis []CustomEmoji `json:"emojis"` Emojis []CustomEmoji `json:"emojis,omitempty"`
Locked opt.Optional[bool] `json:"locked"` Locked opt.Optional[bool] `json:"locked,omitempty"`
Bot opt.Optional[bool] `json:"bot"` Bot opt.Optional[bool] `json:"bot,omitempty"`
Group opt.Optional[bool] `json:"group"` Group opt.Optional[bool] `json:"group,omitempty"`
Discoverable nul.Nullable[bool] `json:"discoverable"` Discoverable nul.Nullable[bool] `json:"discoverable,omitempty"`
NoIndex nul.Nullable[bool] `json:"noindex"` NoIndex nul.Nullable[bool] `json:"noindex,omitempty"`
Moved nul.Nullable[AccountHolder] `json:"moved"` Moved nul.Nullable[AccountHolder] `json:"moved,omitempty"`
Suspended opt.Optional[bool] `json:"suspended"` Suspended opt.Optional[bool] `json:"suspended,omitempty"`
Limited opt.Optional[bool] `json:"limited"` Limited opt.Optional[bool] `json:"limited,omitempty"`
CreatedAt opt.Optional[string] `json:"created_at"` CreatedAt opt.Optional[string] `json:"created_at,omitempty"`
LastStatusAt nul.Nullable[string] `json:"last_status_at"` LastStatusAt nul.Nullable[string] `json:"last_status_at,omitempty"`
StatusesCount opt.Optional[jsonint.Int] `json:"statuses_count"` StatusesCount opt.Optional[jsonint.Int] `json:"statuses_count,omitempty"`
FollowersCount opt.Optional[jsonint.Int] `json:"followers_count"` FollowersCount opt.Optional[jsonint.Int] `json:"followers_count,omitempty"`
FollowingCount opt.Optional[jsonint.Int] `json:"following_count"` FollowingCount opt.Optional[jsonint.Int] `json:"following_count,omitempty"`
Roles []Role `json:"roles"` Roles []Role `json:"roles,omitempty"`
MuteExpiresAt nul.Nullable[string] `json:"mute_expires_at"` MuteExpiresAt nul.Nullable[string] `json:"mute_expires_at,omitempty"`
}
func (receiver Account) MarshalJSON() ([]byte, error) {
var buffer []byte
buffer = append(buffer, "{"...)
{
buffer = append(buffer, `"id":`...)
marshaled, err := json.Marshal(receiver.ID)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.ID as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"username":`...)
marshaled, err := json.Marshal(receiver.UserName)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.UserName as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"acct":`...)
marshaled, err := json.Marshal(receiver.Acct)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.Acct as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"url":`...)
marshaled, err := json.Marshal(receiver.URL)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.URL as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"uri":`...)
marshaled, err := json.Marshal(receiver.URI)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.URI as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"display_name":`...)
marshaled, err := json.Marshal(receiver.DisplayName)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.DisplayName as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"note":`...)
marshaled, err := json.Marshal(receiver.Note)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.Note as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"avatar":`...)
marshaled, err := json.Marshal(receiver.Avatar)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.Avatar as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"avatar_static":`...)
marshaled, err := json.Marshal(receiver.AvatarStatic)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.AvatarStatic as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"header":`...)
marshaled, err := json.Marshal(receiver.Header)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.Header as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"header_static":`...)
marshaled, err := json.Marshal(receiver.HeaderStatic)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.HeaderStatic as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"fields":`...)
var src interface{} = receiver.Fields
if nil == receiver.Fields {
src = []Field{}
}
marshaled, err := json.Marshal(src)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.Fields as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"emojis":`...)
var src interface{} = receiver.Emojis
if nil == receiver.Emojis {
src = []CustomEmoji{}
}
marshaled, err := json.Marshal(src)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.Emojis as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"locked":`...)
marshaled, err := json.Marshal(receiver.Locked)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.Locked as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"bot":`...)
marshaled, err := json.Marshal(receiver.Bot)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.Bot as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"group":`...)
marshaled, err := json.Marshal(receiver.Group)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.Group as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"discoverable":`...)
marshaled, err := json.Marshal(receiver.Discoverable)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.Discoverable as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
if nul.Nothing[bool]() != receiver.NoIndex {
buffer = append(buffer, `,"noindex":`...)
marshaled, err := json.Marshal(receiver.NoIndex)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.NoIndex as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
}
{
if nul.Nothing[AccountHolder]() != receiver.Moved {
buffer = append(buffer, `,"moved":`...)
marshaled, err := json.Marshal(receiver.Moved)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.Moved as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
}
{
if opt.Nothing[bool]() != receiver.Suspended {
buffer = append(buffer, `,"suspended":`...)
marshaled, err := json.Marshal(receiver.Suspended)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.Suspended as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
}
{
if opt.Nothing[bool]() != receiver.Limited {
buffer = append(buffer, `,"limited":`...)
marshaled, err := json.Marshal(receiver.Limited)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.Limited as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
}
{
buffer = append(buffer, `,"created_at":`...)
marshaled, err := json.Marshal(receiver.CreatedAt)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.CreatedAt as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"last_status_at":`...)
marshaled, err := json.Marshal(receiver.LastStatusAt)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.LastStatusAt as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"statuses_count":`...)
marshaled, err := json.Marshal(receiver.StatusesCount)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.StatusesCount as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"followers_count":`...)
marshaled, err := json.Marshal(receiver.FollowersCount)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.FollowersCount as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"following_count":`...)
marshaled, err := json.Marshal(receiver.FollowingCount)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.FollowingCount as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
if nil != receiver.Roles {
buffer = append(buffer, `,"roles":`...)
marshaled, err := json.Marshal(receiver.Roles)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.Roles as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
}
{
if nul.Nothing[string]() != receiver.MuteExpiresAt {
buffer = append(buffer, `,"mute_expires_at":`...)
marshaled, err := json.Marshal(receiver.MuteExpiresAt)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.Account.MuteExpiresAt as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
}
buffer = append(buffer, "}"...)
return buffer, nil
} }

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@ package ent
import ( import (
"testing" "testing"
"encoding/json" "github.com/reiver/go-json"
) )
func TestAccountHolder_MarshalJSON(t *testing.T) { func TestAccountHolder_MarshalJSON(t *testing.T) {

View File

@ -1,140 +1,18 @@
package ent package ent
import ( import (
"encoding/json"
"github.com/reiver/go-erorr"
"github.com/reiver/go-opt" "github.com/reiver/go-opt"
"github.com/reiver/go-nul" "github.com/reiver/go-nul"
) )
var _ json.Marshaler = Application{}
const (
errCannotMashalApplicationAsJSONNoName = erorr.Error("cannot marshal ent.Application to JSON — no name set")
errCannotMashalApplicationAsJSONNoVapidKey = erorr.Error("cannot marshal ent.Application to JSON — no vapid_key set")
)
// Appication represents a Mastodon API "Appication". // Appication represents a Mastodon API "Appication".
// //
// See: // See:
// https://docs.joinmastodon.org/entities/Application/ // https://docs.joinmastodon.org/entities/Application/
type Application struct { type Application struct {
Name opt.Optional[string] `json:"name"` Name opt.Optional[string] `json:"name,omitempty"`
WebSite nul.Nullable[string] `json:"website"` // optional — field has JSON null value in JSON if not set WebSite nul.Nullable[string] `json:"website,omitempty"` // optional — field has JSON null value in JSON if not set
VapidKey opt.Optional[string] `json:"vapid_key"` VapidKey opt.Optional[string] `json:"vapid_key,omitempty"` // optional — field not included in JSON if not set
ClientID opt.Optional[string] `json:"client_id"` // optional — field not included in JSON if not set ClientID opt.Optional[string] `json:"client_id,omitempty"` // optional — field not included in JSON if not set
ClientSecret opt.Optional[string] `json:"client_secret"` // optional — field not included in JSON if not set ClientSecret opt.Optional[string] `json:"client_secret,omitempty"` // optional — field not included in JSON if not set
}
func (receiver Application) MarshalJSON() ([]byte, error) {
var array [512]byte
var buffer []byte = array[0:0]
buffer = append(buffer, "{"...)
{
val, found := receiver.Name.Get()
if !found {
return nil, errCannotMashalApplicationAsJSONNoName
}
buffer = append(buffer, `"name":`...)
{
marshaled, err := json.Marshal(val)
if nil != err {
return nil, erorr.Errorf("ent: could not marshal ent.Application.Name as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
}
{
buffer = append(buffer, `,"website":`...)
switch receiver.WebSite {
case nul.Nothing[string](), nul.Null[string]():
buffer = append(buffer, `null`...)
default:
website, found := receiver.WebSite.Get()
if !found {
return nil, erorr.Error("ent: could not marshal ent.Application.WebSite as JSON: internal error")
}
marshaled, err := json.Marshal(website)
if nil != err {
return nil, erorr.Errorf("ent: could not marshal ent.Application.WebSite as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
}
{
val, found := receiver.VapidKey.Get()
if !found {
return nil, errCannotMashalApplicationAsJSONNoVapidKey
}
buffer = append(buffer, `,"vapid_key":`...)
{
marshaled, err := json.Marshal(val)
if nil != err {
return nil, erorr.Errorf("ent: could not marshal ent.Application.VapidKey as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
}
{
switch receiver.ClientID {
case opt.Nothing[string]():
// Nothing here.
default:
clientID, found := receiver.ClientID.Get()
if !found {
return nil, erorr.Error("ent: could not marshal ent.Application.ClientID as JSON: internal error")
}
buffer = append(buffer, `,"client_id":`...)
marshaled, err := json.Marshal(clientID)
if nil != err {
return nil, erorr.Errorf("ent: could not marshal ent.Application.ClientID as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
}
{
switch receiver.ClientSecret {
case opt.Nothing[string]():
// Nothing here.
default:
clientSecret, found := receiver.ClientSecret.Get()
if !found {
return nil, erorr.Error("ent: could not marshal ent.Application.ClientSecret as JSON: internal error")
}
buffer = append(buffer, `,"client_secret":`...)
marshaled, err := json.Marshal(clientSecret)
if nil != err {
return nil, erorr.Errorf("ent: could not marshal ent.Application.ClientSecret as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
}
buffer = append(buffer, "}"...)
return buffer, nil
} }

View File

@ -3,8 +3,7 @@ package ent_test
import ( import (
"testing" "testing"
"encoding/json" "github.com/reiver/go-json"
"github.com/reiver/go-jsonpp" "github.com/reiver/go-jsonpp"
"github.com/reiver/go-nul" "github.com/reiver/go-nul"
"github.com/reiver/go-opt" "github.com/reiver/go-opt"
@ -18,9 +17,17 @@ func TestApplication_MarshalJSON(t *testing.T) {
Application ent.Application Application ent.Application
Expected string Expected string
}{ }{
{
Application: ent.Application{},
Expected: "{}",
},
{ {
Application: ent.Application{ Application: ent.Application{
Name: opt.Something("acme app"), Name: opt.Something("acme app"),
WebSite: nul.Null[string](),
VapidKey: opt.Something("BHgNMADAUjgYgM4PZtHkY3yTQRYD-ibS_qrWYg2KPBRidocowKcOc-8YpyItumamkGph2bk8FuryT4-p3Eymwz8"), VapidKey: opt.Something("BHgNMADAUjgYgM4PZtHkY3yTQRYD-ibS_qrWYg2KPBRidocowKcOc-8YpyItumamkGph2bk8FuryT4-p3Eymwz8"),
}, },
Expected: Expected:

View File

@ -1,91 +1,17 @@
package ent package ent
import ( import (
"encoding/json"
"github.com/reiver/go-erorr"
"github.com/reiver/go-opt" "github.com/reiver/go-opt"
) )
var _ json.Marshaler = CustomEmoji{}
// CustomEmoji represents a Mastodon API "CustomEmoji". // CustomEmoji represents a Mastodon API "CustomEmoji".
// //
// See: // See:
// https://docs.joinmastodon.org/entities/CustomEmoji/ // https://docs.joinmastodon.org/entities/CustomEmoji/
type CustomEmoji struct { type CustomEmoji struct {
ShortCode opt.Optional[string] `json:"shortcode"` ShortCode opt.Optional[string] `json:"shortcode,omitempty"`
URL opt.Optional[string] `json:"url"` URL opt.Optional[string] `json:"url,omitempty"`
StaticURL opt.Optional[string] `json:"static_url"` StaticURL opt.Optional[string] `json:"static_url,omitempty"`
VisibleInPicker opt.Optional[bool] `json:"visible_in_picker"` VisibleInPicker opt.Optional[bool] `json:"visible_in_picker,omitempty"`
Category opt.Optional[string] `json:"category"` Category opt.Optional[string] `json:"category,omitempty"`
}
func (receiver CustomEmoji) MarshalJSON() ([]byte, error) {
var buffer []byte
buffer = append(buffer, "{"...)
{
buffer = append(buffer, `"shortcode":`...)
marshaled, err := json.Marshal(receiver.ShortCode)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.CustomEmoji.ShortCode as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"url":`...)
marshaled, err := json.Marshal(receiver.URL)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.CustomEmoji.URL as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"static_url":`...)
marshaled, err := json.Marshal(receiver.StaticURL)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.CustomEmoji.StaticURL as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
buffer = append(buffer, `,"visible_in_picker":`...)
marshaled, err := json.Marshal(receiver.VisibleInPicker)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.CustomEmoji.VisibleInPicker as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
{
if opt.Nothing[string]() != receiver.Category {
buffer = append(buffer, `,"category":`...)
marshaled, err := json.Marshal(receiver.Category)
if nil != err {
return nil, erorr.Errorf("mstdn/ent: could not marshal ent.CustomEmoji.Category as JSON: %w", err)
}
buffer = append(buffer, marshaled...)
}
}
buffer = append(buffer, "}"...)
return buffer, nil
} }

View File

@ -4,8 +4,8 @@ import (
"testing" "testing"
"bytes" "bytes"
"encoding/json"
"github.com/reiver/go-json"
"github.com/reiver/go-opt" "github.com/reiver/go-opt"
) )
@ -93,11 +93,31 @@ func TestCustomEmoji_MarshalJSON(t *testing.T) {
"\t"+ `"visible_in_picker":` +`true` +"" +"\n"+ "\t"+ `"visible_in_picker":` +`true` +"" +"\n"+
"}" +"\n", "}" +"\n",
}, },
{
Value: CustomEmoji{
ShortCode: opt.Something[string](""),
URL: opt.Something[string](""),
StaticURL: opt.Something[string](""),
VisibleInPicker: opt.Something[bool](false),
Category: opt.Something[string]("super-heroes"),
},
Expected:
"{" +"\n"+
"\t"+ `"shortcode":` +`""` +"," +"\n"+
"\t"+ `"url":` +`""` +"," +"\n"+
"\t"+ `"static_url":` +`""` +"," +"\n"+
"\t"+ `"visible_in_picker":` +`false` +"," +"\n"+
"\t"+ `"category":` +`"super-heroes"` +"" +"\n"+
"}" +"\n",
},
} }
for testNumber, test := range tests { for testNumber, test := range tests {
actualBytes, err := test.Value.MarshalJSON() actualBytes, err := json.Marshal(test.Value)
if nil != err { if nil != err {
t.Errorf("For test #%d, did not expect to get an error but actually got one.", testNumber) t.Errorf("For test #%d, did not expect to get an error but actually got one.", testNumber)
t.Logf("ERROR: (%T) %s", err, err) t.Logf("ERROR: (%T) %s", err, err)

View File

@ -6,4 +6,5 @@ import (
const ( const (
errNilReceiver = erorr.Error("mstdn/ent: nil receiver") errNilReceiver = erorr.Error("mstdn/ent: nil receiver")
errNothingID = erorr.Error("mstdn/ent: nothing id")
) )

View File

@ -1,34 +1,18 @@
package ent package ent
import ( import (
"encoding/json"
"github.com/reiver/go-erorr"
"github.com/reiver/go-opt" "github.com/reiver/go-opt"
"github.com/reiver/go-nul" "github.com/reiver/go-nul"
) )
var _ json.Marshaler = Field{}
var _ json.Unmarshaler = &Field{}
const (
errCannotMashalFieldAsJSONNoName = erorr.Error("cannot marshal ent.Field to JSON — no name set")
errCannotMashalFieldAsJSONNoValue = erorr.Error("cannot marshal ent.Field to JSON — no value set")
)
// Field represents a Mastodon API "Field". // Field represents a Mastodon API "Field".
// //
// See: // See:
// https://docs.joinmastodon.org/entities/Field/ // https://docs.joinmastodon.org/entities/Field/
type Field struct { type Field struct {
Name opt.Optional[string] Name opt.Optional[string] `json:"name,omitempty"`
Value opt.Optional[string] Value opt.Optional[string] `json:"value,omitempty"`
VerifiedAt nul.Nullable[string]
}
type field struct {
Name opt.Optional[string] `json:"name"`
Value opt.Optional[string] `json:"value"`
VerifiedAt nul.Nullable[string] `json:"verified_at"` VerifiedAt nul.Nullable[string] `json:"verified_at"`
} }
@ -36,6 +20,7 @@ func FieldNameValue(name string, value string) Field {
return Field{ return Field{
Name: opt.Something(name), Name: opt.Something(name),
Value: opt.Something(value), Value: opt.Something(value),
VerifiedAt: nul.Null[string](),
} }
} }
@ -46,60 +31,3 @@ func FieldVerifiedNameValue(when string, name string, value string) Field {
VerifiedAt: nul.Something(when), VerifiedAt: nul.Something(when),
} }
} }
func (receiver Field) MarshalJSON() ([]byte, error) {
var data = map[string]interface{}{}
{
val, found := receiver.Name.Get()
if !found {
return nil, errCannotMashalFieldAsJSONNoName
}
data["name"] = val
}
{
val, found := receiver.Value.Get()
if !found {
return nil, errCannotMashalFieldAsJSONNoValue
}
data["value"] = val
}
{
val, found := receiver.VerifiedAt.Get()
if !found {
data["verified_at"] = nil
} else {
data["verified_at"] = val
}
}
return json.Marshal(data)
}
func (receiver *Field) UnmarshalJSON(data []byte) error {
if nil == receiver {
return errNilReceiver
}
var f field
err := json.Unmarshal(data, &f)
if nil != err {
return err
}
if nul.Null[string]() == f.VerifiedAt {
f.VerifiedAt = nul.Nothing[string]()
}
*receiver = Field{
Name: f.Name,
Value: f.Value,
VerifiedAt: f.VerifiedAt,
}
return nil
}

View File

@ -4,8 +4,8 @@ import (
"testing" "testing"
"bytes" "bytes"
"encoding/json"
"github.com/reiver/go-json"
"github.com/reiver/go-mstdn/ent" "github.com/reiver/go-mstdn/ent"
) )
@ -112,7 +112,7 @@ func TestField_MarshalJSON(t *testing.T) {
for testNumber, test := range tests { for testNumber, test := range tests {
actualBytes, err := test.Value.MarshalJSON() actualBytes, err := json.Marshal(test.Value)
if nil != err { if nil != err {
t.Errorf("For test #%d, did not expect to get an error but actually got one.", testNumber) t.Errorf("For test #%d, did not expect to get an error but actually got one.", testNumber)
t.Logf("ERROR: (%T) %s", err, err) t.Logf("ERROR: (%T) %s", err, err)

View File

@ -14,10 +14,6 @@ func TestField_UnmarshalJSON(t *testing.T) {
JSON string JSON string
Expected ent.Field Expected ent.Field
}{ }{
{
JSON: `{"name":"Location","value":"Metro Vancouver"}`,
Expected: ent.FieldNameValue("Location", "Metro Vancouver"),
},
{ {
JSON: `{"name":"Location","value":"Metro Vancouver", "verified_at":null}`, JSON: `{"name":"Location","value":"Metro Vancouver", "verified_at":null}`,
Expected: ent.FieldNameValue("Location", "Metro Vancouver"), Expected: ent.FieldNameValue("Location", "Metro Vancouver"),

View File

@ -7,14 +7,14 @@ import (
) )
type Poll struct { type Poll struct {
ID opt.Optional[string] `json:"id"` ID opt.Optional[string] `json:"id,omitempty"`
ExpiresAt nul.Nullable[string] `json:"expires_at"` ExpiresAt nul.Nullable[string] `json:"expires_at,omitempty"`
Expired opt.Optional[bool] `json:"expired"` Expired opt.Optional[bool] `json:"expired,omitempty"`
Multiple opt.Optional[bool] `json:"multiple"` Multiple opt.Optional[bool] `json:"multiple,omitempty"`
VotesCount opt.Optional[jsonint.Int] `json:"votes_count"` VotesCount opt.Optional[jsonint.Int] `json:"votes_count,omitempty"`
VotersCount opt.Optional[jsonint.Int] `json:"voters_count"` VotersCount opt.Optional[jsonint.Int] `json:"voters_count,omitempty"`
Options []PollOption `json:"options"` Options []PollOption `json:"options,omitempty"`
Emojis []CustomEmoji `json:"emojis"` Emojis []CustomEmoji `json:"emojis,omitempty"`
Voted opt.Optional[bool] `json:"voted"` Voted opt.Optional[bool] `json:"voted,omitempty"`
OwnVotes []jsonint.Int `json:"own_votes"` OwnVotes []jsonint.Int `json:"own_votes,omitempty"`
} }

View File

@ -11,14 +11,14 @@ import (
// See: // See:
// https://docs.joinmastodon.org/entities/Report/ // https://docs.joinmastodon.org/entities/Report/
type Report struct { type Report struct {
ID opt.Optional[string] `json:"id"` ID opt.Optional[string] `json:"id,omitempty"`
ActionTaken opt.Optional[bool] `json:"action_taken"` ActionTaken opt.Optional[bool] `json:"action_taken,omitempty"`
ActionTakenAt nul.Nullable[string] `json:"action_taken_at"` ActionTakenAt nul.Nullable[string] `json:"action_taken_at,omitempty"`
Category opt.Optional[string] `json:"category"` Category opt.Optional[string] `json:"category,omitempty"`
Comment opt.Optional[string] `json:"comment"` Comment opt.Optional[string] `json:"comment,omitempty"`
Forwarded opt.Optional[bool] `json:"forwarded"` Forwarded opt.Optional[bool] `json:"forwarded,omitempty"`
CreatedAt opt.Optional[string] `json:"created_at"` CreatedAt opt.Optional[string] `json:"created_at,omitempty"`
StatusIDs nul.Nullable[jsonstr.Strings] `json:"status_ids"` StatusIDs nul.Nullable[jsonstr.Strings] `json:"status_ids,omitempty"`
RuleIDs nul.Nullable[jsonstr.Strings] `json:"rule_ids"` RuleIDs nul.Nullable[jsonstr.Strings] `json:"rule_ids,omitempty"`
TargetAccount Account `json:"target_account"` TargetAccount opt.Optional[Account] `json:"target_account,omitempty"`
} }

View File

@ -3,9 +3,7 @@ package ent_test
import ( import (
"testing" "testing"
"encoding/json" "github.com/reiver/go-json"
"github.com/reiver/go-jsonint"
"github.com/reiver/go-jsonpp" "github.com/reiver/go-jsonpp"
"github.com/reiver/go-jsonstr" "github.com/reiver/go-jsonstr"
"github.com/reiver/go-nul" "github.com/reiver/go-nul"
@ -31,32 +29,7 @@ func TestReport_MarshalJSON(t *testing.T) {
CreatedAt: opt.Something("2023-09-27T00:31:59Z"), CreatedAt: opt.Something("2023-09-27T00:31:59Z"),
StatusIDs: nul.Null[jsonstr.Strings](), StatusIDs: nul.Null[jsonstr.Strings](),
RuleIDs: nul.Null[jsonstr.Strings](), RuleIDs: nul.Null[jsonstr.Strings](),
TargetAccount: ent.Account{ TargetAccount: opt.Something(demoAccount1()),
ID: opt.Something("12345"),
UserName: opt.Something("joeblow"),
Acct: opt.Something("joeblow@example.com"),
URL: opt.Something("https://example.com/@joeblow"),
URI: opt.Something("https://example.com/users/joeblow"),
DisplayName: opt.Something("Joe Blow :-)"),
Note: opt.Something("<p>I came. I saw. I conquered!</p>"),
Avatar: opt.Something("https://files.example.com/avatar/joeblow.png"),
AvatarStatic: opt.Something("https://files.example.com/avatar-static/joeblow.png"),
Header: opt.Something("https://files.example.com/header/joeblow.png"),
HeaderStatic: opt.Something("https://files.example.com/header-static/joeblow/png"),
//Fields []Field `json:"fields"`
//Emojis []CustomEmoji `json:"emojis"`
Locked: opt.Something(false),
Bot: opt.Something(false),
Group: opt.Something(false),
Discoverable: nul.Something(true),
CreatedAt: opt.Something("2017-02-08T02:00:53.274Z"),
LastStatusAt: nul.Something("2023-09-27T00:31:59Z"),
StatusesCount: opt.Something(jsonint.Int64(9876543210)),
FollowersCount: opt.Something(jsonint.Int64(12345)),
FollowingCount: opt.Something(jsonint.Int64(210)),
NoIndex: nul.Nothing[bool](),
//Roles []Role `json:"roles"`
},
}, },
Expected : Expected :
`{`+ `{`+
@ -78,51 +51,7 @@ func TestReport_MarshalJSON(t *testing.T) {
`,`+ `,`+
`"rule_ids":null`+ `"rule_ids":null`+
`,`+ `,`+
`"target_account":{`+ `"target_account":`+toJSON(demoAccount1())+
`"id":"12345"`+
`,`+
`"username":"joeblow"`+
`,`+
`"acct":"joeblow@example.com"`+
`,`+
`"url":"https://example.com/@joeblow"`+
`,`+
`"uri":"https://example.com/users/joeblow"`+
`,`+
`"display_name":"Joe Blow :-)"`+
`,`+
`"note":"\u003cp\u003eI came. I saw. I conquered!\u003c/p\u003e"`+
`,`+
`"avatar":"https://files.example.com/avatar/joeblow.png"`+
`,`+
`"avatar_static":"https://files.example.com/avatar-static/joeblow.png"`+
`,`+
`"header":"https://files.example.com/header/joeblow.png"`+
`,`+
`"header_static":"https://files.example.com/header-static/joeblow/png"`+
`,`+
`"fields":[]`+
`,`+
`"emojis":[]`+
`,`+
`"locked":false`+
`,`+
`"bot":false`+
`,`+
`"group":false`+
`,`+
`"discoverable":true`+
`,`+
`"created_at":"2017-02-08T02:00:53.274Z"`+
`,`+
`"last_status_at":"2023-09-27T00:31:59Z"`+
`,`+
`"statuses_count":9876543210`+
`,`+
`"followers_count":12345`+
`,`+
`"following_count":210`+
`}`+
`}`, `}`,
}, },
@ -145,32 +74,7 @@ func TestReport_MarshalJSON(t *testing.T) {
CreatedAt: opt.Something("2023-09-27T00:31:59Z"), CreatedAt: opt.Something("2023-09-27T00:31:59Z"),
StatusIDs: nul.Null[jsonstr.Strings](), StatusIDs: nul.Null[jsonstr.Strings](),
RuleIDs: nul.Null[jsonstr.Strings](), RuleIDs: nul.Null[jsonstr.Strings](),
TargetAccount: ent.Account{ TargetAccount: opt.Something(demoAccount1()),
ID: opt.Something("12345"),
UserName: opt.Something("joeblow"),
Acct: opt.Something("joeblow@example.com"),
URL: opt.Something("https://example.com/@joeblow"),
URI: opt.Something("https://example.com/users/joeblow"),
DisplayName: opt.Something("Joe Blow :-)"),
Note: opt.Something("<p>I came. I saw. I conquered!</p>"),
Avatar: opt.Something("https://files.example.com/avatar/joeblow.png"),
AvatarStatic: opt.Something("https://files.example.com/avatar-static/joeblow.png"),
Header: opt.Something("https://files.example.com/header/joeblow.png"),
HeaderStatic: opt.Something("https://files.example.com/header-static/joeblow/png"),
//Fields []Field `json:"fields"`
//Emojis []CustomEmoji `json:"emojis"`
Locked: opt.Something(false),
Bot: opt.Something(false),
Group: opt.Something(false),
Discoverable: nul.Something(true),
CreatedAt: opt.Something("2017-02-08T02:00:53.274Z"),
LastStatusAt: nul.Something("2023-09-27T00:31:59Z"),
StatusesCount: opt.Something(jsonint.Int64(9876543210)),
FollowersCount: opt.Something(jsonint.Int64(12345)),
FollowingCount: opt.Something(jsonint.Int64(210)),
NoIndex: nul.Nothing[bool](),
//Roles []Role `json:"roles"`
},
}, },
Expected : Expected :
`{`+ `{`+
@ -192,51 +96,7 @@ func TestReport_MarshalJSON(t *testing.T) {
`,`+ `,`+
`"rule_ids":null`+ `"rule_ids":null`+
`,`+ `,`+
`"target_account":{`+ `"target_account":`+toJSON(demoAccount1())+
`"id":"12345"`+
`,`+
`"username":"joeblow"`+
`,`+
`"acct":"joeblow@example.com"`+
`,`+
`"url":"https://example.com/@joeblow"`+
`,`+
`"uri":"https://example.com/users/joeblow"`+
`,`+
`"display_name":"Joe Blow :-)"`+
`,`+
`"note":"\u003cp\u003eI came. I saw. I conquered!\u003c/p\u003e"`+
`,`+
`"avatar":"https://files.example.com/avatar/joeblow.png"`+
`,`+
`"avatar_static":"https://files.example.com/avatar-static/joeblow.png"`+
`,`+
`"header":"https://files.example.com/header/joeblow.png"`+
`,`+
`"header_static":"https://files.example.com/header-static/joeblow/png"`+
`,`+
`"fields":[]`+
`,`+
`"emojis":[]`+
`,`+
`"locked":false`+
`,`+
`"bot":false`+
`,`+
`"group":false`+
`,`+
`"discoverable":true`+
`,`+
`"created_at":"2017-02-08T02:00:53.274Z"`+
`,`+
`"last_status_at":"2023-09-27T00:31:59Z"`+
`,`+
`"statuses_count":9876543210`+
`,`+
`"followers_count":12345`+
`,`+
`"following_count":210`+
`}`+
`}`, `}`,
}, },
@ -259,32 +119,7 @@ func TestReport_MarshalJSON(t *testing.T) {
CreatedAt: opt.Something("2023-09-27T00:31:59Z"), CreatedAt: opt.Something("2023-09-27T00:31:59Z"),
StatusIDs: nul.Null[jsonstr.Strings](), StatusIDs: nul.Null[jsonstr.Strings](),
RuleIDs: nul.Null[jsonstr.Strings](), RuleIDs: nul.Null[jsonstr.Strings](),
TargetAccount: ent.Account{ TargetAccount: opt.Something(demoAccount1()),
ID: opt.Something("12345"),
UserName: opt.Something("joeblow"),
Acct: opt.Something("joeblow@example.com"),
URL: opt.Something("https://example.com/@joeblow"),
URI: opt.Something("https://example.com/users/joeblow"),
DisplayName: opt.Something("Joe Blow :-)"),
Note: opt.Something("<p>I came. I saw. I conquered!</p>"),
Avatar: opt.Something("https://files.example.com/avatar/joeblow.png"),
AvatarStatic: opt.Something("https://files.example.com/avatar-static/joeblow.png"),
Header: opt.Something("https://files.example.com/header/joeblow.png"),
HeaderStatic: opt.Something("https://files.example.com/header-static/joeblow/png"),
//Fields []Field `json:"fields"`
//Emojis []CustomEmoji `json:"emojis"`
Locked: opt.Something(false),
Bot: opt.Something(false),
Group: opt.Something(false),
Discoverable: nul.Something(true),
CreatedAt: opt.Something("2017-02-08T02:00:53.274Z"),
LastStatusAt: nul.Something("2023-09-27T00:31:59Z"),
StatusesCount: opt.Something(jsonint.Int64(9876543210)),
FollowersCount: opt.Something(jsonint.Int64(12345)),
FollowingCount: opt.Something(jsonint.Int64(210)),
NoIndex: nul.Nothing[bool](),
//Roles []Role `json:"roles"`
},
}, },
Expected : Expected :
`{`+ `{`+
@ -306,58 +141,14 @@ func TestReport_MarshalJSON(t *testing.T) {
`,`+ `,`+
`"rule_ids":null`+ `"rule_ids":null`+
`,`+ `,`+
`"target_account":{`+ `"target_account":`+toJSON(demoAccount1())+
`"id":"12345"`+
`,`+
`"username":"joeblow"`+
`,`+
`"acct":"joeblow@example.com"`+
`,`+
`"url":"https://example.com/@joeblow"`+
`,`+
`"uri":"https://example.com/users/joeblow"`+
`,`+
`"display_name":"Joe Blow :-)"`+
`,`+
`"note":"\u003cp\u003eI came. I saw. I conquered!\u003c/p\u003e"`+
`,`+
`"avatar":"https://files.example.com/avatar/joeblow.png"`+
`,`+
`"avatar_static":"https://files.example.com/avatar-static/joeblow.png"`+
`,`+
`"header":"https://files.example.com/header/joeblow.png"`+
`,`+
`"header_static":"https://files.example.com/header-static/joeblow/png"`+
`,`+
`"fields":[]`+
`,`+
`"emojis":[]`+
`,`+
`"locked":false`+
`,`+
`"bot":false`+
`,`+
`"group":false`+
`,`+
`"discoverable":true`+
`,`+
`"created_at":"2017-02-08T02:00:53.274Z"`+
`,`+
`"last_status_at":"2023-09-27T00:31:59Z"`+
`,`+
`"statuses_count":9876543210`+
`,`+
`"followers_count":12345`+
`,`+
`"following_count":210`+
`}`+
`}`, `}`,
}, },
} }
for testNumber, test := range tests { for testNumber, test := range tests {
actualBytes, err := json.Marshal(test.Report) actualBytes, err := json.Marshal(&test.Report)
if nil != err { if nil != err {
t.Errorf("For test #%d, did not expect an error but actually got one.", testNumber) t.Errorf("For test #%d, did not expect an error but actually got one.", testNumber)
t.Logf("ERROR: (%T) %s", err, err) t.Logf("ERROR: (%T) %s", err, err)

View File

@ -1,75 +1,18 @@
package ent package ent
import ( import (
"encoding/json"
"github.com/reiver/go-opt" "github.com/reiver/go-opt"
"github.com/reiver/go-jsonint" "github.com/reiver/go-jsonint"
) )
var _ json.Marshaler = Role{}
var _ json.Unmarshaler = new(Role)
// Role represents a Mastodon API "Role". // Role represents a Mastodon API "Role".
// //
// See: // See:
// https://docs.joinmastodon.org/entities/Role/ // https://docs.joinmastodon.org/entities/Role/
type Role struct { type Role struct {
ID opt.Optional[jsonint.Int] ID opt.Optional[jsonint.Int] `json:"id,omitempty"`
Name opt.Optional[string] Name opt.Optional[string] `json:"name,omitempty"`
Color opt.Optional[string] Color opt.Optional[string] `json:"color,omitempty"`
Permissions opt.Optional[jsonint.Int] Permissions opt.Optional[jsonint.Int] `json:"permissions,omitempty"`
Highlighted opt.Optional[bool] Highlighted opt.Optional[bool] `json:"highlighted,omitempty"`
}
type role struct {
ID opt.Optional[jsonint.Int] `json:"id"`
Name opt.Optional[string] `json:"name"`
Color opt.Optional[string] `json:"color"`
Permissions opt.Optional[jsonint.Int] `json:"permissions"`
Highlighted opt.Optional[bool] `json:"highlighted"`
}
func (receiver Role) MarshalJSON() ([]byte, error) {
var src role
src.ID = receiver.ID
src.Name = receiver.Name
src.Permissions = receiver.Permissions
src.Highlighted = receiver.Highlighted
src.Color = receiver.Color
src.Color.WhenNothing(func(){
src.Color = opt.Something("")
})
return json.Marshal(src)
}
func (receiver *Role) UnmarshalJSON(data []byte) error {
if nil == receiver {
return errNilReceiver
}
var dst role
if err := json.Unmarshal(data, &dst); nil != err {
return err
}
receiver.ID = dst.ID
receiver.Name = dst.Name
receiver.Permissions = dst.Permissions
receiver.Highlighted = dst.Highlighted
dst.Color.WhenSomething(func(value string){
if "" == value {
return
}
receiver.Color = opt.Something(value)
})
return nil
} }

View File

@ -66,6 +66,7 @@ func TestRole_MarshalJSON(t *testing.T) {
Expected: ent.Role{ Expected: ent.Role{
ID: opt.Something(jsonint.Int64(87)), ID: opt.Something(jsonint.Int64(87)),
Name: opt.Something("QA Specialist"), Name: opt.Something("QA Specialist"),
Color: opt.Something(""),
Permissions: opt.Something(jsonint.Int64(218)), Permissions: opt.Something(jsonint.Int64(218)),
Highlighted: opt.Something(true), Highlighted: opt.Something(true),
}, },

View File

@ -1,7 +1,7 @@
package ent package ent
import ( import (
"encoding/json" gojson "encoding/json"
"github.com/reiver/go-jsonint" "github.com/reiver/go-jsonint"
"github.com/reiver/go-opt" "github.com/reiver/go-opt"
@ -9,34 +9,35 @@ import (
) )
type Status struct { type Status struct {
ID opt.Optional[string] `json:"id"` ID opt.Optional[string] `json:"id,omitempty"`
URL nul.Nullable[string] `json:"uri"` URI nul.Nullable[string] `json:"uri,omitempty"`
CreatedAt opt.Optional[string] `json:"created_at"` URL nul.Nullable[string] `json:"url,omitempty"`
Account Account `json:"account"` CreatedAt opt.Optional[string] `json:"created_at,omitempty"`
Content opt.Optional[string] `json:"content"` Account Account `json:"account,omitempty"`
Visibility opt.Optional[string] `json:"visibility"` Content opt.Optional[string] `json:"content,omitempty"`
Sensitive opt.Optional[bool] `json:"sensitive"` Visibility opt.Optional[string] `json:"visibility,omitempty"`
SpoilerText opt.Optional[string] `json:"spoiler_text"` Sensitive opt.Optional[bool] `json:"sensitive,omitempty"`
MediaAttachments []MediaAttachment `json:"media_attachments"` SpoilerText opt.Optional[string] `json:"spoiler_text,omitempty"`
Application nul.Nullable[Application] `json:"application"` MediaAttachments []MediaAttachment `json:"media_attachments,omitempty"`
Mentions []Mention `json:"mentions"` Application nul.Nullable[Application] `json:"application,omitempty"`
Tags []Tag `json:"tags"` Mentions []Mention `json:"mentions,omitempty"`
Emojis []CustomEmoji `json:"emojis"` Tags []Tag `json:"tags,omitempty"`
ReblogsCount opt.Optional[jsonint.Int] `json:"reblogs_count"` Emojis []CustomEmoji `json:"emojis,omitempty"`
FavouritesCount opt.Optional[jsonint.Int] `json:"favourites_count"` ReblogsCount opt.Optional[jsonint.Int] `json:"reblogs_count,omitempty"`
RepliesCount opt.Optional[jsonint.Int] `json:"replies_count"` FavouritesCount opt.Optional[jsonint.Int] `json:"favourites_count,omitempty"`
InReplyToID nul.Nullable[string] `json:"in_reply_to_id"` RepliesCount opt.Optional[jsonint.Int] `json:"replies_count,omitempty"`
InReplyToAccountID nul.Nullable[string] `json:"in_reply_to_account_id"` InReplyToID nul.Nullable[string] `json:"in_reply_to_id,omitempty"`
Reblog json.RawMessage `json:"reblog"` InReplyToAccountID nul.Nullable[string] `json:"in_reply_to_account_id,omitempty"`
Poll nul.Nullable[Poll] `json:"poll"` Reblog gojson.RawMessage `json:"reblog,omitempty"`
Card nul.Nullable[PreviewCard] `json:"card"` Poll nul.Nullable[Poll] `json:"poll,omitempty"`
Language nul.Nullable[string] `json:"language"` Card nul.Nullable[PreviewCard] `json:"card,omitempty"`
Text nul.Nullable[string] `json:"text"` Language nul.Nullable[string] `json:"language,omitempty"`
EditedAt nul.Nullable[string] `json:"edited_at"` Text nul.Nullable[string] `json:"text,omitempty"`
Favourited opt.Optional[bool] `json:"favourited"` EditedAt nul.Nullable[string] `json:"edited_at,omitempty"`
Reblogged opt.Optional[bool] `json:"reblogged"` Favourited opt.Optional[bool] `json:"favourited,omitempty"`
Muted opt.Optional[bool] `json:"muted"` Reblogged opt.Optional[bool] `json:"reblogged,omitempty"`
Bookmarked opt.Optional[bool] `json:"bookmarked"` Muted opt.Optional[bool] `json:"muted,omitempty"`
Pinned opt.Optional[bool] `json:"pinned"` Bookmarked opt.Optional[bool] `json:"bookmarked,omitempty"`
Filtered json.RawMessage `json:"filtered"` Pinned opt.Optional[bool] `json:"pinned,omitempty"`
Filtered gojson.RawMessage `json:"filtered,omitempty"`
} }

View File

@ -0,0 +1,198 @@
package ent
import (
"testing"
gojson "encoding/json"
"github.com/reiver/go-json"
"github.com/reiver/go-nul"
"github.com/reiver/go-opt"
)
func TestStatus_JSON(t *testing.T) {
tests := []struct{
Status Status
Expected string
}{
{
Status:Status{
ID: opt.Something("123"),
},
Expected: `{"id":"123"}`,
},
{
Status:Status{
ID: opt.Something("123"),
URI: nul.Something("https://example.com/status/123"),
},
Expected: `{"id":"123","uri":"https://example.com/status/123"}`,
},
{
Status:Status{
ID: opt.Something("123"),
URL: nul.Something("https://example.com/@joeblow/status/123"),
},
Expected: `{"id":"123","url":"https://example.com/@joeblow/status/123"}`,
},
{
Status:Status{
ID: opt.Something("123"),
CreatedAt: opt.Something("yesterday"),
},
Expected: `{"id":"123","created_at":"yesterday"}`,
},
//@TODO: Account
{
Status:Status{
ID: opt.Something("123"),
Content: opt.Something("Hello world!"),
},
Expected: `{"id":"123","content":"Hello world!"}`,
},
{
Status:Status{
ID: opt.Something("123"),
Visibility: opt.Something("public"),
},
Expected: `{"id":"123","visibility":"public"}`,
},
{
Status:Status{
ID: opt.Something("123"),
Sensitive: opt.Something(true),
},
Expected: `{"id":"123","sensitive":true}`,
},
{
Status:Status{
ID: opt.Something("123"),
SpoilerText: opt.Something("the rabbit did it"),
},
Expected: `{"id":"123","spoiler_text":"the rabbit did it"}`,
},
//@TODO: MediaAttachments
//@TODO: Application
//@TODO: Mentions
{
Status:Status{
ID: opt.Something("123"),
Tags: []Tag{
Tag{
Name:opt.Something("akkoma"),
URL:opt.Something("https://example.com/tags/akkoma"),
},
Tag{
Name:opt.Something("fediverse"),
URL:opt.Something("https://example.com/tags/fediverse"),
},
},
},
Expected: `{"id":"123","tags":[{"name":"akkoma","url":"https://example.com/tags/akkoma"},{"name":"fediverse","url":"https://example.com/tags/fediverse"}]}`,
},
//@TODO: Emojis
{
Status:Status{
ID: opt.Something("123"),
Language: nul.Something("en"),
},
Expected: `{"id":"123","language":"en"}`,
},
{
Status:Status{
ID: opt.Something("123"),
Text: nul.Something("bing bong bang"),
},
Expected: `{"id":"123","text":"bing bong bang"}`,
},
{
Status:Status{
ID: opt.Something("123"),
EditedAt: nul.Something("today"),
},
Expected: `{"id":"123","edited_at":"today"}`,
},
{
Status:Status{
ID: opt.Something("123"),
Favourited: opt.Something(true),
},
Expected: `{"id":"123","favourited":true}`,
},
{
Status:Status{
ID: opt.Something("123"),
Reblogged: opt.Something(true),
},
Expected: `{"id":"123","reblogged":true}`,
},
{
Status:Status{
ID: opt.Something("123"),
Muted: opt.Something(true),
},
Expected: `{"id":"123","muted":true}`,
},
{
Status:Status{
ID: opt.Something("123"),
Bookmarked: opt.Something(true),
},
Expected: `{"id":"123","bookmarked":true}`,
},
{
Status:Status{
ID: opt.Something("123"),
Pinned: opt.Something(true),
},
Expected: `{"id":"123","pinned":true}`,
},
{
Status:Status{
ID: opt.Something("123"),
Filtered: gojson.RawMessage([]byte(`{"once":1,"twice":2,"thrice":3,"fource":4}`)),
},
Expected: `{"id":"123","filtered":{"once":1,"twice":2,"thrice":3,"fource":4}}`,
},
}
for testNumber, test := range tests {
actualBytes, err := json.Marshal(test.Status)
if nil != err {
t.Errorf("For test #%d, did not expect to get an error but actually did.", testNumber)
t.Logf("ERROR: (%T) %s", err, err)
continue
}
actual := string(actualBytes)
expected := test.Expected
if expected != actual {
t.Errorf("For test #%d, the actual string-serialized form is not what was expected.", testNumber)
t.Logf("EXPECTED: %q", expected)
t.Logf("ACTUAL: %q", actual)
t.Logf("EXPECTED:\n%s", expected)
t.Logf("ACTUAL:\n%s", actual)
t.Logf("STATUS:\n%#v", test.Status)
continue
}
}
}

19
go.mod
View File

@ -1,12 +1,12 @@
module github.com/reiver/go-mstdn module github.com/reiver/go-mstdn
go 1.20 go 1.22.4
require github.com/reiver/go-opt v0.0.0-20240704165441-4ce81358adfc require github.com/reiver/go-opt v0.0.0-20240809032727-8e60e8ea3e8f
require ( require (
github.com/reiver/go-jsonint v0.0.0-20240801233651-21b9c52057ee github.com/reiver/go-jsonint v0.0.0-20240801233651-21b9c52057ee
github.com/reiver/go-nul v0.0.0-20240801233226-90d138095e48 github.com/reiver/go-nul v0.0.0-20240809033544-506ae7916afa
) )
require github.com/reiver/go-jsonstr v0.0.0-20240802000653-a5a8363975f1 require github.com/reiver/go-jsonstr v0.0.0-20240802000653-a5a8363975f1
@ -15,4 +15,15 @@ require github.com/reiver/go-jsonpp v0.0.0-20240802002345-16cd4c5a34ee
require github.com/reiver/go-erorr v0.0.0-20240801233437-8cbde6d1fa3f require github.com/reiver/go-erorr v0.0.0-20240801233437-8cbde6d1fa3f
require github.com/reiver/go-pathmatch v1.0.1-0.20240802004530-0dc31d85afa8 require (
github.com/reiver/go-httpsse v0.0.0-20240806223648-6ed5a785f3f8
github.com/reiver/go-json v0.0.0-20240808191545-fa5fbb1bb3f6
github.com/reiver/go-pathmatch v1.0.1-0.20240802004530-0dc31d85afa8
)
require (
github.com/reiver/go-ascii v0.0.0-20240302002050-442843e5ea02 // indirect
github.com/reiver/go-errhttp v1.1.1-0.20240513035723-daf47d264c9c // indirect
github.com/reiver/go-lck v0.0.0-20240808133902-b56df221c39f // indirect
github.com/reiver/go-utf8 v2.0.2-0.20240806185936-5dd7c5557d34+incompatible // indirect
)

21
go.sum
View File

@ -1,15 +1,28 @@
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/reiver/go-ascii v0.0.0-20240302002050-442843e5ea02 h1:YviUVCHZw6gaxvLznXQTXp7LwaWzly80TXUsWxwPPi4=
github.com/reiver/go-ascii v0.0.0-20240302002050-442843e5ea02/go.mod h1:f43/Y1zpLvqorZN0BelarPzO2GrotAQrBy3T08NUSTM=
github.com/reiver/go-erorr v0.0.0-20240801233437-8cbde6d1fa3f h1:D1QSxKHm8U73XhjsW3SFLkT0zT5pKJi+1KGboMhY1Rk= github.com/reiver/go-erorr v0.0.0-20240801233437-8cbde6d1fa3f h1:D1QSxKHm8U73XhjsW3SFLkT0zT5pKJi+1KGboMhY1Rk=
github.com/reiver/go-erorr v0.0.0-20240801233437-8cbde6d1fa3f/go.mod h1:F0HbBf+Ak2ZlE8YkDW4Y+KxaUmT0KaaIJK6CXY3cJxE= github.com/reiver/go-erorr v0.0.0-20240801233437-8cbde6d1fa3f/go.mod h1:F0HbBf+Ak2ZlE8YkDW4Y+KxaUmT0KaaIJK6CXY3cJxE=
github.com/reiver/go-errhttp v1.1.1-0.20240513035723-daf47d264c9c h1:C/ALE5tUOywYv2tqweTLOjH0BVkN5TTMDKY6qDTD/3Q=
github.com/reiver/go-errhttp v1.1.1-0.20240513035723-daf47d264c9c/go.mod h1:J55rONfn+39379roH0KxMCGv6w9ifhZIoSKaVswRUDk=
github.com/reiver/go-httpsse v0.0.0-20240806223648-6ed5a785f3f8 h1:HOpgKCgKqWYub73sQvWxamDqJ7G8P0tMk5wYBFUDblE=
github.com/reiver/go-httpsse v0.0.0-20240806223648-6ed5a785f3f8/go.mod h1:hABgQXG6IXrFbcy8C+VKEM3/tDZg3nZ4u82N/QvvH70=
github.com/reiver/go-json v0.0.0-20240808191545-fa5fbb1bb3f6 h1:Ty3b38DeA5BKi42+3f3o8Bl4zEAKlJsF0ozArGJmo9k=
github.com/reiver/go-json v0.0.0-20240808191545-fa5fbb1bb3f6/go.mod h1:IHoQXCaObMGCDoTKko/XzDk8QbpNzepuCLMt7KKCLBs=
github.com/reiver/go-jsonint v0.0.0-20240801233651-21b9c52057ee h1:DfEj/iGAIU8CtTcnttyU6t3lae0jl760fUGjxZhIHNU= github.com/reiver/go-jsonint v0.0.0-20240801233651-21b9c52057ee h1:DfEj/iGAIU8CtTcnttyU6t3lae0jl760fUGjxZhIHNU=
github.com/reiver/go-jsonint v0.0.0-20240801233651-21b9c52057ee/go.mod h1:1lW12ILqOxqtylQBMezy6oRuMQWlzFjeWF/Wbz/9U8g= github.com/reiver/go-jsonint v0.0.0-20240801233651-21b9c52057ee/go.mod h1:1lW12ILqOxqtylQBMezy6oRuMQWlzFjeWF/Wbz/9U8g=
github.com/reiver/go-jsonpp v0.0.0-20240802002345-16cd4c5a34ee h1:KhA+fe6qjEw6ZbN3vjPICGL0NqR+rghYre4SQB0WWVs= github.com/reiver/go-jsonpp v0.0.0-20240802002345-16cd4c5a34ee h1:KhA+fe6qjEw6ZbN3vjPICGL0NqR+rghYre4SQB0WWVs=
github.com/reiver/go-jsonpp v0.0.0-20240802002345-16cd4c5a34ee/go.mod h1:iubeYYU+lR1wqKssI6Sz1DK/Aw/N/qmRcam0wf8hYIs= github.com/reiver/go-jsonpp v0.0.0-20240802002345-16cd4c5a34ee/go.mod h1:iubeYYU+lR1wqKssI6Sz1DK/Aw/N/qmRcam0wf8hYIs=
github.com/reiver/go-jsonstr v0.0.0-20240802000653-a5a8363975f1 h1:32ulBfhFKQIaRMuwCo2vi7FLOphgyKTZzJIk+LWmMOE= github.com/reiver/go-jsonstr v0.0.0-20240802000653-a5a8363975f1 h1:32ulBfhFKQIaRMuwCo2vi7FLOphgyKTZzJIk+LWmMOE=
github.com/reiver/go-jsonstr v0.0.0-20240802000653-a5a8363975f1/go.mod h1:/msMJNY8TjDYgO/h06wEuzTWGs1b37xbUo8FH/2aZqQ= github.com/reiver/go-jsonstr v0.0.0-20240802000653-a5a8363975f1/go.mod h1:/msMJNY8TjDYgO/h06wEuzTWGs1b37xbUo8FH/2aZqQ=
github.com/reiver/go-nul v0.0.0-20240801233226-90d138095e48 h1:+x7mICqo/GBI0B7I5obJM51wve8zcIsTW5kdctlO9pw= github.com/reiver/go-lck v0.0.0-20240808133902-b56df221c39f h1:KBVWBoNIM8mHkUR3dP64hm2p2vxoD5xHZ6wUllCjFTc=
github.com/reiver/go-nul v0.0.0-20240801233226-90d138095e48/go.mod h1:hN0edxyC/F7HMB86sn/ljqTJ0CrAeOCGMpCZJ2Nmtc4= github.com/reiver/go-lck v0.0.0-20240808133902-b56df221c39f/go.mod h1:PFseSi8S0CBkc+YB6jYWtogPk83LumclnYJZZBQJJhs=
github.com/reiver/go-opt v0.0.0-20240704165441-4ce81358adfc h1:9ARo75fNaZDaGy6zzTrCnJWtkDMNigM7uMRRK8sUERM= github.com/reiver/go-nul v0.0.0-20240809033544-506ae7916afa h1:Owi2PuvHrN3KaCyNsXxgN5deGISkVwmmO0tWXZgHtfE=
github.com/reiver/go-opt v0.0.0-20240704165441-4ce81358adfc/go.mod h1:Yu6dFKh0IZ0evP9U5QiBlxBa5BhlBzGEn7EZ1kP/pkA= github.com/reiver/go-nul v0.0.0-20240809033544-506ae7916afa/go.mod h1:aXT75/yMqcZA2wN/hW52vb5CQjUCEKGoyikjSi02oS8=
github.com/reiver/go-opt v0.0.0-20240809032727-8e60e8ea3e8f h1:EKHU13RjaUjg67RaIbqNr/4Mb+4qyfQRJAX+DiR05Dw=
github.com/reiver/go-opt v0.0.0-20240809032727-8e60e8ea3e8f/go.mod h1:iXlSKwDjhyF10Fdd2J5PYKwBFlWgfq7OTRxCHU+yTUY=
github.com/reiver/go-pathmatch v1.0.1-0.20240802004530-0dc31d85afa8 h1:3mxWa4FELLc6TbC00ZZTe30tYUZ8HN4IHaH0m7Y3SPc= github.com/reiver/go-pathmatch v1.0.1-0.20240802004530-0dc31d85afa8 h1:3mxWa4FELLc6TbC00ZZTe30tYUZ8HN4IHaH0m7Y3SPc=
github.com/reiver/go-pathmatch v1.0.1-0.20240802004530-0dc31d85afa8/go.mod h1:SveMAOrJP7XCL3UlazOjQdKNCGG2T/E1zE6gDHfs3cA= github.com/reiver/go-pathmatch v1.0.1-0.20240802004530-0dc31d85afa8/go.mod h1:SveMAOrJP7XCL3UlazOjQdKNCGG2T/E1zE6gDHfs3cA=
github.com/reiver/go-utf8 v2.0.2-0.20240806185936-5dd7c5557d34+incompatible h1:Ewax9SN/f+OibeHNDBPI2lsf1t17YRBvKDA4X7CU4vA=
github.com/reiver/go-utf8 v2.0.2-0.20240806185936-5dd7c5557d34+incompatible/go.mod h1:tfGntNDUeEZIAsz0mTu0lF7/IlErc+BpQgO6yxYmJig=