Skip to content

Commit db5064b

Browse files
committed
add docs and a few more functions, upgrade to go 1.25
1 parent e19335c commit db5064b

File tree

4 files changed

+282
-19
lines changed

4 files changed

+282
-19
lines changed

README.md

Lines changed: 165 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,165 @@
1-
# Pantry
1+
# Pantry
2+
3+
[![Go Reference](https://pkg.go.dev/badge/github.com/webermarci/pantry.svg)](https://pkg.go.dev/github.com/webermarci/pantry)
4+
[![Test](https://github.com/webermarci/pantry/actions/workflows/testing.yml/badge.svg)](https://github.com/webermarci/pantry/actions/workflows/testing.yml)
5+
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6+
7+
Pantry is a generic, thread-safe, in-memory key-value store for Go with expiring
8+
items.
9+
10+
## Features
11+
12+
- Thread-safe for concurrent use
13+
- Generic, works with any type
14+
- Items expire automatically
15+
- Graceful shutdown with context cancellation
16+
- Iterators for keys, values, and all items
17+
18+
## Installation
19+
20+
```bash
21+
go get github.com/webermarci/pantry
22+
```
23+
24+
## Usage
25+
26+
```go
27+
package main
28+
29+
import (
30+
"context"
31+
"fmt"
32+
"time"
33+
34+
"github.com/webermarci/pantry"
35+
)
36+
37+
func main() {
38+
// Create a new pantry with a default expiration of 1 hour.
39+
p := pantry.New[string](context.Background(), time.Hour)
40+
41+
// Set a value with the default expiration.
42+
p.Set("hello", "world")
43+
44+
// Get a value.
45+
if value, found := p.Get("hello"); found {
46+
fmt.Println(value)
47+
}
48+
49+
// Check if a key exists.
50+
if p.Contains("hello") {
51+
fmt.Println("hello exists")
52+
}
53+
54+
// Get the number of items.
55+
fmt.Printf("pantry contains %d items\n", p.Count())
56+
57+
// Iterate over keys.
58+
for key := range p.Keys() {
59+
fmt.Println(key)
60+
}
61+
62+
// Iterate over values.
63+
for value := range p.Values() {
64+
fmt.Println(value)
65+
}
66+
67+
// Iterate over all items.
68+
for key, value := range p.All() {
69+
fmt.Println(key, value)
70+
}
71+
72+
// Remove a value.
73+
p.Remove("hello")
74+
75+
// Clear all items.
76+
p.Clear()
77+
78+
// Check if the pantry is empty.
79+
if p.IsEmpty() {
80+
fmt.Println("pantry is empty")
81+
}
82+
}
83+
```
84+
85+
## API
86+
87+
### `New[T any](ctx context.Context, expiration time.Duration) *Pantry[T]`
88+
89+
Creates a new pantry. The `expiration` duration is the time-to-live for items in
90+
the pantry. The context can be used to gracefully shutdown the pantry and free
91+
up resources.
92+
93+
### `(p *Pantry[T]) Get(key string) (T, bool)`
94+
95+
Gets a value from the pantry. If the item has expired, it will be removed from
96+
the pantry and `false` will be returned.
97+
98+
### `(p *Pantry[T]) Set(key string, value T)`
99+
100+
Sets a value in the pantry. The item will expire after the expiration time.
101+
102+
### `(p *Pantry[T]) Remove(key string)`
103+
104+
Removes a value from the pantry.
105+
106+
### `(p *Pantry[T]) IsEmpty() bool`
107+
108+
Returns `true` if the pantry is empty.
109+
110+
### `(p *Pantry[T]) Clear()`
111+
112+
Removes all items from the pantry.
113+
114+
### `(p *Pantry[T]) Contains(key string) bool`
115+
116+
Returns `true` if the key exists in the pantry.
117+
118+
### `(p *Pantry[T]) Count() int`
119+
120+
Returns the number of items in the pantry.
121+
122+
### `(p *Pantry[T]) Keys() iter.Seq[string]`
123+
124+
Returns an iterator over the keys in the pantry.
125+
126+
### `(p *Pantry[T]) Values() iter.Seq[T]`
127+
128+
Returns an iterator over the values in the pantry.
129+
130+
### `(p *Pantry[T]) All() iter.Seq2[string, T]`
131+
132+
Returns an iterator over the keys and values in the pantry.
133+
134+
## Expiration
135+
136+
Items in the pantry will be automatically removed after they expire. The
137+
expiration time is set when the pantry is created.
138+
139+
## Context Cancellation
140+
141+
The pantry can be gracefully shut down by canceling the context that was passed
142+
to the `New` function. This will stop the background cleanup goroutine and
143+
remove all items from the pantry.
144+
145+
## Benchmarks
146+
147+
```
148+
goos: darwin
149+
goarch: arm64
150+
pkg: github.com/webermarci/pantry
151+
cpu: Apple M1
152+
BenchmarkGet/0-8 1000000000 0.0000002 ns/op 0 B/op 0 allocs/op
153+
BenchmarkSet/0-8 1000000000 0.0000010 ns/op 0 B/op 0 allocs/op
154+
BenchmarkRemove/0-8 1000000000 0.0000002 ns/op 0 B/op 0 allocs/op
155+
```
156+
157+
## Contributing
158+
159+
Contributions are welcome! Please feel free to submit a pull request or open an
160+
issue.
161+
162+
## License
163+
164+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
165+
for details.

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module github.com/webermarci/pantry
22

3-
go 1.23
3+
go 1.25

pantry.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Package pantry provides a thread-safe, in-memory key-value store for Go with expiring items.
12
package pantry
23

34
import (
@@ -12,12 +13,14 @@ type item[T any] struct {
1213
expires int64
1314
}
1415

16+
// Pantry is a thread-safe, in-memory key-value store with expiring items
1517
type Pantry[T any] struct {
1618
expiration time.Duration
1719
store map[string]item[T]
1820
mutex sync.RWMutex
1921
}
2022

23+
// Get retrieves a value from the pantry. If the item has expired, it will be removed and `false` will be returned.
2124
func (pantry *Pantry[T]) Get(key string) (T, bool) {
2225
pantry.mutex.RLock()
2326
defer pantry.mutex.RUnlock()
@@ -29,6 +32,7 @@ func (pantry *Pantry[T]) Get(key string) (T, bool) {
2932
return item.value, found
3033
}
3134

35+
// Set adds a value to the pantry. The item will expire after the default expiration time.
3236
func (pantry *Pantry[T]) Set(key string, value T) {
3337
pantry.mutex.Lock()
3438
defer pantry.mutex.Unlock()
@@ -39,20 +43,45 @@ func (pantry *Pantry[T]) Set(key string, value T) {
3943
}
4044
}
4145

46+
// Remove removes a value from the pantry.
4247
func (pantry *Pantry[T]) Remove(key string) {
4348
pantry.mutex.Lock()
4449
defer pantry.mutex.Unlock()
4550

4651
delete(pantry.store, key)
4752
}
4853

54+
// IsEmpty returns `true` if the pantry is empty.
4955
func (pantry *Pantry[T]) IsEmpty() bool {
5056
pantry.mutex.RLock()
5157
defer pantry.mutex.RUnlock()
5258

5359
return len(pantry.store) == 0
5460
}
5561

62+
// Contains returns `true` if the key exists in the pantry.
63+
func (pantry *Pantry[T]) Contains(key string) bool {
64+
_, found := pantry.Get(key)
65+
return found
66+
}
67+
68+
// Count returns the number of items in the pantry.
69+
func (pantry *Pantry[T]) Count() int {
70+
pantry.mutex.RLock()
71+
defer pantry.mutex.RUnlock()
72+
73+
return len(pantry.store)
74+
}
75+
76+
// Clear removes all items from the pantry.
77+
func (pantry *Pantry[T]) Clear() {
78+
pantry.mutex.Lock()
79+
defer pantry.mutex.Unlock()
80+
81+
pantry.store = make(map[string]item[T])
82+
}
83+
84+
// Keys returns an iterator over the keys in the pantry.
5685
func (pantry *Pantry[T]) Keys() iter.Seq[string] {
5786
return func(yield func(string) bool) {
5887
pantry.mutex.RLock()
@@ -70,6 +99,7 @@ func (pantry *Pantry[T]) Keys() iter.Seq[string] {
7099
}
71100
}
72101

102+
// Values returns an iterator over the values in the pantry.
73103
func (pantry *Pantry[T]) Values() iter.Seq[T] {
74104
return func(yield func(T) bool) {
75105
pantry.mutex.RLock()
@@ -87,6 +117,7 @@ func (pantry *Pantry[T]) Values() iter.Seq[T] {
87117
}
88118
}
89119

120+
// All returns an iterator over the keys and values in the pantry.
90121
func (pantry *Pantry[T]) All() iter.Seq2[string, T] {
91122
return func(yield func(string, T) bool) {
92123
pantry.mutex.RLock()
@@ -104,6 +135,8 @@ func (pantry *Pantry[T]) All() iter.Seq2[string, T] {
104135
}
105136
}
106137

138+
// New creates a new pantry. The expiration duration is the time-to-live for items.
139+
// The context can be used to gracefully shutdown the pantry and free up resources.
107140
func New[T any](ctx context.Context, expiration time.Duration) *Pantry[T] {
108141
pantry := &Pantry[T]{
109142
expiration: expiration,

0 commit comments

Comments
 (0)