github.com/haraldrudell/parl@v0.4.176/sets/set.go (about) 1 /* 2 © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package sets 7 8 import ( 9 "fmt" 10 11 "github.com/haraldrudell/parl/iters" 12 "github.com/haraldrudell/parl/perrors" 13 "github.com/haraldrudell/parl/pfmt" 14 "github.com/haraldrudell/parl/plog" 15 ) 16 17 // Set0 holds a multiple-selection function argument that has printable representation. 18 // PrintableEnum allows finite selection function arguments to have meaningful names and makes 19 // those names printable. 20 type Set0[T comparable, E any] struct { 21 elementMap map[T]*E 22 elements []E 23 } 24 25 // NewSet returns an enumeration of printable semantic elements 26 // - elements implement the sets.Element[E] interface: 27 // - — Value method returns the comparable 28 // - — String method returns string representation 29 // - a common concrete type is [sets.SetElement]: 30 // - — ValueV field is comparable 31 // - — Name field is the string value 32 // Typically single word 33 // - another concerete type is sets.ElementFull[E]: Value, String, Description 34 // - — Description method returns a sentece-length description 35 // 36 // usage: 37 // 38 // sets.NewSet(sets.NewElements[int]( 39 // []sets.SetElement[int]{ 40 // {ValueV: unix.AF_INET, Name: "IPv4"}, 41 // {ValueV: unix.AF_INET6, Name: "IPv6"}, 42 // })) 43 func NewSet[T comparable, E any](elements []E) (set Set[T]) { 44 return NewSetFieldp[T](elements, nil, nil, nil) 45 } 46 47 func NewSetFieldp[T comparable, E any]( 48 elements []E, 49 fieldp *Set0[T, E], 50 fTEp *func(comparable T) (ep *E), 51 epIteratorp *func() (iterator iters.Iterator[*E]), 52 ) (set Set[T]) { 53 54 // E can be concrete type or interface but must have: 55 // - a Value method returning comparable T 56 // - a String method returning string 57 var ep *E 58 if _, ok := any(ep).(Element[T]); !ok { 59 var eType = fmt.Sprintf("%T", ep)[1:] // get the interface type name by removing leading star 60 var i Element[T] 61 var interfaceType = fmt.Sprintf("%T", i) 62 panic(perrors.ErrorfPF("type E %s: &E does not implement interface %s", 63 eType, interfaceType, 64 )) 65 } 66 var m = make(map[T]*E, len(elements)) 67 for i := 0; i < len(elements); i++ { 68 var ep = &elements[i] 69 var element = any(ep).(Element[T]) 70 var valueT = element.Value() 71 if existingElement, ok := m[valueT]; ok { 72 panic(perrors.ErrorfPF( 73 "duplicate set-element:"+ 74 " comparable type T: %T duplicate value: ‘%s’"+ 75 " new duplicate name: %q existing name: %q"+ 76 " number of added values: %d", 77 valueT, pfmt.NoRecurseVPrint(valueT), 78 element, existingElement, 79 len(m), 80 )) 81 } 82 m[valueT] = ep 83 } 84 if fieldp != nil { 85 *fieldp = Set0[T, E]{ 86 elementMap: m, 87 elements: elements, 88 } 89 } else { 90 fieldp = &Set0[T, E]{ 91 elementMap: m, 92 elements: elements, 93 } 94 } 95 if fTEp != nil { 96 *fTEp = fieldp.fTE 97 } 98 if epIteratorp != nil { 99 *epIteratorp = fieldp.epIterator 100 } 101 return fieldp 102 } 103 104 // IsValid returns whether value is part of the set 105 func (s *Set0[T, E]) IsValid(value T) (isValid bool) { 106 _, isValid = s.elementMap[value] 107 return 108 } 109 110 // Iterator allows iteration over all set elements 111 func (s *Set0[T, E]) Iterator() (iterator iters.Iterator[T]) { 112 return iters.NewSimpleConverterIterator( 113 iters.NewSlicePointerIterator(s.elements), 114 s.simpleConverter, 115 ) 116 } 117 118 func (s *Set0[T, E]) simpleConverter(element *E) (valueT T) { return any(element).(Element[T]).Value() } 119 120 // StringT returns a string representation for an element of this set. 121 // If value is not a valid element, a fmt.Printf value is output like ?'%v' 122 func (s *Set0[T, E]) StringT(value T) (s2 string) { 123 // StringT is intended to be the String method of a named type implementing set. 124 // if StringT method code would somehow invoke the T.String method again, 125 // this will cause infinite recursion and stack overflow panic. 126 var ep *E 127 var ok bool 128 if ep, ok = s.elementMap[value]; ok { 129 var element = any(ep).(Element[T]) 130 // e is Element interface, likely set.Element runtime type. 131 // set.Element.String() returns a value from a type string, ie. type T 132 // is not involved. 133 // This ensures no T-type String-function recursion. 134 s2 = element.String() 135 } else { 136 // converting T to string may recurse if printf %v or %s verbs are used. 137 // ie. printf will re-enter this method and exhaust the stack. 138 // all that is known about T is that it is a comparable type 139 //p = string(value) 140 141 s2 = "?\x27" + pfmt.NoRecurseVPrint(value) + "\x27" 142 } 143 return 144 } 145 146 // Description returns a more elaborate string representation 147 // for an element. Description and StringT may return the same value 148 func (s *Set0[T, E]) Description(value T) (full string) { 149 150 // get a pointer to the Element type 151 var ep *E 152 var ok bool 153 if ep, ok = s.elementMap[value]; !ok { 154 return // invalid T return 155 } 156 157 // type assert to Element with Description method 158 var elementDescription ElementDescription 159 if elementDescription, ok = any(ep).(ElementDescription); !ok { 160 return // not a full element return 161 } 162 163 full = elementDescription.Description() 164 165 return 166 } 167 168 func (s *Set0[T, E]) fTE(valueT T) (ep *E) { return s.elementMap[valueT] } 169 170 func (s *Set0[T, E]) epIterator() (iterator iters.Iterator[*E]) { 171 return iters.NewSlicePointerIterator(s.elements) 172 } 173 174 func (s *Set0[T, E]) String() (s2 string) { 175 var t T 176 s2 = plog.Sprintf("set_%T:%d", t, len(s.elementMap)) 177 return 178 }