github.com/facebookincubator/go-belt@v0.0.0-20230703220935-39cd348f1a38/pkg/field/abstract_fields.go (about)

     1  // Copyright 2022 Meta Platforms, Inc. and affiliates.
     2  //
     3  // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
     4  //
     5  // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
     6  //
     7  // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
     8  //
     9  // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
    10  //
    11  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    12  
    13  package field
    14  
    15  // ForEachFieldser is an iterator through a collection of fields.
    16  //
    17  // Any object which implements this interface may be used as a source
    18  // of fields. For example a generic parser of field values in a Logger
    19  // checks if a value implements this interface, and if does, then
    20  // it is used to provide the fields instead of using a generic method.
    21  type ForEachFieldser interface {
    22  	// ForEachField iterates through all the fields, until first false is returned.
    23  	//
    24  	// WARNING! This callback is supposed to be used only to read
    25  	// the field content, but not to hold the pointer to the field itself.
    26  	// For the purposes of performance optimizations it is assumed
    27  	// that the pointer is not escapable. If it is required to store
    28  	// the pointer to the field after `callback` returned then copy the field.
    29  	// Otherwise Go's memory guarantees are broken.
    30  	ForEachField(callback func(f *Field) bool) bool
    31  }
    32  
    33  // AbstractFields is an abstraction of any types of collections of fields.
    34  type AbstractFields interface {
    35  	ForEachFieldser
    36  	Len() int
    37  }
    38  
    39  // Slice is a collection of collections of fields.
    40  type Slice[T AbstractFields] []T
    41  
    42  // Len implements AbstractFields.
    43  func (s Slice[T]) Len() int {
    44  	count := 0
    45  	for _, items := range s {
    46  		count += items.Len()
    47  	}
    48  	return count
    49  }
    50  
    51  // ForEachField implements AbstractFields.
    52  func (s Slice[T]) ForEachField(callback func(f *Field) bool) bool {
    53  	for _, items := range s {
    54  		if !items.ForEachField(callback) {
    55  			return false
    56  		}
    57  	}
    58  	return true
    59  }
    60  
    61  // Slicer implements AbstractFields providing a subset of fields (reported through
    62  // method ForEachField of the initial collection).
    63  type Slicer[T AbstractFields] struct {
    64  	All      T
    65  	StartIdx uint
    66  	EndIdx   uint
    67  }
    68  
    69  var _ AbstractFields = (*Slicer[AbstractFields])(nil)
    70  
    71  // Len implements AbstractFields.
    72  func (s *Slicer[T]) Len() int {
    73  	count := 0
    74  	s.ForEachField(func(f *Field) bool {
    75  		count++
    76  		return true
    77  	})
    78  	return count
    79  }
    80  
    81  // ForEachField implements AbstractFields.
    82  func (s *Slicer[T]) ForEachField(callback func(f *Field) bool) bool {
    83  	idx := uint(0)
    84  	return s.All.ForEachField(func(f *Field) bool {
    85  		if idx >= s.EndIdx {
    86  			return false
    87  		}
    88  		if idx >= s.StartIdx {
    89  			if !callback(f) {
    90  				return false
    91  			}
    92  		}
    93  		idx++
    94  		return true
    95  	})
    96  }
    97  
    98  // NewSlicer provides a subset of fields (reported through method ForEachField of the initial collection).
    99  func NewSlicer[T AbstractFields](all T, startIdx, endIdx uint) *Slicer[T] {
   100  	return &Slicer[T]{
   101  		All:      all,
   102  		StartIdx: startIdx,
   103  		EndIdx:   endIdx,
   104  	}
   105  }
   106  
   107  // Gather copies all the fields into a slice and returns it.
   108  func Gather[T AbstractFields](fields T) Fields {
   109  	result := make(Fields, 0, fields.Len())
   110  	fields.ForEachField(func(field *Field) bool {
   111  		result = append(result, *field)
   112  		return true
   113  	})
   114  	return result
   115  }