diff --git a/cmd/NewRoleHelperGenerator/main.go b/cmd/NewRoleHelperGenerator/main.go new file mode 100644 index 0000000..54fd8d6 --- /dev/null +++ b/cmd/NewRoleHelperGenerator/main.go @@ -0,0 +1,195 @@ +/* +Tool for generating helper functions for storage.Role structs inside of the storage package +It generates the following functions: + - CollapseRolesIntoOne: Collapse a list of roles into one singular role. Each value will be set to the + value of the role with the highest priority + - RoleDeepCopy: Copy a role, including all arrays. Every value will be copied too + - CompareRoles: Compare two roles. Returns true only if all fields are equal + (if one of the fields is nil, that field is seen as equal) +*/ +package main + +import ( + "flag" + "fmt" + "io" + "os" + "regexp" + "strings" + + "git.mstar.dev/mstar/goutils/sliceutils" +) + +var findRoleStructRegex = regexp.MustCompile(`type Role struct \{([\s\S]+)\}\n\n/\*`) + +var ( + flagInputFile = flag.String("input", "", "Specify the input file. If empty, read from stdin") + flagOutputFile = flag.String( + "output", + "", + "Specify the output file. If empty, writes to stdout", + ) + flagOutputModule = flag.String( + "mod", + "", + "Module for the output file. If empty, use mod of input file", + ) +) + +func main() { + flag.Parse() + var input io.Reader + var output io.Writer + + if *flagInputFile == "" { + input = os.Stdin + } else { + file, err := os.Open(*flagInputFile) + if err != nil { + panic(err) + } + defer file.Close() + input = file + } + if *flagOutputFile == "" { + output = os.Stdout + } else { + file, err := os.Create(*flagOutputFile) + if err != nil { + panic(err) + } + defer file.Close() + output = file + } + + data, err := io.ReadAll(input) + if err != nil { + panic(err) + } + if !findRoleStructRegex.Match(data) { + panic("Input doesn't contain role struct") + } + content := findRoleStructRegex.FindStringSubmatch(string(data))[1] + lines := strings.Split(content, "\n") + lines = sliceutils.Map(lines, func(t string) string { return strings.TrimSpace(t) }) + importantLines := sliceutils.Filter(lines, func(t string) bool { + if strings.HasPrefix(t, "//") { + return false + } + if strings.Contains(t, "gorm.Model") { + return false + } + data := sliceutils.Filter(strings.Split(t, " "), func(t string) bool { return t != "" }) + if len(data) < 2 { + return false + } + if !strings.HasPrefix(data[1], "*") && !strings.HasPrefix(data[1], "[]") { + return false + } + return true + }) + nameTypeMap := map[string]string{} + for _, line := range importantLines { + parts := sliceutils.Filter(strings.Split(line, " "), func(t string) bool { return t != "" }) + nameTypeMap[parts[0]] = parts[1] + } + pkgString, _, _ := strings.Cut(string(data), "\n") + + outBuilder := strings.Builder{} + outBuilder.WriteString(`// Code generated by cmd/RolesGenerator DO NOT EDIT. +// If you need to refresh the content, run go generate again +`) + if *flagOutputModule == "" { + outBuilder.WriteString(pkgString + "\n\n") + } else { + outBuilder.WriteString("package " + *flagOutputModule + "\n\n") + } + outBuilder.WriteString( + `import ( + "slices" + "git.mstar.dev/mstar/goutils/sliceutils" + "git.mstar.dev/mstar/linstrom/storage-new/models" +) + +`, + ) + + // Build role collapse function + outBuilder.WriteString( + `func CollapseRolesIntoOne(roles ...models.Role) models.Role { + startingRole := RoleDeepCopy(models.DefaultUserRole) + slices.SortFunc(roles, func(a, b models.Role) int { return int(int64(a.Priority)-int64(b.Priority)) }) + for _, role := range roles { +`) + // Write all the stupid conditions here + for valName, valType := range nameTypeMap { + if strings.HasPrefix(valType, "[]") { + outBuilder.WriteString(fmt.Sprintf(` if role.%s != nil { + startingRole.%s = append(startingRole.%s, role.%s...) + } +`, valName, valName, valName, valName)) + } else { + outBuilder.WriteString(fmt.Sprintf(` if role.%s != nil { + *startingRole.%s = *role.%s + } +`, valName, valName, valName)) + } + } + // Then finish up with the end of the function + outBuilder.WriteString( + ` } + return startingRole +} +`) + + // Then build the deep copy function + outBuilder.WriteString("\nfunc RoleDeepCopy(o models.Role) models.Role {\n") + outBuilder.WriteString(` n := models.Role{} + n.Model = o.Model + n.Name = o.Name + n.Priority = o.Priority + n.IsUserRole = o.IsUserRole + n.IsBuiltIn = o.IsBuiltIn +`) + for valName, valType := range nameTypeMap { + if strings.HasPrefix(valType, "[]") { + outBuilder.WriteString(fmt.Sprintf(" n.%s = slices.Clone(o.%s)\n", valName, valName)) + } else { + outBuilder.WriteString(fmt.Sprintf(` if o.%s == nil { n.%s = nil } else { + t := *o.%s + n.%s = &t + } +`, valName, valName, valName, valName)) + } + } + outBuilder.WriteString(" return n\n}\n\n") + + // Build compare function + outBuilder.WriteString("func CompareRoles(a, b *models.Role) bool {\n") + outBuilder.WriteString(" return ") + lastName, lastType := "", "" + for valName, valType := range nameTypeMap { + lastName = valName + lastType = valType + outBuilder.WriteString(fmt.Sprintf("(a.%s == nil || b.%s == nil || ", valName, valName)) + if strings.HasPrefix(valType, "[]") { + outBuilder.WriteString( + fmt.Sprintf("sliceutils.CompareUnordered(a.%s,b.%s)) && ", valName, valName), + ) + } else { + outBuilder.WriteString(fmt.Sprintf("a.%s == b.%s) && ", valName, valName)) + } + } + outBuilder.WriteString("(a == nil || b == nil || ") + if strings.HasPrefix(lastType, "[]") { + outBuilder.WriteString( + fmt.Sprintf("sliceutils.CompareUnordered(a.%s,b.%s))", lastName, lastName), + ) + } else { + outBuilder.WriteString(fmt.Sprintf("a.%s == b.%s)", lastName, lastName)) + } + outBuilder.WriteString("\n}") + + // And write the entire thing to the output + fmt.Fprint(output, outBuilder.String()) +} diff --git a/storage-new/role_generated.go b/storage-new/role_generated.go new file mode 100644 index 0000000..aa31fc1 --- /dev/null +++ b/storage-new/role_generated.go @@ -0,0 +1,340 @@ +// Code generated by cmd/RolesGenerator DO NOT EDIT. +// If you need to refresh the content, run go generate again +package storage + +import ( + "slices" + "git.mstar.dev/mstar/goutils/sliceutils" + "git.mstar.dev/mstar/linstrom/storage-new/models" +) + +func CollapseRolesIntoOne(roles ...models.Role) models.Role { + startingRole := RoleDeepCopy(models.DefaultUserRole) + slices.SortFunc(roles, func(a, b models.Role) int { return int(int64(a.Priority)-int64(b.Priority)) }) + for _, role := range roles { + if role.CanBoost != nil { + *startingRole.CanBoost = *role.CanBoost + } + if role.CanIncludeSurvey != nil { + *startingRole.CanIncludeSurvey = *role.CanIncludeSurvey + } + if role.CanMentionOthers != nil { + *startingRole.CanMentionOthers = *role.CanMentionOthers + } + if role.CanViewDeletedNotes != nil { + *startingRole.CanViewDeletedNotes = *role.CanViewDeletedNotes + } + if role.CanFederateBsky != nil { + *startingRole.CanFederateBsky = *role.CanFederateBsky + } + if role.CanSubmitReports != nil { + *startingRole.CanSubmitReports = *role.CanSubmitReports + } + if role.AutoCwPostsText != nil { + *startingRole.AutoCwPostsText = *role.AutoCwPostsText + } + if role.CanAssignRoles != nil { + *startingRole.CanAssignRoles = *role.CanAssignRoles + } + if role.ScanCreatedPrivateNotes != nil { + *startingRole.ScanCreatedPrivateNotes = *role.ScanCreatedPrivateNotes + } + if role.DisallowInteractionsWith != nil { + startingRole.DisallowInteractionsWith = append(startingRole.DisallowInteractionsWith, role.DisallowInteractionsWith...) + } + if role.CanDeleteNotes != nil { + *startingRole.CanDeleteNotes = *role.CanDeleteNotes + } + if role.CanConfirmWithheldNotes != nil { + *startingRole.CanConfirmWithheldNotes = *role.CanConfirmWithheldNotes + } + if role.CanSendMedia != nil { + *startingRole.CanSendMedia = *role.CanSendMedia + } + if role.CanSendFollowerOnlyNotes != nil { + *startingRole.CanSendFollowerOnlyNotes = *role.CanSendFollowerOnlyNotes + } + if role.CanSendReplies != nil { + *startingRole.CanSendReplies = *role.CanSendReplies + } + if role.ScanCreatedPublicNotes != nil { + *startingRole.ScanCreatedPublicNotes = *role.ScanCreatedPublicNotes + } + if role.CanOverwriteDisplayNames != nil { + *startingRole.CanOverwriteDisplayNames = *role.CanOverwriteDisplayNames + } + if role.AutoCwPosts != nil { + *startingRole.AutoCwPosts = *role.AutoCwPosts + } + if role.CanQuote != nil { + *startingRole.CanQuote = *role.CanQuote + } + if role.CanIncludeLinks != nil { + *startingRole.CanIncludeLinks = *role.CanIncludeLinks + } + if role.CanLogin != nil { + *startingRole.CanLogin = *role.CanLogin + } + if role.MentionLimit != nil { + *startingRole.MentionLimit = *role.MentionLimit + } + if role.FullAdmin != nil { + *startingRole.FullAdmin = *role.FullAdmin + } + if role.CanSendCustomReactions != nil { + *startingRole.CanSendCustomReactions = *role.CanSendCustomReactions + } + if role.CanSendPublicNotes != nil { + *startingRole.CanSendPublicNotes = *role.CanSendPublicNotes + } + if role.HasMentionCountLimit != nil { + *startingRole.HasMentionCountLimit = *role.HasMentionCountLimit + } + if role.ScanCreatedFollowerOnlyNotes != nil { + *startingRole.ScanCreatedFollowerOnlyNotes = *role.ScanCreatedFollowerOnlyNotes + } + if role.WithholdNotesForManualApproval != nil { + *startingRole.WithholdNotesForManualApproval = *role.WithholdNotesForManualApproval + } + if role.WithholdNotesBasedOnRegex != nil { + *startingRole.WithholdNotesBasedOnRegex = *role.WithholdNotesBasedOnRegex + } + if role.WithholdNotesRegexes != nil { + startingRole.WithholdNotesRegexes = append(startingRole.WithholdNotesRegexes, role.WithholdNotesRegexes...) + } + if role.CanAffectOtherAdmins != nil { + *startingRole.CanAffectOtherAdmins = *role.CanAffectOtherAdmins + } + if role.CanSendCustomEmotes != nil { + *startingRole.CanSendCustomEmotes = *role.CanSendCustomEmotes + } + if role.CanSendPrivateNotes != nil { + *startingRole.CanSendPrivateNotes = *role.CanSendPrivateNotes + } + if role.CanChangeDisplayName != nil { + *startingRole.CanChangeDisplayName = *role.CanChangeDisplayName + } + if role.ScanCreatedLocalNotes != nil { + *startingRole.ScanCreatedLocalNotes = *role.ScanCreatedLocalNotes + } + if role.CanManageAvatarDecorations != nil { + *startingRole.CanManageAvatarDecorations = *role.CanManageAvatarDecorations + } + if role.CanDeleteAccounts != nil { + *startingRole.CanDeleteAccounts = *role.CanDeleteAccounts + } + if role.CanSendAnnouncements != nil { + *startingRole.CanSendAnnouncements = *role.CanSendAnnouncements + } + if role.CanSendLocalNotes != nil { + *startingRole.CanSendLocalNotes = *role.CanSendLocalNotes + } + if role.CanSupressInteractionsBetweenUsers != nil { + *startingRole.CanSupressInteractionsBetweenUsers = *role.CanSupressInteractionsBetweenUsers + } + if role.CanRecoverDeletedNotes != nil { + *startingRole.CanRecoverDeletedNotes = *role.CanRecoverDeletedNotes + } + if role.CanManageAds != nil { + *startingRole.CanManageAds = *role.CanManageAds + } + if role.CanFederateFedi != nil { + *startingRole.CanFederateFedi = *role.CanFederateFedi + } + if role.AutoNsfwMedia != nil { + *startingRole.AutoNsfwMedia = *role.AutoNsfwMedia + } + if role.CanManageCustomEmotes != nil { + *startingRole.CanManageCustomEmotes = *role.CanManageCustomEmotes + } + } + return startingRole +} + +func RoleDeepCopy(o models.Role) models.Role { + n := models.Role{} + n.Model = o.Model + n.Name = o.Name + n.Priority = o.Priority + n.IsUserRole = o.IsUserRole + n.IsBuiltIn = o.IsBuiltIn + if o.AutoCwPostsText == nil { n.AutoCwPostsText = nil } else { + t := *o.AutoCwPostsText + n.AutoCwPostsText = &t + } + if o.CanAssignRoles == nil { n.CanAssignRoles = nil } else { + t := *o.CanAssignRoles + n.CanAssignRoles = &t + } + if o.CanFederateBsky == nil { n.CanFederateBsky = nil } else { + t := *o.CanFederateBsky + n.CanFederateBsky = &t + } + if o.CanSubmitReports == nil { n.CanSubmitReports = nil } else { + t := *o.CanSubmitReports + n.CanSubmitReports = &t + } + if o.CanSendReplies == nil { n.CanSendReplies = nil } else { + t := *o.CanSendReplies + n.CanSendReplies = &t + } + if o.ScanCreatedPublicNotes == nil { n.ScanCreatedPublicNotes = nil } else { + t := *o.ScanCreatedPublicNotes + n.ScanCreatedPublicNotes = &t + } + if o.ScanCreatedPrivateNotes == nil { n.ScanCreatedPrivateNotes = nil } else { + t := *o.ScanCreatedPrivateNotes + n.ScanCreatedPrivateNotes = &t + } + n.DisallowInteractionsWith = slices.Clone(o.DisallowInteractionsWith) + if o.CanDeleteNotes == nil { n.CanDeleteNotes = nil } else { + t := *o.CanDeleteNotes + n.CanDeleteNotes = &t + } + if o.CanConfirmWithheldNotes == nil { n.CanConfirmWithheldNotes = nil } else { + t := *o.CanConfirmWithheldNotes + n.CanConfirmWithheldNotes = &t + } + if o.CanSendMedia == nil { n.CanSendMedia = nil } else { + t := *o.CanSendMedia + n.CanSendMedia = &t + } + if o.CanSendFollowerOnlyNotes == nil { n.CanSendFollowerOnlyNotes = nil } else { + t := *o.CanSendFollowerOnlyNotes + n.CanSendFollowerOnlyNotes = &t + } + if o.CanOverwriteDisplayNames == nil { n.CanOverwriteDisplayNames = nil } else { + t := *o.CanOverwriteDisplayNames + n.CanOverwriteDisplayNames = &t + } + if o.CanLogin == nil { n.CanLogin = nil } else { + t := *o.CanLogin + n.CanLogin = &t + } + if o.MentionLimit == nil { n.MentionLimit = nil } else { + t := *o.MentionLimit + n.MentionLimit = &t + } + if o.AutoCwPosts == nil { n.AutoCwPosts = nil } else { + t := *o.AutoCwPosts + n.AutoCwPosts = &t + } + if o.CanQuote == nil { n.CanQuote = nil } else { + t := *o.CanQuote + n.CanQuote = &t + } + if o.CanIncludeLinks == nil { n.CanIncludeLinks = nil } else { + t := *o.CanIncludeLinks + n.CanIncludeLinks = &t + } + if o.HasMentionCountLimit == nil { n.HasMentionCountLimit = nil } else { + t := *o.HasMentionCountLimit + n.HasMentionCountLimit = &t + } + if o.ScanCreatedFollowerOnlyNotes == nil { n.ScanCreatedFollowerOnlyNotes = nil } else { + t := *o.ScanCreatedFollowerOnlyNotes + n.ScanCreatedFollowerOnlyNotes = &t + } + if o.FullAdmin == nil { n.FullAdmin = nil } else { + t := *o.FullAdmin + n.FullAdmin = &t + } + if o.CanSendCustomReactions == nil { n.CanSendCustomReactions = nil } else { + t := *o.CanSendCustomReactions + n.CanSendCustomReactions = &t + } + if o.CanSendPublicNotes == nil { n.CanSendPublicNotes = nil } else { + t := *o.CanSendPublicNotes + n.CanSendPublicNotes = &t + } + if o.CanChangeDisplayName == nil { n.CanChangeDisplayName = nil } else { + t := *o.CanChangeDisplayName + n.CanChangeDisplayName = &t + } + if o.ScanCreatedLocalNotes == nil { n.ScanCreatedLocalNotes = nil } else { + t := *o.ScanCreatedLocalNotes + n.ScanCreatedLocalNotes = &t + } + if o.WithholdNotesForManualApproval == nil { n.WithholdNotesForManualApproval = nil } else { + t := *o.WithholdNotesForManualApproval + n.WithholdNotesForManualApproval = &t + } + if o.WithholdNotesBasedOnRegex == nil { n.WithholdNotesBasedOnRegex = nil } else { + t := *o.WithholdNotesBasedOnRegex + n.WithholdNotesBasedOnRegex = &t + } + n.WithholdNotesRegexes = slices.Clone(o.WithholdNotesRegexes) + if o.CanAffectOtherAdmins == nil { n.CanAffectOtherAdmins = nil } else { + t := *o.CanAffectOtherAdmins + n.CanAffectOtherAdmins = &t + } + if o.CanSendCustomEmotes == nil { n.CanSendCustomEmotes = nil } else { + t := *o.CanSendCustomEmotes + n.CanSendCustomEmotes = &t + } + if o.CanSendPrivateNotes == nil { n.CanSendPrivateNotes = nil } else { + t := *o.CanSendPrivateNotes + n.CanSendPrivateNotes = &t + } + if o.CanManageAvatarDecorations == nil { n.CanManageAvatarDecorations = nil } else { + t := *o.CanManageAvatarDecorations + n.CanManageAvatarDecorations = &t + } + if o.CanDeleteAccounts == nil { n.CanDeleteAccounts = nil } else { + t := *o.CanDeleteAccounts + n.CanDeleteAccounts = &t + } + if o.CanRecoverDeletedNotes == nil { n.CanRecoverDeletedNotes = nil } else { + t := *o.CanRecoverDeletedNotes + n.CanRecoverDeletedNotes = &t + } + if o.CanManageAds == nil { n.CanManageAds = nil } else { + t := *o.CanManageAds + n.CanManageAds = &t + } + if o.CanSendAnnouncements == nil { n.CanSendAnnouncements = nil } else { + t := *o.CanSendAnnouncements + n.CanSendAnnouncements = &t + } + if o.CanSendLocalNotes == nil { n.CanSendLocalNotes = nil } else { + t := *o.CanSendLocalNotes + n.CanSendLocalNotes = &t + } + if o.CanSupressInteractionsBetweenUsers == nil { n.CanSupressInteractionsBetweenUsers = nil } else { + t := *o.CanSupressInteractionsBetweenUsers + n.CanSupressInteractionsBetweenUsers = &t + } + if o.CanManageCustomEmotes == nil { n.CanManageCustomEmotes = nil } else { + t := *o.CanManageCustomEmotes + n.CanManageCustomEmotes = &t + } + if o.CanFederateFedi == nil { n.CanFederateFedi = nil } else { + t := *o.CanFederateFedi + n.CanFederateFedi = &t + } + if o.AutoNsfwMedia == nil { n.AutoNsfwMedia = nil } else { + t := *o.AutoNsfwMedia + n.AutoNsfwMedia = &t + } + if o.CanMentionOthers == nil { n.CanMentionOthers = nil } else { + t := *o.CanMentionOthers + n.CanMentionOthers = &t + } + if o.CanViewDeletedNotes == nil { n.CanViewDeletedNotes = nil } else { + t := *o.CanViewDeletedNotes + n.CanViewDeletedNotes = &t + } + if o.CanBoost == nil { n.CanBoost = nil } else { + t := *o.CanBoost + n.CanBoost = &t + } + if o.CanIncludeSurvey == nil { n.CanIncludeSurvey = nil } else { + t := *o.CanIncludeSurvey + n.CanIncludeSurvey = &t + } + return n +} + +func CompareRoles(a, b *models.Role) bool { + return (a.CanSendPublicNotes == nil || b.CanSendPublicNotes == nil || a.CanSendPublicNotes == b.CanSendPublicNotes) && (a.HasMentionCountLimit == nil || b.HasMentionCountLimit == nil || a.HasMentionCountLimit == b.HasMentionCountLimit) && (a.ScanCreatedFollowerOnlyNotes == nil || b.ScanCreatedFollowerOnlyNotes == nil || a.ScanCreatedFollowerOnlyNotes == b.ScanCreatedFollowerOnlyNotes) && (a.FullAdmin == nil || b.FullAdmin == nil || a.FullAdmin == b.FullAdmin) && (a.CanSendCustomReactions == nil || b.CanSendCustomReactions == nil || a.CanSendCustomReactions == b.CanSendCustomReactions) && (a.CanSendPrivateNotes == nil || b.CanSendPrivateNotes == nil || a.CanSendPrivateNotes == b.CanSendPrivateNotes) && (a.CanChangeDisplayName == nil || b.CanChangeDisplayName == nil || a.CanChangeDisplayName == b.CanChangeDisplayName) && (a.ScanCreatedLocalNotes == nil || b.ScanCreatedLocalNotes == nil || a.ScanCreatedLocalNotes == b.ScanCreatedLocalNotes) && (a.WithholdNotesForManualApproval == nil || b.WithholdNotesForManualApproval == nil || a.WithholdNotesForManualApproval == b.WithholdNotesForManualApproval) && (a.WithholdNotesBasedOnRegex == nil || b.WithholdNotesBasedOnRegex == nil || a.WithholdNotesBasedOnRegex == b.WithholdNotesBasedOnRegex) && (a.WithholdNotesRegexes == nil || b.WithholdNotesRegexes == nil || sliceutils.CompareUnordered(a.WithholdNotesRegexes,b.WithholdNotesRegexes)) && (a.CanAffectOtherAdmins == nil || b.CanAffectOtherAdmins == nil || a.CanAffectOtherAdmins == b.CanAffectOtherAdmins) && (a.CanSendCustomEmotes == nil || b.CanSendCustomEmotes == nil || a.CanSendCustomEmotes == b.CanSendCustomEmotes) && (a.CanDeleteAccounts == nil || b.CanDeleteAccounts == nil || a.CanDeleteAccounts == b.CanDeleteAccounts) && (a.CanManageAvatarDecorations == nil || b.CanManageAvatarDecorations == nil || a.CanManageAvatarDecorations == b.CanManageAvatarDecorations) && (a.CanSupressInteractionsBetweenUsers == nil || b.CanSupressInteractionsBetweenUsers == nil || a.CanSupressInteractionsBetweenUsers == b.CanSupressInteractionsBetweenUsers) && (a.CanRecoverDeletedNotes == nil || b.CanRecoverDeletedNotes == nil || a.CanRecoverDeletedNotes == b.CanRecoverDeletedNotes) && (a.CanManageAds == nil || b.CanManageAds == nil || a.CanManageAds == b.CanManageAds) && (a.CanSendAnnouncements == nil || b.CanSendAnnouncements == nil || a.CanSendAnnouncements == b.CanSendAnnouncements) && (a.CanSendLocalNotes == nil || b.CanSendLocalNotes == nil || a.CanSendLocalNotes == b.CanSendLocalNotes) && (a.AutoNsfwMedia == nil || b.AutoNsfwMedia == nil || a.AutoNsfwMedia == b.AutoNsfwMedia) && (a.CanManageCustomEmotes == nil || b.CanManageCustomEmotes == nil || a.CanManageCustomEmotes == b.CanManageCustomEmotes) && (a.CanFederateFedi == nil || b.CanFederateFedi == nil || a.CanFederateFedi == b.CanFederateFedi) && (a.CanIncludeSurvey == nil || b.CanIncludeSurvey == nil || a.CanIncludeSurvey == b.CanIncludeSurvey) && (a.CanMentionOthers == nil || b.CanMentionOthers == nil || a.CanMentionOthers == b.CanMentionOthers) && (a.CanViewDeletedNotes == nil || b.CanViewDeletedNotes == nil || a.CanViewDeletedNotes == b.CanViewDeletedNotes) && (a.CanBoost == nil || b.CanBoost == nil || a.CanBoost == b.CanBoost) && (a.CanSubmitReports == nil || b.CanSubmitReports == nil || a.CanSubmitReports == b.CanSubmitReports) && (a.AutoCwPostsText == nil || b.AutoCwPostsText == nil || a.AutoCwPostsText == b.AutoCwPostsText) && (a.CanAssignRoles == nil || b.CanAssignRoles == nil || a.CanAssignRoles == b.CanAssignRoles) && (a.CanFederateBsky == nil || b.CanFederateBsky == nil || a.CanFederateBsky == b.CanFederateBsky) && (a.CanSendFollowerOnlyNotes == nil || b.CanSendFollowerOnlyNotes == nil || a.CanSendFollowerOnlyNotes == b.CanSendFollowerOnlyNotes) && (a.CanSendReplies == nil || b.CanSendReplies == nil || a.CanSendReplies == b.CanSendReplies) && (a.ScanCreatedPublicNotes == nil || b.ScanCreatedPublicNotes == nil || a.ScanCreatedPublicNotes == b.ScanCreatedPublicNotes) && (a.ScanCreatedPrivateNotes == nil || b.ScanCreatedPrivateNotes == nil || a.ScanCreatedPrivateNotes == b.ScanCreatedPrivateNotes) && (a.DisallowInteractionsWith == nil || b.DisallowInteractionsWith == nil || sliceutils.CompareUnordered(a.DisallowInteractionsWith,b.DisallowInteractionsWith)) && (a.CanDeleteNotes == nil || b.CanDeleteNotes == nil || a.CanDeleteNotes == b.CanDeleteNotes) && (a.CanConfirmWithheldNotes == nil || b.CanConfirmWithheldNotes == nil || a.CanConfirmWithheldNotes == b.CanConfirmWithheldNotes) && (a.CanSendMedia == nil || b.CanSendMedia == nil || a.CanSendMedia == b.CanSendMedia) && (a.CanOverwriteDisplayNames == nil || b.CanOverwriteDisplayNames == nil || a.CanOverwriteDisplayNames == b.CanOverwriteDisplayNames) && (a.CanIncludeLinks == nil || b.CanIncludeLinks == nil || a.CanIncludeLinks == b.CanIncludeLinks) && (a.CanLogin == nil || b.CanLogin == nil || a.CanLogin == b.CanLogin) && (a.MentionLimit == nil || b.MentionLimit == nil || a.MentionLimit == b.MentionLimit) && (a.AutoCwPosts == nil || b.AutoCwPosts == nil || a.AutoCwPosts == b.AutoCwPosts) && (a.CanQuote == nil || b.CanQuote == nil || a.CanQuote == b.CanQuote) && (a == nil || b == nil || a.CanQuote == b.CanQuote) +} \ No newline at end of file