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, ParseOstatusAtomUriData, ParseOstatusConversationData, ParseW3InboxData, ParseW3VcardAddressData, ParseW3VcardBirthdayData, ParseW3SecurityOwnerData, ParseW3SecurityPublicKeyPemData, ParseW3SecurityPublicKeyData, ParseMastoDevicesData, ParseMastoDiscoverableData, ParseMastoFeaturedData, ParseMastoDiscoverableData, ParseMastoIndexableData, ParseMastoMemorialData, ParseSchemaValueData, ParseMKIsCatData, ParseMKSummaryData, ParseFFSpeakAsCatData, } 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 }