1
0
Fork 0
golang-github-go-ap-activit.../item.go
Daniel Baumann c8085bda34
Adding upstream version 0.0~git20250501.71edba4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-05-18 22:06:39 +02:00

212 lines
5.5 KiB
Go

package activitypub
import (
"fmt"
"reflect"
"strings"
)
// Item struct
type Item = ObjectOrLink
const (
// EmptyIRI represents a zero length IRI
EmptyIRI IRI = ""
// NilIRI represents by convention an IRI which is nil
// Its use is mostly to check if a property of an ActivityPub Item is nil
NilIRI IRI = "-"
// EmptyID represents a zero length ID
EmptyID = EmptyIRI
// NilID represents by convention an ID which is nil, see details of NilIRI
NilID = NilIRI
)
func itemsNeedSwapping(i1, i2 Item) bool {
if IsIRI(i1) && !IsIRI(i2) {
return true
}
t1 := i1.GetType()
t2 := i2.GetType()
if ObjectTypes.Contains(t2) {
return !ObjectTypes.Contains(t1)
}
return false
}
// ItemsEqual checks if it and with Items are equal
func ItemsEqual(it, with Item) bool {
if IsNil(it) || IsNil(with) {
return IsNil(with) && IsNil(it)
}
if itemsNeedSwapping(it, with) {
return ItemsEqual(with, it)
}
result := false
if IsIRI(with) || IsIRI(it) {
// NOTE(marius): I'm not sure this logic is sound:
// if only one item is an IRI it should not be equal to the other even if it has the same ID
result = it.GetLink().Equals(with.GetLink(), false)
} else if IsItemCollection(it) {
if !IsItemCollection(with) {
return false
}
_ = OnItemCollection(it, func(c *ItemCollection) error {
result = c.Equals(with)
return nil
})
} else if IsObject(it) {
_ = OnObject(it, func(i *Object) error {
result = i.Equals(with)
return nil
})
if ActivityTypes.Contains(with.GetType()) {
_ = OnActivity(it, func(i *Activity) error {
result = i.Equals(with)
return nil
})
} else if ActorTypes.Contains(with.GetType()) {
_ = OnActor(it, func(i *Actor) error {
result = i.Equals(with)
return nil
})
} else if it.IsCollection() {
if it.GetType() == CollectionType {
_ = OnCollection(it, func(c *Collection) error {
result = c.Equals(with)
return nil
})
}
if it.GetType() == OrderedCollectionType {
_ = OnOrderedCollection(it, func(c *OrderedCollection) error {
result = c.Equals(with)
return nil
})
}
if it.GetType() == CollectionPageType {
_ = OnCollectionPage(it, func(c *CollectionPage) error {
result = c.Equals(with)
return nil
})
}
if it.GetType() == OrderedCollectionPageType {
_ = OnOrderedCollectionPage(it, func(c *OrderedCollectionPage) error {
result = c.Equals(with)
return nil
})
}
}
}
return result
}
// IsItemCollection returns if the current Item interface holds a Collection
func IsItemCollection(it LinkOrIRI) bool {
_, ok := it.(ItemCollection)
_, okP := it.(*ItemCollection)
return ok || okP || IsIRIs(it)
}
// IsIRI returns if the current Item interface holds an IRI
func IsIRI(it LinkOrIRI) bool {
_, okV := it.(IRI)
_, okP := it.(*IRI)
return okV || okP
}
// IsIRIs returns if the current Item interface holds an IRI slice
func IsIRIs(it LinkOrIRI) bool {
_, okV := it.(IRIs)
_, okP := it.(*IRIs)
return okV || okP
}
// IsLink returns if the current Item interface holds a Link
func IsLink(it LinkOrIRI) bool {
_, okV := it.(Link)
_, okP := it.(*Link)
return okV || okP
}
// IsObject returns if the current Item interface holds an Object
func IsObject(it LinkOrIRI) bool {
switch ob := it.(type) {
case Actor, *Actor,
Object, *Object, Profile, *Profile, Place, *Place, Relationship, *Relationship, Tombstone, *Tombstone,
Activity, *Activity, IntransitiveActivity, *IntransitiveActivity, Question, *Question,
Collection, *Collection, CollectionPage, *CollectionPage,
OrderedCollection, *OrderedCollection, OrderedCollectionPage, *OrderedCollectionPage:
return ob != nil
default:
return false
}
}
// IsNil checks if the object matching an ObjectOrLink interface is nil
func IsNil(it LinkOrIRI) bool {
if it == nil {
return true
}
// This is the default if the argument can't be cast to Object, as is the case for an ItemCollection
isNil := false
if IsIRI(it) {
isNil = len(it.GetLink()) == 0 || strings.EqualFold(it.GetLink().String(), NilIRI.String())
} else if IsItemCollection(it) {
if v, ok := it.(ItemCollection); ok {
return v == nil
}
if v, ok := it.(*ItemCollection); ok {
return v == nil
}
if v, ok := it.(IRIs); ok {
return v == nil
}
if v, ok := it.(*IRIs); ok {
return v == nil
}
} else if IsObject(it) {
if ob, ok := it.(Item); ok {
_ = OnObject(ob, func(o *Object) error {
isNil = o == nil
return nil
})
}
} else if IsLink(it) {
_ = OnLink(it, func(l *Link) error {
isNil = l == nil
return nil
})
} else {
// NOTE(marius): we're not dealing with a type that we know about, so we use slow reflection
// as we still care about the result
v := reflect.ValueOf(it)
isNil = v.Kind() == reflect.Pointer && v.IsNil()
}
return isNil
}
func ErrorInvalidType[T Objects | Links | IRIs](received LinkOrIRI) error {
return fmt.Errorf("unable to convert %T to %T", received, new(T))
}
// OnItem runs function "fn" on the Item "it", with the benefit of destructuring "it" to individual
// items if it's actually an ItemCollection or an object holding an ItemCollection
//
// It is expected that the caller handles the logic of dealing with different Item implementations
// internally in "fn".
func OnItem(it Item, fn func(Item) error) error {
if it == nil {
return nil
}
if !IsItemCollection(it) {
return fn(it)
}
return OnItemCollection(it, func(col *ItemCollection) error {
for _, it := range *col {
if err := OnItem(it, fn); err != nil {
return err
}
}
return nil
})
}