goap/parser.go
2024-08-30 13:49:41 +02:00

157 lines
4 KiB
Go

package goap
import (
"fmt"
"slices"
"github.com/piprate/json-gold/ld"
)
// Internal so that others don't mess with this and potentially accidentally overwrite it, breaking everything
var allInternalParsersExceptBase []UnmarshalFunc = []UnmarshalFunc{
ParseUDIdData,
ParseUDTypeData,
ParseASActorData,
ParseASAlsoKnownAsData,
ParseASAttachmentsData,
ParseASAttributedToData,
ParseASCCData,
ParseASContentData,
ParseASEndpointsData,
ParseASFollowersData,
ParseASFollowingData,
ParseASHrefData,
ParseASIconData,
ParseASImageData,
ParseASMediaTypeData,
ParseASNameData,
ParseASOutboxData,
ParseASObjectData,
ParseASPreferredNameData,
ParseASPublishedData,
ParseASRestrictedData,
ParseASRepliesData,
ParseASSharedInboxData,
ParseASSummaryData,
ParseASSensitiveData,
ParseASTagData,
ParseASToData,
ParseASUrlData,
ParseASUpdatedData,
ParseASFirstData,
ParseASNextData,
ParseASItemsData,
ParseASPartOfData,
ParseASInReplyToData,
ParseASQuoteUrlData,
ParseOstatusAtomUriData,
ParseOstatusConversationData,
ParseOstatusInReplyToAtomUriData,
ParseW3InboxData,
ParseW3VcardAddressData,
ParseW3VcardBirthdayData,
ParseW3SecurityOwnerData,
ParseW3SecurityPublicKeyPemData,
ParseW3SecurityPublicKeyData,
ParseMastoDevicesData,
ParseMastoDiscoverableData,
ParseMastoFeaturedData,
ParseMastoDiscoverableData,
ParseMastoIndexableData,
ParseMastoMemorialData,
ParseSchemaValueData,
ParseMKIsCatData,
ParseMKSummaryData,
ParseMKQuoteData,
ParseFFSpeakAsCatData,
ParseFedibirdQuoteUriData,
}
func chainParse(
raw map[string]any,
start BaseApChain,
parsers ...UnmarshalFunc,
) (BaseApChain, []error) {
var current BaseApChain = start
errors := []error{}
for _, p := range parsers {
next, err := p(raw, current)
current = next
errors = append(errors, err)
}
return current, errors
}
// Unmarshal raw data into an Activitypub object in the form of a recursive BaseApChain
// Each element corresponds to one attribute the object contained
// If ldOptions or processor are nil, the following defaults are used:
// - ldOptions: `ld.NewJsonLdOptions("")`
// - processor: `ld.NewJsonLdProcessor()`
// Returns an ActivityPub object and a list of errors produced by the parser functions
// Not each function has to produce an error. Those will have nil as error value
// Those errors (or nils) come in the order of the parser functions
// which can be inspected in the `allInternalParsersExceptBase` internal variable
func Unmarshal(
raw []byte,
ldOptions *ld.JsonLdOptions,
processor *ld.JsonLdProcessor,
extraParsers ...UnmarshalFunc,
) (BaseApChain, []error) {
if ldOptions == nil {
ldOptions = ld.NewJsonLdOptions("")
}
if processor == nil {
processor = ld.NewJsonLdProcessor()
}
rawData, err := processor.Expand(raw, ldOptions)
if err != nil {
return nil, []error{err}
}
data, ok := rawData[0].(map[string]any)
if !ok {
return nil, []error{fmt.Errorf("failed to cast expand result into map[string]any")}
}
return UnmarshalPreprocessed(data, extraParsers...)
}
// Unmarshal a preproccessed object. Preproccessed meaning being first parsed as json and then expanded by json-gold
// Otherwise the same as Unmarshal
func UnmarshalPreprocessed(
raw map[string]any,
extraParsers ...UnmarshalFunc,
) (BaseApChain, []error) {
base := EmptyBaseObject{}
allParsers := slices.Concat(allInternalParsersExceptBase, extraParsers)
return chainParse(raw, &base, allParsers...)
}
// Find an attribute in an ActivityPub object of the given type
// Returns a pointer to the found attribute and whether it found it
// 2nd parameter is true if the attribute was found, false otherwise
func FindAttribute[T BaseApChain](object BaseApChain) (*T, bool) {
var obj T
var ok bool
// Try and cast object into wanted type
// If cast failed, do inner codeblock
// Try and cast again
for obj, ok = object.(T); !ok; obj, ok = object.(T) {
// Get the next attribute in the chain
object, ok = object.GetSelfOrBase()
// If this is the final object in the chain, cancel and return false
if !ok {
return nil, false
}
}
return &obj, true
}