1
0
Fork 0
mirror of https://github.com/anyproto/anytype-heart.git synced 2025-06-09 17:44:59 +09:00
anytype-heart/util/pbtypes/compare.go
2022-12-08 16:27:58 +01:00

513 lines
10 KiB
Go

package pbtypes
import (
"sort"
"github.com/gogo/protobuf/types"
"github.com/anytypeio/go-anytype-middleware/pkg/lib/pb/model"
"github.com/anytypeio/go-anytype-middleware/util/slice"
)
func StructEqualIgnore(det1 *types.Struct, det2 *types.Struct, excludeKeys []string) (equal bool) {
var m1, m2 map[string]*types.Value
if det1 == nil || det1.Fields == nil {
m1 = make(map[string]*types.Value)
} else {
m1 = det1.Fields
}
if det2 == nil || det2.Fields == nil {
m2 = make(map[string]*types.Value)
} else {
m2 = det2.Fields
}
for key, v1 := range m1 {
if slice.FindPos(excludeKeys, key) >= 0 {
continue
}
if v2, exists := m2[key]; !exists {
return false
} else if !v2.Equal(v1) {
return false
}
}
for key, _ := range m2 {
if slice.FindPos(excludeKeys, key) >= 0 {
continue
}
if _, exists := m1[key]; !exists {
return false
}
}
return true
}
// StructFilterKeys returns provided keys reusing underlying pb values pointers
func StructFilterKeys(st *types.Struct, filteredKeys []string) *types.Struct {
if st == nil || st.Fields == nil {
return st
}
m := make(map[string]*types.Value, len(st.Fields))
for k, v := range st.Fields {
if slice.FindPos(filteredKeys, k) > -1 {
m[k] = v
}
}
return &types.Struct{Fields: m}
}
// StructCutKeys excludes provided keys reusing underlying pb values pointers
func StructCutKeys(st *types.Struct, excludeKeys []string) *types.Struct {
if st == nil || st.Fields == nil {
return st
}
m := make(map[string]*types.Value, len(st.Fields))
for k, v := range st.Fields {
if slice.FindPos(excludeKeys, k) == -1 {
m[k] = v
}
}
return &types.Struct{Fields: m}
}
// StructDiff returns pb struct which contains:
// - st2 fields that not exist in st1
// - st2 fields that not equal to ones exist in st1
// - nil map value for st1 fields not exist in st2
// In case st1 and st2 are equal returns nil
func StructDiff(st1, st2 *types.Struct) *types.Struct {
var diff *types.Struct
if st1 == nil {
return st2
}
if st2 == nil {
diff = &types.Struct{Fields: map[string]*types.Value{}}
for k, _ := range st1.Fields {
diff.Fields[k] = nil
}
return diff
}
for k, v2 := range st2.Fields {
v1, ok := st1.Fields[k]
if !ok || !v1.Equal(v2) {
if diff == nil {
diff = &types.Struct{Fields: map[string]*types.Value{}}
}
diff.Fields[k] = v2
}
}
for k, _ := range st1.Fields {
_, ok := st2.Fields[k]
if !ok {
if diff == nil {
diff = &types.Struct{Fields: map[string]*types.Value{}}
}
diff.Fields[k] = nil
}
}
return diff
}
func StructMerge(st1, st2 *types.Struct, copyVals bool) *types.Struct {
var res *types.Struct
if st1 == nil || st1.Fields == nil {
return CopyStruct(st2)
}
if st2 == nil || st2.Fields == nil {
return CopyStruct(st1)
}
res = CopyStruct(st1)
for k, v := range st2.Fields {
res.Fields[k] = CopyVal(v)
}
return res
}
func RelationsDiff(rels1, rels2 []*model.Relation) (added []*model.Relation, updated []*model.Relation, removed []string) {
for i := 0; i < len(rels2); i++ {
if r := GetRelation(rels1, rels2[i].Key); r == nil {
added = append(added, rels2[i])
continue
} else {
if !RelationEqual(r, rels2[i]) {
updated = append(updated, rels2[i])
continue
}
}
}
for i := 0; i < len(rels1); i++ {
if r := GetRelation(rels2, rels1[i].Key); r == nil {
removed = append(removed, rels1[i].Key)
continue
}
}
return
}
func RelationsEqual(rels1 []*model.Relation, rels2 []*model.Relation) (equal bool) {
if len(rels1) != len(rels2) {
return false
}
for i := 0; i < len(rels2); i++ {
if !RelationEqual(rels1[i], rels2[i]) {
return false
}
}
return true
}
func RelationEqualOmitDictionary(rel1 *model.Relation, rel2 *model.Relation) (equal bool) {
if rel1 == nil && rel2 != nil {
return false
}
if rel2 == nil && rel1 != nil {
return false
}
if rel2 == nil && rel1 == nil {
return true
}
if rel1.Key != rel2.Key {
return false
}
if rel1.Format != rel2.Format {
return false
}
if rel1.Name != rel2.Name {
return false
}
if rel1.DefaultValue.Compare(rel2.DefaultValue) != 0 {
return false
}
if rel1.Hidden != rel2.Hidden {
return false
}
if rel1.ReadOnly != rel2.ReadOnly {
return false
}
if rel1.ReadOnlyRelation != rel2.ReadOnlyRelation {
return false
}
if rel1.Multi != rel2.Multi {
return false
}
if rel1.MaxCount != rel2.MaxCount {
return false
}
if !slice.SortedEquals(rel1.ObjectTypes, rel2.ObjectTypes) {
return false
}
return true
}
// RelationCompatible returns if provided relations are compatible in terms of underlying data format
// e.g. it is ok if relation can have a different name and selectDict, while having the same key and format
func RelationCompatible(rel1 *model.Relation, rel2 *model.Relation) (equal bool) {
if rel1 == nil && rel2 != nil {
return false
}
if rel2 == nil && rel1 != nil {
return false
}
if rel2 == nil && rel1 == nil {
return true
}
if rel1.Key != rel2.Key {
return false
}
if rel1.Format != rel2.Format {
return false
}
// todo: should we compare objectType here?
return true
}
func RelationEqual(rel1 *model.Relation, rel2 *model.Relation) (equal bool) {
if !RelationEqualOmitDictionary(rel1, rel2) {
return false
}
return RelationSelectDictEqual(rel1.SelectDict, rel2.SelectDict)
}
func RelationSelectDictEqual(dict1, dict2 []*model.RelationOption) bool {
if len(dict1) != len(dict2) {
return false
}
for i := 0; i < len(dict1); i++ {
if !OptionEqual(dict1[i], dict2[i]) {
return false
}
}
return true
}
func RelationSelectDictDiff(dict1, dict2 []*model.RelationOption) (added []*model.RelationOption, updated []*model.RelationOption, removed []string) {
for i := 0; i < len(dict2); i++ {
if opt := GetOption(dict1, dict2[i].Id); opt == nil {
added = append(added, dict2[i])
continue
} else {
if !OptionEqual(opt, dict2[i]) {
updated = append(updated, dict2[i])
continue
}
}
}
for i := 0; i < len(dict1); i++ {
if r := GetOption(dict2, dict1[i].Id); r == nil {
removed = append(removed, dict1[i].Id)
continue
}
}
return
}
func RelationSelectDictDiffOmitScope(dict1, dict2 []*model.RelationOption) (added []*model.RelationOption, updated []*model.RelationOption, removed []string) {
for i := 0; i < len(dict2); i++ {
if opt := GetOption(dict1, dict2[i].Id); opt == nil {
added = append(added, dict2[i])
continue
} else {
if !OptionEqualOmitScope(opt, dict2[i]) {
updated = append(updated, dict2[i])
continue
}
}
}
for i := 0; i < len(dict1); i++ {
if r := GetOption(dict2, dict1[i].Id); r == nil {
removed = append(removed, dict1[i].Id)
continue
}
}
return
}
func OptionEqualOmitScope(opt1, opt2 *model.RelationOption) bool {
if (opt1 == nil) && (opt2 != nil) {
return false
}
if (opt1 != nil) && (opt2 == nil) {
return false
}
if opt1 == nil && opt2 == nil {
return true
}
if opt1.Id != opt2.Id {
return false
}
if opt1.Text != opt2.Text {
return false
}
if opt1.Color != opt2.Color {
return false
}
return true
}
func OptionEqual(opt1, opt2 *model.RelationOption) bool {
if (opt1 == nil) && (opt2 != nil) {
return false
}
if (opt1 != nil) && (opt2 == nil) {
return false
}
if opt1 == nil && opt2 == nil {
return true
}
if opt1.Id != opt2.Id {
return false
}
if opt1.Text != opt2.Text {
return false
}
if opt1.Color != opt2.Color {
return false
}
if opt1.RelationKey != opt2.RelationKey {
return false
}
return true
}
func DataviewSortsEqualSorted(sorts1, sorts2 []*model.BlockContentDataviewSort) bool {
if len(sorts1) != len(sorts2) {
return false
}
for i := range sorts1 {
if !DataviewSortEqual(sorts1[i], sorts2[i]) {
return false
}
}
return true
}
func DataviewFiltersEqualSorted(filters1, filters2 []*model.BlockContentDataviewFilter) bool {
if len(filters1) != len(filters2) {
return false
}
for i := range filters1 {
if !DataviewFilterEqual(filters1[i], filters2[i]) {
return false
}
}
return true
}
func DataviewViewsEqualSorted(views1, views2 []*model.BlockContentDataviewView) bool {
if len(views1) != len(views2) {
return false
}
for i := range views1 {
if !DataviewViewEqual(views1[i], views2[i]) {
return false
}
}
return true
}
func DataviewSortEqual(sort1, sort2 *model.BlockContentDataviewSort) bool {
if sort1 == nil && sort2 != nil {
return false
}
if sort1 != nil && sort2 == nil {
return false
}
if sort1 == nil && sort2 == nil {
return true
}
if sort1.RelationKey != sort2.RelationKey {
return false
}
if sort1.Type != sort2.Type {
return false
}
return true
}
func DataviewFilterEqual(filter1, filter2 *model.BlockContentDataviewFilter) bool {
if filter1 == nil && filter2 != nil {
return false
}
if filter1 != nil && filter2 == nil {
return false
}
if filter1 == nil && filter2 == nil {
return true
}
if filter1.RelationKey != filter2.RelationKey {
return false
}
if filter1.Condition != filter2.Condition {
return false
}
if filter1.Operator != filter2.Operator {
return false
}
if filter1.RelationProperty != filter2.RelationProperty {
return false
}
if filter1.Value.Compare(filter2.Value) != 0 {
return false
}
return true
}
func DataviewViewEqual(view1, view2 *model.BlockContentDataviewView) bool {
if view1 == nil && view2 != nil {
return false
}
if view1 != nil && view2 == nil {
return false
}
if view1 == nil && view2 == nil {
return true
}
if view1.Id != view2.Id {
return false
}
if view1.Name != view2.Name {
return false
}
if view1.Type != view2.Type {
return false
}
if !DataviewFiltersEqualSorted(view1.Filters, view2.Filters) {
return false
}
if !DataviewSortsEqualSorted(view1.Sorts, view2.Sorts) {
return false
}
if len(view1.Relations) != len(view2.Relations) {
return false
// todo add relations check
}
return true
}
func SortedRange(s *types.Struct, f func(k string, v *types.Value)) {
if s == nil || s.Fields == nil {
return
}
var keys = make([]string, 0, len(s.Fields))
for k := range s.Fields {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
f(k, s.Fields[k])
}
}
func RelationOptionsFilter(options []*model.RelationOption, f func(option *model.RelationOption) bool) []*model.RelationOption {
if len(options) == 0 {
return nil
}
res := make([]*model.RelationOption, 0, len(options))
for i := range options {
if f(options[i]) {
res = append(res, options[i])
}
}
return res
}