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  }