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 }