func Parse(tpl string) *v0.RelationTuple {
	groups := parserRegex.FindStringSubmatch(tpl)
	if len(groups) == 0 {
		return nil
	}
	subjectRelation := ellipsis
	subjectRelIndex := stringz.SliceIndex(parserRegex.SubexpNames(), "subjectRel")
	if len(groups[subjectRelIndex]) > 0 {
		subjectRelation = groups[subjectRelIndex]
	}
	return &v0.RelationTuple{
		ObjectAndRelation: &v0.ObjectAndRelation{
			Namespace: groups[stringz.SliceIndex(parserRegex.SubexpNames(), "resourceType")],
			ObjectId:  groups[stringz.SliceIndex(parserRegex.SubexpNames(), "resourceID")],
			Relation:  groups[stringz.SliceIndex(parserRegex.SubexpNames(), "resourceRel")],
		},
		User: &v0.User{UserOneof: &v0.User_Userset{Userset: &v0.ObjectAndRelation{
			Namespace: groups[stringz.SliceIndex(parserRegex.SubexpNames(), "subjectType")],
			ObjectId:  groups[stringz.SliceIndex(parserRegex.SubexpNames(), "subjectID")],
			Relation:  subjectRelation,
		}}},
	}
}
Parse unmarshals the string form of a Tuple and returns nil if there is a
failure.

This function treats both missing and ellipsis relations equally.
package tuple
import (
	"fmt"
	"regexp"
	v0 "github.com/authzed/authzed-go/proto/authzed/api/v0"
	v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
	"github.com/jzelinskie/stringz"
)
const (
	ellipsis = "..."
)
const (
	namespaceNameExpr = "([a-z][a-z0-9_]{2,61}[a-z0-9]/)?[a-z][a-z0-9_]{2,62}[a-z0-9]"
	objectIDExpr      = "[a-zA-Z0-9_][a-zA-Z0-9/_-]{0,127}"
	relationExpr      = "[a-z][a-z0-9_]{2,62}[a-z0-9]"
)
var onrExpr = fmt.Sprintf(
	`(?P<resourceType>(%s)):(?P<resourceID>%s)#(?P<resourceRel>%s)`,
	namespaceNameExpr,
	objectIDExpr,
	relationExpr,
)
var subjectExpr = fmt.Sprintf(
	`(?P<subjectType>(%s)):(?P<subjectID>%s)(#(?P<subjectRel>%s|\.\.\.))?`,
	namespaceNameExpr,
	objectIDExpr,
	relationExpr,
)
var (
	onrRegex     = regexp.MustCompile(fmt.Sprintf("^%s$", onrExpr))
	subjectRegex = regexp.MustCompile(fmt.Sprintf("^%s$", subjectExpr))
)
var parserRegex = regexp.MustCompile(
	fmt.Sprintf(
		`^%s@%s$`,
		onrExpr,
		subjectExpr,
	),
)
func String(tpl *v0.RelationTuple) string {
	if tpl == nil || tpl.ObjectAndRelation == nil || tpl.User == nil || tpl.User.GetUserset() == nil {
		return ""
	}
	return fmt.Sprintf("%s@%s", StringONR(tpl.ObjectAndRelation), StringONR(tpl.User.GetUserset()))
}
func MustRelString(tpl *v1.Relationship) string {
	return String(MustFromRelationship(tpl))
}
func MustParse(tpl string) *v0.RelationTuple {
	if parsed := Parse(tpl); parsed != nil {
		return parsed
	}
	panic("failed to parse tuple")
}
func RelString(tpl *v1.Relationship) string {
	return String(FromRelationship(tpl))
}