github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/tuple/onr.go (about)

     1  package tuple
     2  
     3  import (
     4  	"slices"
     5  	"sort"
     6  	"strings"
     7  
     8  	core "github.com/authzed/spicedb/pkg/proto/core/v1"
     9  )
    10  
    11  // ObjectAndRelation creates an ONR from string pieces.
    12  func ObjectAndRelation(ns, oid, rel string) *core.ObjectAndRelation {
    13  	return &core.ObjectAndRelation{
    14  		Namespace: ns,
    15  		ObjectId:  oid,
    16  		Relation:  rel,
    17  	}
    18  }
    19  
    20  // RelationReference creates a RelationReference from the string pieces.
    21  func RelationReference(namespaceName string, relationName string) *core.RelationReference {
    22  	return &core.RelationReference{
    23  		Namespace: namespaceName,
    24  		Relation:  relationName,
    25  	}
    26  }
    27  
    28  // ParseSubjectONR converts a string representation of a Subject ONR to a proto object. Unlike
    29  // ParseONR, this method allows for objects without relations. If an object without a relation
    30  // is given, the relation will be set to ellipsis.
    31  func ParseSubjectONR(subjectOnr string) *core.ObjectAndRelation {
    32  	groups := subjectRegex.FindStringSubmatch(subjectOnr)
    33  
    34  	if len(groups) == 0 {
    35  		return nil
    36  	}
    37  
    38  	relation := Ellipsis
    39  	subjectRelIndex := slices.Index(subjectRegex.SubexpNames(), "subjectRel")
    40  	if len(groups[subjectRelIndex]) > 0 {
    41  		relation = groups[subjectRelIndex]
    42  	}
    43  
    44  	return &core.ObjectAndRelation{
    45  		Namespace: groups[slices.Index(subjectRegex.SubexpNames(), "subjectType")],
    46  		ObjectId:  groups[slices.Index(subjectRegex.SubexpNames(), "subjectID")],
    47  		Relation:  relation,
    48  	}
    49  }
    50  
    51  // ParseONR converts a string representation of an ONR to a proto object.
    52  func ParseONR(onr string) *core.ObjectAndRelation {
    53  	groups := onrRegex.FindStringSubmatch(onr)
    54  
    55  	if len(groups) == 0 {
    56  		return nil
    57  	}
    58  
    59  	return &core.ObjectAndRelation{
    60  		Namespace: groups[slices.Index(onrRegex.SubexpNames(), "resourceType")],
    61  		ObjectId:  groups[slices.Index(onrRegex.SubexpNames(), "resourceID")],
    62  		Relation:  groups[slices.Index(onrRegex.SubexpNames(), "resourceRel")],
    63  	}
    64  }
    65  
    66  // JoinRelRef joins the namespace and relation together into the same
    67  // format as `StringRR()`.
    68  func JoinRelRef(namespace, relation string) string { return namespace + "#" + relation }
    69  
    70  // MustSplitRelRef splits a string produced by `JoinRelRef()` and panics if
    71  // it fails.
    72  func MustSplitRelRef(relRef string) (namespace, relation string) {
    73  	var ok bool
    74  	namespace, relation, ok = strings.Cut(relRef, "#")
    75  	if !ok {
    76  		panic("improperly formatted relation reference")
    77  	}
    78  	return
    79  }
    80  
    81  // StringRR converts a RR object to a string.
    82  func StringRR(rr *core.RelationReference) string {
    83  	if rr == nil {
    84  		return ""
    85  	}
    86  
    87  	return JoinRelRef(rr.Namespace, rr.Relation)
    88  }
    89  
    90  // StringONR converts an ONR object to a string.
    91  func StringONR(onr *core.ObjectAndRelation) string {
    92  	if onr == nil {
    93  		return ""
    94  	}
    95  	if onr.Relation == Ellipsis {
    96  		return JoinObjectRef(onr.Namespace, onr.ObjectId)
    97  	}
    98  	return JoinRelRef(JoinObjectRef(onr.Namespace, onr.ObjectId), onr.Relation)
    99  }
   100  
   101  // StringsONRs converts ONR objects to a string slice, sorted.
   102  func StringsONRs(onrs []*core.ObjectAndRelation) []string {
   103  	onrstrings := make([]string, 0, len(onrs))
   104  	for _, onr := range onrs {
   105  		onrstrings = append(onrstrings, StringONR(onr))
   106  	}
   107  
   108  	sort.Strings(onrstrings)
   109  	return onrstrings
   110  }
   111  
   112  func OnrEqual(lhs, rhs *core.ObjectAndRelation) bool {
   113  	// Properties are sorted by highest to lowest cardinality to optimize for short-circuiting.
   114  	return lhs.ObjectId == rhs.ObjectId && lhs.Relation == rhs.Relation && lhs.Namespace == rhs.Namespace
   115  }
   116  
   117  func OnrEqualOrWildcard(tpl, target *core.ObjectAndRelation) bool {
   118  	return OnrEqual(tpl, target) || (tpl.ObjectId == PublicWildcard && tpl.Namespace == target.Namespace)
   119  }