github.com/maypok86/otter@v1.2.1/builder.go (about)

     1  // Copyright (c) 2023 Alexey Mayshev. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package otter
    16  
    17  import (
    18  	"errors"
    19  	"time"
    20  
    21  	"github.com/maypok86/otter/internal/core"
    22  )
    23  
    24  const (
    25  	unsetCapacity = -1
    26  )
    27  
    28  var (
    29  	// ErrIllegalCapacity means that a non-positive capacity has been passed to the NewBuilder.
    30  	ErrIllegalCapacity = errors.New("capacity should be positive")
    31  	// ErrIllegalInitialCapacity means that a non-positive capacity has been passed to the Builder.InitialCapacity.
    32  	ErrIllegalInitialCapacity = errors.New("initial capacity should be positive")
    33  	// ErrNilCostFunc means that a nil cost func has been passed to the Builder.Cost.
    34  	ErrNilCostFunc = errors.New("setCostFunc func should not be nil")
    35  	// ErrIllegalTTL means that a non-positive ttl has been passed to the Builder.WithTTL.
    36  	ErrIllegalTTL = errors.New("ttl should be positive")
    37  )
    38  
    39  type baseOptions[K comparable, V any] struct {
    40  	capacity         int
    41  	initialCapacity  int
    42  	statsEnabled     bool
    43  	withCost         bool
    44  	costFunc         func(key K, value V) uint32
    45  	deletionListener func(key K, value V, cause DeletionCause)
    46  }
    47  
    48  func (o *baseOptions[K, V]) collectStats() {
    49  	o.statsEnabled = true
    50  }
    51  
    52  func (o *baseOptions[K, V]) setCostFunc(costFunc func(key K, value V) uint32) {
    53  	o.costFunc = costFunc
    54  	o.withCost = true
    55  }
    56  
    57  func (o *baseOptions[K, V]) setInitialCapacity(initialCapacity int) {
    58  	o.initialCapacity = initialCapacity
    59  }
    60  
    61  func (o *baseOptions[K, V]) setDeletionListener(deletionListener func(key K, value V, cause DeletionCause)) {
    62  	o.deletionListener = deletionListener
    63  }
    64  
    65  func (o *baseOptions[K, V]) validate() error {
    66  	if o.initialCapacity <= 0 && o.initialCapacity != unsetCapacity {
    67  		return ErrIllegalInitialCapacity
    68  	}
    69  	if o.costFunc == nil {
    70  		return ErrNilCostFunc
    71  	}
    72  	return nil
    73  }
    74  
    75  func (o *baseOptions[K, V]) toConfig() core.Config[K, V] {
    76  	var initialCapacity *int
    77  	if o.initialCapacity != unsetCapacity {
    78  		initialCapacity = &o.initialCapacity
    79  	}
    80  	return core.Config[K, V]{
    81  		Capacity:         o.capacity,
    82  		InitialCapacity:  initialCapacity,
    83  		StatsEnabled:     o.statsEnabled,
    84  		CostFunc:         o.costFunc,
    85  		WithCost:         o.withCost,
    86  		DeletionListener: o.deletionListener,
    87  	}
    88  }
    89  
    90  type constTTLOptions[K comparable, V any] struct {
    91  	baseOptions[K, V]
    92  	ttl time.Duration
    93  }
    94  
    95  func (o *constTTLOptions[K, V]) validate() error {
    96  	if o.ttl <= 0 {
    97  		return ErrIllegalTTL
    98  	}
    99  	return o.baseOptions.validate()
   100  }
   101  
   102  func (o *constTTLOptions[K, V]) toConfig() core.Config[K, V] {
   103  	c := o.baseOptions.toConfig()
   104  	c.TTL = &o.ttl
   105  	return c
   106  }
   107  
   108  type variableTTLOptions[K comparable, V any] struct {
   109  	baseOptions[K, V]
   110  }
   111  
   112  func (o *variableTTLOptions[K, V]) toConfig() core.Config[K, V] {
   113  	c := o.baseOptions.toConfig()
   114  	c.WithVariableTTL = true
   115  	return c
   116  }
   117  
   118  // Builder is a one-shot builder for creating a cache instance.
   119  type Builder[K comparable, V any] struct {
   120  	baseOptions[K, V]
   121  }
   122  
   123  // MustBuilder creates a builder and sets the future cache capacity.
   124  //
   125  // Panics if capacity <= 0.
   126  func MustBuilder[K comparable, V any](capacity int) *Builder[K, V] {
   127  	b, err := NewBuilder[K, V](capacity)
   128  	if err != nil {
   129  		panic(err)
   130  	}
   131  	return b
   132  }
   133  
   134  // NewBuilder creates a builder and sets the future cache capacity.
   135  //
   136  // Returns an error if capacity <= 0.
   137  func NewBuilder[K comparable, V any](capacity int) (*Builder[K, V], error) {
   138  	if capacity <= 0 {
   139  		return nil, ErrIllegalCapacity
   140  	}
   141  
   142  	return &Builder[K, V]{
   143  		baseOptions: baseOptions[K, V]{
   144  			capacity:        capacity,
   145  			initialCapacity: unsetCapacity,
   146  			statsEnabled:    false,
   147  			costFunc: func(key K, value V) uint32 {
   148  				return 1
   149  			},
   150  		},
   151  	}, nil
   152  }
   153  
   154  // CollectStats determines whether statistics should be calculated when the cache is running.
   155  //
   156  // By default, statistics calculating is disabled.
   157  func (b *Builder[K, V]) CollectStats() *Builder[K, V] {
   158  	b.collectStats()
   159  	return b
   160  }
   161  
   162  // InitialCapacity sets the minimum total size for the internal data structures. Providing a large enough estimate
   163  // at construction time avoids the need for expensive resizing operations later, but setting this
   164  // value unnecessarily high wastes memory.
   165  func (b *Builder[K, V]) InitialCapacity(initialCapacity int) *Builder[K, V] {
   166  	b.setInitialCapacity(initialCapacity)
   167  	return b
   168  }
   169  
   170  // Cost sets a function to dynamically calculate the cost of an item.
   171  //
   172  // By default, this function always returns 1.
   173  func (b *Builder[K, V]) Cost(costFunc func(key K, value V) uint32) *Builder[K, V] {
   174  	b.setCostFunc(costFunc)
   175  	return b
   176  }
   177  
   178  // DeletionListener specifies a listener instance that caches should notify each time an entry is deleted for any
   179  // DeletionCause cause. The cache will invoke this listener in the background goroutine
   180  // after the entry's deletion operation has completed.
   181  func (b *Builder[K, V]) DeletionListener(deletionListener func(key K, value V, cause DeletionCause)) *Builder[K, V] {
   182  	b.setDeletionListener(deletionListener)
   183  	return b
   184  }
   185  
   186  // WithTTL specifies that each item should be automatically removed from the cache once a fixed duration
   187  // has elapsed after the item's creation.
   188  func (b *Builder[K, V]) WithTTL(ttl time.Duration) *ConstTTLBuilder[K, V] {
   189  	return &ConstTTLBuilder[K, V]{
   190  		constTTLOptions[K, V]{
   191  			baseOptions: b.baseOptions,
   192  			ttl:         ttl,
   193  		},
   194  	}
   195  }
   196  
   197  // WithVariableTTL specifies that each item should be automatically removed from the cache once a duration has
   198  // elapsed after the item's creation. Items are expired based on the custom ttl specified for each item separately.
   199  //
   200  // You should prefer WithTTL to this option whenever possible.
   201  func (b *Builder[K, V]) WithVariableTTL() *VariableTTLBuilder[K, V] {
   202  	return &VariableTTLBuilder[K, V]{
   203  		variableTTLOptions[K, V]{
   204  			baseOptions: b.baseOptions,
   205  		},
   206  	}
   207  }
   208  
   209  // Build creates a configured cache or
   210  // returns an error if invalid parameters were passed to the builder.
   211  func (b *Builder[K, V]) Build() (Cache[K, V], error) {
   212  	if err := b.validate(); err != nil {
   213  		return Cache[K, V]{}, err
   214  	}
   215  
   216  	return newCache(b.toConfig()), nil
   217  }
   218  
   219  // ConstTTLBuilder is a one-shot builder for creating a cache instance.
   220  type ConstTTLBuilder[K comparable, V any] struct {
   221  	constTTLOptions[K, V]
   222  }
   223  
   224  // CollectStats determines whether statistics should be calculated when the cache is running.
   225  //
   226  // By default, statistics calculating is disabled.
   227  func (b *ConstTTLBuilder[K, V]) CollectStats() *ConstTTLBuilder[K, V] {
   228  	b.collectStats()
   229  	return b
   230  }
   231  
   232  // InitialCapacity sets the minimum total size for the internal data structures. Providing a large enough estimate
   233  // at construction time avoids the need for expensive resizing operations later, but setting this
   234  // value unnecessarily high wastes memory.
   235  func (b *ConstTTLBuilder[K, V]) InitialCapacity(initialCapacity int) *ConstTTLBuilder[K, V] {
   236  	b.setInitialCapacity(initialCapacity)
   237  	return b
   238  }
   239  
   240  // Cost sets a function to dynamically calculate the cost of an item.
   241  //
   242  // By default, this function always returns 1.
   243  func (b *ConstTTLBuilder[K, V]) Cost(costFunc func(key K, value V) uint32) *ConstTTLBuilder[K, V] {
   244  	b.setCostFunc(costFunc)
   245  	return b
   246  }
   247  
   248  // DeletionListener specifies a listener instance that caches should notify each time an entry is deleted for any
   249  // DeletionCause cause. The cache will invoke this listener in the background goroutine
   250  // after the entry's deletion operation has completed.
   251  func (b *ConstTTLBuilder[K, V]) DeletionListener(deletionListener func(key K, value V, cause DeletionCause)) *ConstTTLBuilder[K, V] {
   252  	b.setDeletionListener(deletionListener)
   253  	return b
   254  }
   255  
   256  // Build creates a configured cache or
   257  // returns an error if invalid parameters were passed to the builder.
   258  func (b *ConstTTLBuilder[K, V]) Build() (Cache[K, V], error) {
   259  	if err := b.validate(); err != nil {
   260  		return Cache[K, V]{}, err
   261  	}
   262  
   263  	return newCache(b.toConfig()), nil
   264  }
   265  
   266  // VariableTTLBuilder is a one-shot builder for creating a cache instance.
   267  type VariableTTLBuilder[K comparable, V any] struct {
   268  	variableTTLOptions[K, V]
   269  }
   270  
   271  // CollectStats determines whether statistics should be calculated when the cache is running.
   272  //
   273  // By default, statistics calculating is disabled.
   274  func (b *VariableTTLBuilder[K, V]) CollectStats() *VariableTTLBuilder[K, V] {
   275  	b.collectStats()
   276  	return b
   277  }
   278  
   279  // InitialCapacity sets the minimum total size for the internal data structures. Providing a large enough estimate
   280  // at construction time avoids the need for expensive resizing operations later, but setting this
   281  // value unnecessarily high wastes memory.
   282  func (b *VariableTTLBuilder[K, V]) InitialCapacity(initialCapacity int) *VariableTTLBuilder[K, V] {
   283  	b.setInitialCapacity(initialCapacity)
   284  	return b
   285  }
   286  
   287  // Cost sets a function to dynamically calculate the cost of an item.
   288  //
   289  // By default, this function always returns 1.
   290  func (b *VariableTTLBuilder[K, V]) Cost(costFunc func(key K, value V) uint32) *VariableTTLBuilder[K, V] {
   291  	b.setCostFunc(costFunc)
   292  	return b
   293  }
   294  
   295  // DeletionListener specifies a listener instance that caches should notify each time an entry is deleted for any
   296  // DeletionCause cause. The cache will invoke this listener in the background goroutine
   297  // after the entry's deletion operation has completed.
   298  func (b *VariableTTLBuilder[K, V]) DeletionListener(deletionListener func(key K, value V, cause DeletionCause)) *VariableTTLBuilder[K, V] {
   299  	b.setDeletionListener(deletionListener)
   300  	return b
   301  }
   302  
   303  // Build creates a configured cache or
   304  // returns an error if invalid parameters were passed to the builder.
   305  func (b *VariableTTLBuilder[K, V]) Build() (CacheWithVariableTTL[K, V], error) {
   306  	if err := b.validate(); err != nil {
   307  		return CacheWithVariableTTL[K, V]{}, err
   308  	}
   309  
   310  	return newCacheWithVariableTTL(b.toConfig()), nil
   311  }