github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/appdef/impl_unique.go (about)

     1  /*
     2   * Copyright (c) 2023-present Sigma-Soft, Ltd.
     3   * @author: Nikolay Nikitin
     4   */
     5  
     6  package appdef
     7  
     8  import (
     9  	"fmt"
    10  	"slices"
    11  )
    12  
    13  // # Implements:
    14  //   - IUnique
    15  type unique struct {
    16  	comment
    17  	name   QName
    18  	fields []IField
    19  }
    20  
    21  func newUnique(name QName, fieldNames []FieldName, fields IFields) *unique {
    22  	u := &unique{
    23  		name:   name,
    24  		fields: make([]IField, 0),
    25  	}
    26  	slices.Sort(fieldNames)
    27  	for _, f := range fieldNames {
    28  		fld := fields.Field(f)
    29  		if fld == nil {
    30  			panic(ErrFieldNotFound(f))
    31  		}
    32  		u.fields = append(u.fields, fld)
    33  	}
    34  	return u
    35  }
    36  
    37  func (u unique) Name() QName {
    38  	return u.name
    39  }
    40  
    41  func (u unique) Fields() []IField {
    42  	return u.fields
    43  }
    44  
    45  func (u unique) String() string {
    46  	return fmt.Sprintf("unique «%v»", u.name)
    47  }
    48  
    49  // # Implements:
    50  //   - IUniques
    51  type uniques struct {
    52  	app     *appDef
    53  	fields  IFields
    54  	uniques map[QName]IUnique
    55  	field   IField
    56  }
    57  
    58  func makeUniques(app *appDef, fields IFields) uniques {
    59  	uu := uniques{
    60  		app:     app,
    61  		fields:  fields,
    62  		uniques: make(map[QName]IUnique),
    63  	}
    64  	return uu
    65  }
    66  
    67  func (uu *uniques) setUniqueField(name FieldName) {
    68  	if name == NullName {
    69  		uu.field = nil
    70  		return
    71  	}
    72  	if ok, err := ValidFieldName(name); !ok {
    73  		panic((fmt.Errorf("unique field name «%v» is invalid: %w", name, err)))
    74  	}
    75  
    76  	fld := uu.fields.Field(name)
    77  	if fld == nil {
    78  		panic(ErrFieldNotFound(name))
    79  	}
    80  
    81  	uu.field = fld
    82  }
    83  
    84  func (uu uniques) UniqueByName(name QName) IUnique {
    85  	if u, ok := uu.uniques[name]; ok {
    86  		return u
    87  	}
    88  	return nil
    89  }
    90  
    91  func (uu uniques) UniqueCount() int {
    92  	return len(uu.uniques)
    93  }
    94  
    95  func (uu uniques) UniqueField() IField {
    96  	return uu.field
    97  }
    98  
    99  func (uu uniques) Uniques() map[QName]IUnique {
   100  	return uu.uniques
   101  }
   102  
   103  func (uu *uniques) addUnique(name QName, fields []FieldName, comment ...string) {
   104  	if name == NullQName {
   105  		panic(ErrMissed("unique name"))
   106  	}
   107  	if ok, err := ValidQName(name); !ok {
   108  		panic(fmt.Errorf("unique name «%v» is invalid: %w", name, err))
   109  	}
   110  	if uu.UniqueByName(name) != nil {
   111  		panic(ErrAlreadyExists("unique «%v»", name))
   112  	}
   113  
   114  	if uu.app != nil {
   115  		if t := uu.app.TypeByName(name); t != nil {
   116  			panic(ErrAlreadyExists("unique «%v» already used for %v", name, t))
   117  		}
   118  	}
   119  
   120  	if len(fields) == 0 {
   121  		panic(ErrMissed("unique «%v» fields", name))
   122  	}
   123  	if i, j := duplicates(fields); i >= 0 {
   124  		panic(ErrAlreadyExists("fields in unique «%v» has duplicates (fields[%d] == fields[%d] == %q)", name, i, j, fields[i]))
   125  	}
   126  
   127  	if len(fields) > MaxTypeUniqueFieldsCount {
   128  		panic(ErrTooMany("fields in unique «%v», maximum is %d", name, MaxTypeUniqueFieldsCount))
   129  	}
   130  
   131  	for _, un := range uu.uniques {
   132  		ff := make([]FieldName, 0)
   133  		for _, f := range un.Fields() {
   134  			ff = append(ff, f.Name())
   135  		}
   136  		if overlaps(fields, ff) {
   137  			panic(ErrAlreadyExists("type already has %v which fields overlaps new unique fields", un))
   138  		}
   139  	}
   140  
   141  	if len(uu.uniques) >= MaxTypeUniqueCount {
   142  		panic(ErrTooMany("uniques, maximum is %d", MaxTypeUniqueCount))
   143  	}
   144  
   145  	un := newUnique(name, fields, uu.fields)
   146  	un.comment.setComment(comment...)
   147  
   148  	uu.uniques[name] = un
   149  }
   150  
   151  // # Implements:
   152  //   - IUniquesBuilder
   153  type uniquesBuilder struct {
   154  	*uniques
   155  }
   156  
   157  func makeUniquesBuilder(uniques *uniques) uniquesBuilder {
   158  	return uniquesBuilder{
   159  		uniques: uniques,
   160  	}
   161  }
   162  
   163  func (ub *uniquesBuilder) AddUnique(name QName, fields []FieldName, comment ...string) IUniquesBuilder {
   164  	ub.addUnique(name, fields, comment...)
   165  	return ub
   166  }
   167  
   168  func (ub *uniquesBuilder) SetUniqueField(name FieldName) IUniquesBuilder {
   169  	ub.setUniqueField(name)
   170  	return ub
   171  }
   172  
   173  // If the slices have duplicates, then the indices of the first pair are returned, otherwise (-1, -1)
   174  func duplicates[T comparable](s []T) (int, int) {
   175  	for i := range s {
   176  		for j := i + 1; j < len(s); j++ {
   177  			if s[i] == s[j] {
   178  				return i, j
   179  			}
   180  		}
   181  	}
   182  	return -1, -1
   183  }
   184  
   185  // Returns is slice sub is a subset of slice set, i.e. all elements from sub exist in set
   186  func subSet[T comparable](sub, set []T) bool {
   187  	for _, v1 := range sub {
   188  		found := false
   189  		for _, v2 := range set {
   190  			found = v1 == v2
   191  			if found {
   192  				break
   193  			}
   194  		}
   195  		if !found {
   196  			return false
   197  		}
   198  	}
   199  	return true
   200  }
   201  
   202  // Returns is set1 and set2 overlaps, i.e. set1 is subset of set2 or set2 is subset of set1
   203  func overlaps[T comparable](set1, set2 []T) bool {
   204  	return subSet(set1, set2) || subSet(set2, set1)
   205  }