Full Code of faabiosr/cachego for AI

main 859aca6d0873 cached
38 files
51.9 KB
17.8k tokens
112 symbols
1 requests
Download .txt
Repository: faabiosr/cachego
Branch: main
Commit: 859aca6d0873
Files: 38
Total size: 51.9 KB

Directory structure:
gitextract_vdd2xu1x/

├── .github/
│   └── workflows/
│       └── test.yml
├── .gitignore
├── .golangci.yaml
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── bolt/
│   ├── README.md
│   ├── bolt.go
│   └── bolt_test.go
├── cache.go
├── chain/
│   ├── README.md
│   ├── chain.go
│   └── chain_test.go
├── doc.go
├── docker-compose.yml
├── errors.go
├── errors_test.go
├── file/
│   ├── README.md
│   ├── file.go
│   └── file_test.go
├── go.mod
├── go.sum
├── memcached/
│   ├── README.md
│   ├── memcached.go
│   └── memcached_test.go
├── mongo/
│   ├── README.md
│   ├── mongo.go
│   └── mongo_test.go
├── redis/
│   ├── README.md
│   ├── redis.go
│   └── redis_test.go
├── sqlite3/
│   ├── README.md
│   ├── sqlite3.go
│   └── sqlite3_test.go
└── sync/
    ├── README.md
    ├── map.go
    └── map_test.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/test.yml
================================================
name: test

on:
  push:
    branches:
      - main
      - develop
  pull_request:
    branches:
      - main

jobs:
  test:
    strategy:
      matrix:
        go-version:
          - '1.21.x'
          - '1.22.x'
          - '1.23.x'
        platform: [ubuntu-latest]

    name: test
    runs-on: ${{ matrix.platform }}

    services:
      memcached:
        image: memcached:alpine
        ports:
          - 11211:11211
      redis:
        image: redis:alpine
        ports:
          - 6379:6379
      mongodb:
        image: mongo:3.6
        ports:
          - 27017:27017

    steps:
      - name: checkout the code
        uses: actions/checkout@v4

      - name: setup go
        uses: actions/setup-go@v5
        with:
          go-version: ${{ matrix.go-version }}

      - name: unshallow
        run: git fetch --prune --unshallow

      - name: golanci-linter
        uses: golangci/golangci-lint-action@v6
        with:
          version: v1.64.7 

      - name: run unit tests
        run: make test

      - name: upload code coverage
        uses: codecov/codecov-action@v5
        if: contains(github.ref, 'main')
        with:
          file: ./cover.out


================================================
FILE: .gitignore
================================================
*.swp
*.swo
cover*
cache-dir


================================================
FILE: .golangci.yaml
================================================
---
run:
  timeout: "240s"

output:
  formats:
    - format: "colored-line-number"

linters:
  enable:
    - gocyclo
    - unconvert
    - goimports
    - unused
    - misspell
    - nakedret
    - errcheck
    - revive
    - ineffassign
    - goconst
    - govet
    - unparam
    - gofumpt
    - prealloc
    - mnd
    - gocritic


linters-settings:
  revive:
    rules:
      - name: package-comments
        disabled: true

issues:
  exclude-use-default: false


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing

By participating to this project, you agree to abide our [code of conduct](/CODE_OF_CONDUCT.md).

## Setup your machine

`cachego` is written in [Go](https://golang.org/).

Prerequisites:

* `make`
* [Go 1.21+](https://golang.org/doc/install)

Clone `cachego` from source into `$GOPATH`:

```sh
$ mkdir -p $GOPATH/src/github.com/faabiosr
$ cd $_
$ git clone git@github.com:faabiosr/cachego.git
$ cd cachego
```

Install the build and lint dependencies:
```console
$ make depend
```

A good way of making sure everything is all right is running the test suite:
```console
$ make test
```

## Formatting the code
Format the code running:
```console
$ make fmt
```

## Create a commit

Commit messages should be well formatted.
Start your commit message with the type. Choose one of the following:
`feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`, `revert`, `add`, `remove`, `move`, `bump`, `update`, `release`

After a colon, you should give the message a title, starting with uppercase and ending without a dot.
Keep the width of the text at 72 chars.
The title must be followed with a newline, then a more detailed description.

Please reference any GitHub issues on the last line of the commit message (e.g. `See #123`, `Closes #123`, `Fixes #123`).

An example:

```
docs: Add example for --release-notes flag

I added an example to the docs of the `--release-notes` flag to make
the usage more clear.  The example is an realistic use case and might
help others to generate their own changelog.

See #284
```

## Submit a pull request

Push your branch to your `cachego` fork and open a pull request against the
main branch.



================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2016 Fábio da Silva Ribeiro

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: Makefile
================================================
.DEFAULT_GOAL := test

# Clean up
clean:
	@rm -fR ./vendor/ ./cover.*
.PHONY: clean

# Run tests and generates html coverage file
cover: test
	@go tool cover -html=./cover.text -o ./cover.html
	@test -f ./cover.out && rm ./cover.out;
.PHONY: cover

# Up the docker container for testing
docker:
	@docker-compose up -d
.PHONY: docker

# Format all go files
fmt:
	@gofmt -s -w -l $(shell go list -f {{.Dir}} ./...)
.PHONY: fmt

# Run linters
lint:
	@golangci-lint run ./...
.PHONY: lint

# Run tests
test:
	@go test -v -race -coverprofile=./cover.text -covermode=atomic $(shell go list ./...)
.PHONY: test


================================================
FILE: README.md
================================================
# Cachego

[![Codecov branch](https://img.shields.io/codecov/c/github/faabiosr/cachego/main.svg?style=flat-square)](https://codecov.io/gh/faabiosr/cachego)
[![GoDoc](https://img.shields.io/badge/godoc-reference-5272B4.svg?style=flat-square)](https://pkg.go.dev/github.com/faabiosr/cachego)
[![Go Report Card](https://goreportcard.com/badge/github.com/faabiosr/cachego?style=flat-square)](https://goreportcard.com/report/github.com/faabiosr/cachego)
[![License](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](https://github.com/faabiosr/cachego/blob/main/LICENSE)

Simple interface for caching

## Installation

Cachego requires Go 1.21 or later.

```
go get github.com/faabiosr/cachego
```

## Usage

```go
package main

import (
	"log"
	"time"

	"github.com/faabiosr/cachego/sync"
)

func main() {
	cache := sync.New()

	if err := cache.Save("user_id", "1", 10*time.Second); err != nil {
		log.Fatal(err)
	}

	id, err := cache.Fetch("user_id")
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("user id: %s \n", id)

	keys := cache.FetchMulti([]string{"user_id", "user_name"})

	for k, v := range keys {
		log.Printf("%s: %s\n", k, v)
	}

	if cache.Contains("user_name") {
		cache.Delete("user_name")
	}

	if _, err := cache.Fetch("user_name"); err != nil {
		log.Printf("%v\n", err)
	}

	if err := cache.Flush(); err != nil {
		log.Fatal(err)
	}
}

```

## Supported drivers

- [Bolt](/bolt)
- [Chain](/chain)
- [File](/file)
- [Memcached](/memcached)
- [Mongo](/mongo)
- [Redis](/redis)
- [Sqlite3](/sqlite3)
- [Sync](/sync)


## Documentation

Read the full documentation at [https://pkg.go.dev/github.com/faabiosr/cachego](https://pkg.go.dev/github.com/faabiosr/cachego).

## Development

### Requirements

- Install [docker](https://docs.docker.com/install/)
- Install [docker-compose](https://docs.docker.com/compose/install/)

### Makefile
```sh
// Clean up
$ make clean

//Run tests and generates html coverage file
$ make cover

// Up the docker containers for testing
$ make docker

// Format all go files
$ make fmt

//Run linters
$ make lint

// Run tests
$ make test
```

## License

This project is released under the MIT licence. See [LICENSE](https://github.com/faabiosr/cachego/blob/main/LICENSE) for more details.


================================================
FILE: bolt/README.md
================================================
# Cachego - BoltDB driver
The drivers uses [etcd-io/bbolt](https://github.com/etcd-io/bbolt) to store the cache data.

## Usage

```go
package main

import (
	"log"
	"time"

	bt "go.etcd.io/bbolt"

	"github.com/faabiosr/cachego/bolt"
)

func main() {
	db, err := bt.Open("cache.db", 0600, nil)
	if err != nil {
		log.Fatal(err)
	}

	cache := bolt.New(db)

	if err := cache.Save("user_id", "1", 10*time.Second); err != nil {
		log.Fatal(err)
	}

	id, err := cache.Fetch("user_id")
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("user id: %s \n", id)
}
```


================================================
FILE: bolt/bolt.go
================================================
// Package bolt providers a cache driver that stores the cache using BoltDB.
package bolt

import (
	"encoding/json"
	"errors"
	"time"

	bt "go.etcd.io/bbolt"

	"github.com/faabiosr/cachego"
)

var boltBucket = []byte("cachego")

type (
	bolt struct {
		db *bt.DB
	}

	boltContent struct {
		Duration int64  `json:"duration"`
		Data     string `json:"data,omitempty"`
	}
)

// New creates an instance of BoltDB cache
func New(db *bt.DB) cachego.Cache {
	return &bolt{db}
}

func (b *bolt) read(key string) (*boltContent, error) {
	var value []byte

	err := b.db.View(func(tx *bt.Tx) error {
		if bucket := tx.Bucket(boltBucket); bucket != nil {
			value = bucket.Get([]byte(key))
			return nil
		}

		return errors.New("bucket not found")
	})
	if err != nil {
		return nil, err
	}

	content := &boltContent{}
	if err := json.Unmarshal(value, content); err != nil {
		return nil, err
	}

	if content.Duration == 0 {
		return content, nil
	}

	if content.Duration <= time.Now().Unix() {
		_ = b.Delete(key)
		return nil, cachego.ErrCacheExpired
	}

	return content, nil
}

// Contains checks if the cached key exists into the BoltDB storage
func (b *bolt) Contains(key string) bool {
	_, err := b.read(key)
	return err == nil
}

// Delete the cached key from BoltDB storage
func (b *bolt) Delete(key string) error {
	return b.db.Update(func(tx *bt.Tx) error {
		if bucket := tx.Bucket(boltBucket); bucket != nil {
			return bucket.Delete([]byte(key))
		}

		return errors.New("bucket not found")
	})
}

// Fetch retrieves the cached value from key of the BoltDB storage
func (b *bolt) Fetch(key string) (string, error) {
	content, err := b.read(key)
	if err != nil {
		return "", err
	}

	return content.Data, nil
}

// FetchMulti retrieve multiple cached values from keys of the BoltDB storage
func (b *bolt) FetchMulti(keys []string) map[string]string {
	result := make(map[string]string)

	for _, key := range keys {
		if value, err := b.Fetch(key); err == nil {
			result[key] = value
		}
	}

	return result
}

// Flush removes all cached keys of the BoltDB storage
func (b *bolt) Flush() error {
	return b.db.Update(func(tx *bt.Tx) error {
		return tx.DeleteBucket(boltBucket)
	})
}

// Save a value in BoltDB storage by key
func (b *bolt) Save(key string, value string, lifeTime time.Duration) error {
	duration := int64(0)

	if lifeTime > 0 {
		duration = time.Now().Unix() + int64(lifeTime.Seconds())
	}

	content := &boltContent{duration, value}

	data, err := json.Marshal(content)
	if err != nil {
		return err
	}

	return b.db.Update(func(tx *bt.Tx) error {
		bucket, err := tx.CreateBucketIfNotExists(boltBucket)
		if err != nil {
			return err
		}

		return bucket.Put([]byte(key), data)
	})
}


================================================
FILE: bolt/bolt_test.go
================================================
package bolt

import (
	"fmt"
	"os"
	"testing"
	"time"

	bt "go.etcd.io/bbolt"
)

const (
	testKey   = "foo"
	testValue = "bar"
)

func TestBolt(t *testing.T) {
	dir, err := os.MkdirTemp("", t.Name())
	if err != nil {
		t.Fatal(err)
	}

	db, err := bt.Open(fmt.Sprintf("%s/cachego.db", dir), 0o600, nil)
	if err != nil {
		t.Fatal(err)
	}

	t.Cleanup(func() {
		_ = db.Close()
		_ = os.RemoveAll(dir)
	})

	c := New(db)

	if err := c.Save(testKey, testValue, 1*time.Nanosecond); err != nil {
		t.Errorf("save fail: expected nil, got %v", err)
	}

	if _, err := c.Fetch(testKey); err == nil {
		t.Errorf("fetch fail: expected an error, got %v", err)
	}

	_ = c.Save(testKey, testValue, 10*time.Second)

	if res, _ := c.Fetch(testKey); res != testValue {
		t.Errorf("fetch fail, wrong value: expected %s, got %s", testValue, res)
	}

	_ = c.Save(testKey, testValue, 0)

	if !c.Contains(testKey) {
		t.Errorf("contains failed: the key %s should be exist", testKey)
	}

	_ = c.Save("bar", testValue, 0)

	if values := c.FetchMulti([]string{testKey, "bar"}); len(values) == 0 {
		t.Errorf("fetch multi failed: expected %d, got %d", 2, len(values))
	}

	if err := c.Flush(); err != nil {
		t.Errorf("flush failed: expected nil, got %v", err)
	}

	if err := c.Flush(); err == nil {
		t.Errorf("flush failed: expected error, got %v", err)
	}

	if err := c.Delete(testKey); err == nil {
		t.Errorf("delete failed: expected error, got %v", err)
	}

	if c.Contains(testKey) {
		t.Errorf("contains failed: the key %s should not be exist", testKey)
	}
}


================================================
FILE: cache.go
================================================
package cachego

import (
	"time"
)

type (
	// Cache is the top-level cache interface
	Cache interface {
		// Contains check if a cached key exists
		Contains(key string) bool

		// Delete remove the cached key
		Delete(key string) error

		// Fetch retrieve the cached key value
		Fetch(key string) (string, error)

		// FetchMulti retrieve multiple cached keys value
		FetchMulti(keys []string) map[string]string

		// Flush remove all cached keys
		Flush() error

		// Save cache a value by key
		Save(key string, value string, lifeTime time.Duration) error
	}
)


================================================
FILE: chain/README.md
================================================
# Cachego - Chain driver
The chain driver deals with multiple driver at same time, it could save the key in multiple drivers
and for fetching the driver will call the first one, if fails it will try the next until fail.

## Usage

```go
package main

import (
	"log"
	"time"

	bt "go.etcd.io/bbolt"

	"github.com/faabiosr/cachego/bolt"
	"github.com/faabiosr/cachego/chain"
	"github.com/faabiosr/cachego/sync"
)

func main() {
	db, err := bt.Open("cache.db", 0600, nil)
	if err != nil {
		log.Fatal(err)
	}

	cache := chain.New(
		bolt.New(db),
		sync.New(),
	)

	if err := cache.Save("user_id", "1", 10*time.Second); err != nil {
		log.Fatal(err)
	}

	id, err := cache.Fetch("user_id")
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("user id: %s \n", id)
}
```


================================================
FILE: chain/chain.go
================================================
// Package chain provides chaining cache drivers operations, in case of failure
// the driver try to apply using the next driver informed, until fail.
package chain

import (
	"errors"
	"time"

	"github.com/faabiosr/cachego"
)

type (
	chain struct {
		drivers []cachego.Cache
	}
)

// New creates an instance of Chain cache driver
func New(drivers ...cachego.Cache) cachego.Cache {
	return &chain{drivers}
}

// Contains checks if the cached key exists in one of the cache storages
func (c *chain) Contains(key string) bool {
	for _, driver := range c.drivers {
		if driver.Contains(key) {
			return true
		}
	}

	return false
}

// Delete the cached key in all cache storages
func (c *chain) Delete(key string) error {
	for _, driver := range c.drivers {
		if err := driver.Delete(key); err != nil {
			return err
		}
	}

	return nil
}

// Fetch retrieves the value of one of the registred cache storages
func (c *chain) Fetch(key string) (string, error) {
	for _, driver := range c.drivers {
		value, err := driver.Fetch(key)

		if err == nil {
			return value, nil
		}
	}

	return "", errors.New("key not found in cache chain")
}

// FetchMulti retrieves multiple cached values from one of the registred cache storages
func (c *chain) FetchMulti(keys []string) map[string]string {
	result := make(map[string]string)

	for _, key := range keys {
		if value, err := c.Fetch(key); err == nil {
			result[key] = value
		}
	}

	return result
}

// Flush removes all cached keys of the registered cache storages
func (c *chain) Flush() error {
	for _, driver := range c.drivers {
		if err := driver.Flush(); err != nil {
			return err
		}
	}

	return nil
}

// Save a value in all cache storages by key
func (c *chain) Save(key string, value string, lifeTime time.Duration) error {
	for _, driver := range c.drivers {
		if err := driver.Save(key, value, lifeTime); err != nil {
			return err
		}
	}

	return nil
}


================================================
FILE: chain/chain_test.go
================================================
package chain

import (
	"testing"
	"time"

	"github.com/bradfitz/gomemcache/memcache"
	"github.com/faabiosr/cachego/memcached"
	"github.com/faabiosr/cachego/sync"
)

const (
	testKey   = "foo"
	testValue = "bar"
)

func TestChain(t *testing.T) {
	c := New(sync.New())

	if err := c.Save(testKey, testValue, 1*time.Nanosecond); err != nil {
		t.Errorf("save fail: expected nil, got %v", err)
	}

	if _, err := c.Fetch(testKey); err == nil {
		t.Errorf("fetch fail: expected an error, got %v", err)
	}

	_ = c.Save(testKey, testValue, 10*time.Second)

	if res, _ := c.Fetch(testKey); res != testValue {
		t.Errorf("fetch fail, wrong value: expected %s, got %s", testValue, res)
	}

	_ = c.Save(testKey, testValue, 0)

	if !c.Contains(testKey) {
		t.Errorf("contains failed: the key %s should be exist", testKey)
	}

	_ = c.Save("bar", testValue, 0)

	if values := c.FetchMulti([]string{testKey, "bar"}); len(values) == 0 {
		t.Errorf("fetch multi failed: expected %d, got %d", 2, len(values))
	}

	if err := c.Delete(testKey); err != nil {
		t.Errorf("delete failed: expected nil, got %v", err)
	}

	if err := c.Flush(); err != nil {
		t.Errorf("flush failed: expected nil, got %v", err)
	}

	if c.Contains(testKey) {
		t.Errorf("contains failed: the key %s should not be exist", testKey)
	}

	c = New(
		sync.New(),
		memcached.New(memcache.New("127.0.0.1:22222")),
	)

	if err := c.Save(testKey, testValue, 0); err == nil {
		t.Errorf("save failed: expected an error, got %v", err)
	}

	if err := c.Delete(testKey); err == nil {
		t.Errorf("delete failed: expected an error, got %v", err)
	}

	if err := c.Flush(); err == nil {
		t.Errorf("flush failed: expected an error, got %v", err)
	}
}


================================================
FILE: doc.go
================================================
// Package cachego provides a simple way to use cache drivers.
//
// # Example Usage
//
// The following is a simple example using memcached driver:
//
//	import (
//	  "fmt"
//	  "github.com/faabiosr/cachego"
//	  "github.com/bradfitz/gomemcache/memcache"
//	)
//
//	func main() {
//
//	  cache := cachego.NewMemcached(
//	      memcached.New("localhost:11211"),
//	  )
//
//	  cache.Save("foo", "bar")
//
//	  fmt.Println(cache.Fetch("foo"))
//	}
package cachego


================================================
FILE: docker-compose.yml
================================================
---
services:
  memcached:
      image: "memcached:alpine"
      ports:
          - "11211:11211"

  redis:
      image: "redis:alpine"
      ports:
          - "6379:6379"

  mongodb:
      image: "mongo:3.6"
      ports:
          - "27017:27017"


================================================
FILE: errors.go
================================================
package cachego

type err string

// Error returns the string error value.
func (e err) Error() string {
	return string(e)
}

const (
	// ErrCacheExpired returns an error when the cache key was expired.
	ErrCacheExpired = err("cache expired")

	// ErrFlush returns an error when flush fails.
	ErrFlush = err("unable to flush")

	// ErrSave returns an error when save fails.
	ErrSave = err("unable to save")

	// ErrDelete returns an error when deletion fails.
	ErrDelete = err("unable to delete")

	// ErrDecode returns an errors when decode fails.
	ErrDecode = err("unable to decode")
)


================================================
FILE: errors_test.go
================================================
package cachego

import (
	"fmt"
	"testing"
)

func TestError(t *testing.T) {
	expect := "failed"
	er := err(expect)

	if r := fmt.Sprint(er); r != expect {
		t.Errorf("invalid string: expect %s, got %s", expect, r)
	}
}


================================================
FILE: file/README.md
================================================
# Cachego - File driver
The driver stores the cache data in file.

## Usage

```go
package main

import (
	"log"
	"time"

	"github.com/faabiosr/cachego/file"
)

func main() {
	cache := file.New("./cache-dir/")

	if err := cache.Save("user_id", "1", 10*time.Second); err != nil {
		log.Fatal(err)
	}

	id, err := cache.Fetch("user_id")
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("user id: %s \n", id)
}
```


================================================
FILE: file/file.go
================================================
// Package file providers a cache driver that stores the cache content in files.
package file

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/faabiosr/cachego"
)

type (
	file struct {
		dir string
		sync.RWMutex
	}

	fileContent struct {
		Duration int64  `json:"duration"`
		Data     string `json:"data,omitempty"`
	}
)

const perm = 0o666

// New creates an instance of File cache
func New(dir string) cachego.Cache {
	return &file{dir: dir}
}

func (f *file) createName(key string) string {
	h := sha256.New()
	_, _ = h.Write([]byte(key))
	hash := hex.EncodeToString(h.Sum(nil))

	return filepath.Join(f.dir, fmt.Sprintf("%s.cachego", hash))
}

func (f *file) read(key string) (*fileContent, error) {
	f.RLock()
	defer f.RUnlock()

	value, err := os.ReadFile(f.createName(key))
	if err != nil {
		return nil, err
	}

	content := &fileContent{}
	if err := json.Unmarshal(value, content); err != nil {
		return nil, err
	}

	if content.Duration == 0 {
		return content, nil
	}

	return content, nil
}

// Contains checks if the cached key exists into the File storage
func (f *file) Contains(key string) bool {
	content, err := f.read(key)
	if err != nil {
		return false
	}

	if f.isExpired(content) {
		_ = f.Delete(key)
		return false
	}
	return true
}

// Delete the cached key from File storage
func (f *file) Delete(key string) error {
	f.Lock()
	defer f.Unlock()

	_, err := os.Stat(f.createName(key))
	if err != nil && os.IsNotExist(err) {
		return nil
	}

	return os.Remove(f.createName(key))
}

// Fetch retrieves the cached value from key of the File storage
func (f *file) Fetch(key string) (string, error) {
	content, err := f.read(key)
	if err != nil {
		return "", err
	}

	if f.isExpired(content) {
		_ = f.Delete(key)
		return "", cachego.ErrCacheExpired
	}

	return content.Data, nil
}

func (f *file) isExpired(content *fileContent) bool {
	return content.Duration > 0 && content.Duration <= time.Now().Unix()
}

// FetchMulti retrieve multiple cached values from keys of the File storage
func (f *file) FetchMulti(keys []string) map[string]string {
	result := make(map[string]string)

	for _, key := range keys {
		if value, err := f.Fetch(key); err == nil {
			result[key] = value
		}
	}

	return result
}

// Flush removes all cached keys of the File storage
func (f *file) Flush() error {
	f.Lock()
	defer f.Unlock()

	dir, err := os.Open(f.dir)
	if err != nil {
		return err
	}

	defer func() {
		_ = dir.Close()
	}()

	names, _ := dir.Readdirnames(-1)

	for _, name := range names {
		_ = os.Remove(filepath.Join(f.dir, name))
	}

	return nil
}

// Save a value in File storage by key
func (f *file) Save(key string, value string, lifeTime time.Duration) error {
	f.Lock()
	defer f.Unlock()

	duration := int64(0)
	if lifeTime > 0 {
		duration = time.Now().Unix() + int64(lifeTime.Seconds())
	}

	content := &fileContent{duration, value}
	data, err := json.Marshal(content)
	if err != nil {
		return err
	}

	return os.WriteFile(f.createName(key), data, perm)
}


================================================
FILE: file/file_test.go
================================================
package file

import (
	"os"
	"testing"
	"time"
)

const (
	testKey   = "foo"
	testValue = "bar"
)

func TestFile(t *testing.T) {
	dir, err := os.MkdirTemp("", t.Name())
	if err != nil {
		t.Fatal(err)
	}

	c := New(dir)

	if err := c.Save(testKey, testValue, 1*time.Nanosecond); err != nil {
		t.Errorf("save fail: expected nil, got %v", err)
	}

	if _, err := c.Fetch(testKey); err == nil {
		t.Errorf("fetch fail: expected an error, got %v", err)
	}

	_ = c.Save(testKey, testValue, 10*time.Second)

	if res, _ := c.Fetch(testKey); res != testValue {
		t.Errorf("fetch fail, wrong value: expected %s, got %s", testValue, res)
	}

	_ = c.Save(testKey, testValue, 0)

	if !c.Contains(testKey) {
		t.Errorf("contains failed: the key %s should be exist", testKey)
	}

	_ = c.Save("bar", testValue, 0)

	if values := c.FetchMulti([]string{testKey, "bar"}); len(values) == 0 {
		t.Errorf("fetch multi failed: expected %d, got %d", 2, len(values))
	}

	if err := c.Flush(); err != nil {
		t.Errorf("flush failed: expected nil, got %v", err)
	}

	if c.Contains(testKey) {
		t.Errorf("contains failed: the key %s should not be exist", testKey)
	}

	c = New("./test/")

	if err := c.Save(testKey, testValue, 0); err == nil {
		t.Errorf("save failed: expected an error, got %v", err)
	}

	if _, err := c.Fetch(testKey); err == nil {
		t.Errorf("fetch failed: expected and error, got %v", err)
	}

	if err := c.Flush(); err == nil {
		t.Errorf("flush failed: expected an error, got %v", err)
	}
}


================================================
FILE: go.mod
================================================
module github.com/faabiosr/cachego

go 1.21

require (
	github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874
	github.com/mattn/go-sqlite3 v1.14.24
	github.com/redis/go-redis/v9 v9.7.3
	go.etcd.io/bbolt v1.3.10
	go.mongodb.org/mongo-driver/v2 v2.0.1
)

require (
	github.com/cespare/xxhash/v2 v2.2.0 // indirect
	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
	github.com/golang/snappy v0.0.4 // indirect
	github.com/klauspost/compress v1.16.7 // indirect
	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
	github.com/xdg-go/scram v1.1.2 // indirect
	github.com/xdg-go/stringprep v1.0.4 // indirect
	github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
	golang.org/x/crypto v0.33.0 // indirect
	golang.org/x/sync v0.11.0 // indirect
	golang.org/x/sys v0.30.0 // indirect
	golang.org/x/text v0.22.0 // indirect
)


================================================
FILE: go.sum
================================================
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous=
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
go.mongodb.org/mongo-driver/v2 v2.0.1 h1:mhB/ZJkLSv6W6LGzY7sEjpZif47+JdfEEXjlLCIv7Qc=
go.mongodb.org/mongo-driver/v2 v2.0.1/go.mod h1:w7iFnTcQDMXtdXwcvyG3xljYpoBa1ErkI0yOzbkZ9b8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=


================================================
FILE: memcached/README.md
================================================
# Cachego - Memcached driver
The drivers uses [gomemcache](https://github.com/bradfitz/gomemcache) to store the cache data.

## Usage

```go
package main

import (
	"log"
	"time"

	"github.com/bradfitz/gomemcache/memcache"

	"github.com/faabiosr/cachego/memcached"
)

func main() {
	cache := memcached.New(
		memcache.New("localhost:11211"),
	)

	if err := cache.Save("user_id", "1", 10*time.Second); err != nil {
		log.Fatal(err)
	}

	id, err := cache.Fetch("user_id")
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("user id: %s \n", id)
}
```


================================================
FILE: memcached/memcached.go
================================================
// Package memcached providers a cache driver that stores the cache in Memcached.
package memcached

import (
	"time"

	"github.com/bradfitz/gomemcache/memcache"
	"github.com/faabiosr/cachego"
)

type memcached struct {
	driver *memcache.Client
}

// New creates an instance of Memcached cache driver
func New(driver *memcache.Client) cachego.Cache {
	return &memcached{driver}
}

// Contains checks if cached key exists in Memcached storage
func (m *memcached) Contains(key string) bool {
	_, err := m.Fetch(key)
	return err == nil
}

// Delete the cached key from Memcached storage
func (m *memcached) Delete(key string) error {
	return m.driver.Delete(key)
}

// Fetch retrieves the cached value from key of the Memcached storage
func (m *memcached) Fetch(key string) (string, error) {
	item, err := m.driver.Get(key)
	if err != nil {
		return "", err
	}

	return string(item.Value), nil
}

// FetchMulti retrieves multiple cached value from keys of the Memcached storage
func (m *memcached) FetchMulti(keys []string) map[string]string {
	result := make(map[string]string)

	items, err := m.driver.GetMulti(keys)
	if err != nil {
		return result
	}

	for _, i := range items {
		result[i.Key] = string(i.Value)
	}

	return result
}

// Flush removes all cached keys of the Memcached storage
func (m *memcached) Flush() error {
	return m.driver.FlushAll()
}

// Save a value in Memcached storage by key
func (m *memcached) Save(key string, value string, lifeTime time.Duration) error {
	return m.driver.Set(
		&memcache.Item{
			Key:        key,
			Value:      []byte(value),
			Expiration: int32(lifeTime.Seconds()),
		},
	)
}


================================================
FILE: memcached/memcached_test.go
================================================
package memcached

import (
	"net"
	"testing"
	"time"

	"github.com/bradfitz/gomemcache/memcache"
)

const (
	testKey   = "foo"
	testValue = "bar"
)

func TestMemcached(t *testing.T) {
	address := "localhost:11211"
	conn := memcache.New(address)

	if _, err := net.Dial("tcp", address); err != nil {
		t.Skip(err)
	}

	c := New(conn)

	if err := c.Save(testKey, testValue, 1*time.Nanosecond); err != nil {
		t.Errorf("save fail: expected nil, got %v", err)
	}

	if _, err := c.Fetch("bar"); err == nil {
		t.Errorf("fetch fail: expected an error, got %v", err)
	}

	_ = c.Save(testKey, testValue, 10*time.Second)

	if res, _ := c.Fetch(testKey); res != testValue {
		t.Errorf("fetch fail, wrong value: expected %s, got %s", testValue, res)
	}

	_ = c.Save(testKey, testValue, 0)

	if !c.Contains(testKey) {
		t.Errorf("contains failed: the key %s should be exist", testKey)
	}

	_ = c.Save("bar", testValue, 0)

	if values := c.FetchMulti([]string{testKey, "bar"}); len(values) == 0 {
		t.Errorf("fetch multi failed: expected %d, got %d", 2, len(values))
	}

	if err := c.Flush(); err != nil {
		t.Errorf("flush failed: expected nil, got %v", err)
	}

	if c.Contains(testKey) {
		t.Errorf("contains failed: the key %s should not be exist", testKey)
	}

	c = New(memcache.New("localhost:22222"))

	if values := c.FetchMulti([]string{testKey, "bar"}); len(values) != 0 {
		t.Errorf("fetch multi failed: expected %d, got %d", 0, len(values))
	}
}


================================================
FILE: mongo/README.md
================================================
# Cachego - Mongo driver
The drivers uses [go-mgo](https://github.com/go-mgo/mgo) to store the cache data.

## Usage

```go
package main

import (
	"context"
	"log"
	"time"

	"go.mongodb.org/mongo-driver/v2/mongo"
	"go.mongodb.org/mongo-driver/v2/mongo/options"

	"github.com/faabiosr/cachego/mongo"
)

func main() {
	opts := options.Client().ApplyURI("mongodb://localhost:27017")
	client, _ := mongo.Connect(opts)

	cache := mongo.New(
	    client.Database("cache").Collection("cache"),
    )

	if err := cache.Save("user_id", "1", 10*time.Second); err != nil {
		log.Fatal(err)
	}

	id, err := cache.Fetch("user_id")
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("user id: %s \n", id)
}
```


================================================
FILE: mongo/mongo.go
================================================
// Package mongo providers a cache driver that stores the cache in MongoDB.
package mongo

import (
	"context"
	"time"

	"github.com/faabiosr/cachego"
	"go.mongodb.org/mongo-driver/v2/bson"
	"go.mongodb.org/mongo-driver/v2/mongo"
	"go.mongodb.org/mongo-driver/v2/mongo/options"
)

type (
	mongoCache struct {
		collection *mongo.Collection
	}

	mongoContent struct {
		Duration int64
		Key      string `bson:"_id"`
		Value    string
	}
)

// New creates an instance of Mongo cache driver
func New(collection *mongo.Collection) cachego.Cache {
	return &mongoCache{collection}
}

// NewMongoDriver alias for New.
func NewMongoDriver(collection *mongo.Collection) cachego.Cache {
	return New(collection)
}

func (m *mongoCache) Contains(key string) bool {
	_, err := m.Fetch(key)
	return err == nil
}

// Delete the cached key from Mongo storage
func (m *mongoCache) Delete(key string) error {
	_, err := m.collection.DeleteOne(context.TODO(), bson.M{"_id": bson.M{"$eq": key}})
	return err
}

// Fetch retrieves the cached value from key of the Mongo storage
func (m *mongoCache) Fetch(key string) (string, error) {
	content := &mongoContent{}
	result := m.collection.FindOne(context.TODO(), bson.M{"_id": bson.M{"$eq": key}})
	if result == nil {
		return "", cachego.ErrCacheExpired
	}
	if result.Err() != nil {
		return "", result.Err()
	}

	err := result.Decode(&content)
	if err != nil {
		return "", err
	}
	if content.Duration == 0 {
		return content.Value, nil
	}

	if content.Duration <= time.Now().Unix() {
		_ = m.Delete(key)
		return "", cachego.ErrCacheExpired
	}
	return content.Value, nil
}

func (m *mongoCache) FetchMulti(keys []string) map[string]string {
	result := make(map[string]string)

	cur, err := m.collection.Find(context.TODO(), bson.M{"_id": bson.M{"$in": keys}})
	if err != nil {
		return result
	}
	defer func() {
		_ = cur.Close(context.Background())
	}()

	content := &mongoContent{}

	for cur.Next(context.Background()) {
		err := cur.Decode(content)
		if err != nil {
			continue
		}

		result[content.Key] = content.Value
	}
	return result
}

// Flush removes all cached keys of the Mongo storage
func (m *mongoCache) Flush() error {
	_, err := m.collection.DeleteMany(context.TODO(), bson.M{})
	return err
}

// Save a value in Mongo storage by key
func (m *mongoCache) Save(key string, value string, lifeTime time.Duration) error {
	duration := int64(0)

	if lifeTime > 0 {
		duration = time.Now().Unix() + int64(lifeTime.Seconds())
	}

	content := &mongoContent{duration, key, value}
	opts := options.Replace().SetUpsert(true)
	_, err := m.collection.ReplaceOne(context.TODO(), bson.M{"_id": bson.M{"$eq": key}}, content, opts)
	return err
}


================================================
FILE: mongo/mongo_test.go
================================================
package mongo

import (
	"fmt"
	"testing"
	"time"

	"go.mongodb.org/mongo-driver/v2/mongo"
	"go.mongodb.org/mongo-driver/v2/mongo/options"
)

const (
	testKeyMongo   = "foo1"
	testValueMongo = "bar"
)

func TestMongo(t *testing.T) {
	// Set client options
	clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")

	// Connect to MongoDB
	client, err := mongo.Connect(clientOptions)
	if err != nil {
		t.Skip(err)
	}
	collection := client.Database("cache").Collection("cache")

	cache := New(collection)

	if err := cache.Save(testKeyMongo, testValueMongo, 1*time.Nanosecond); err != nil {
		t.Errorf("save fail: expected nil, got %v", err)
	}

	if v, err := cache.Fetch(testKeyMongo); err == nil {
		t.Errorf("fetch fail: expected an error, got %v value %v", err, v)
	}

	_ = cache.Save(testKeyMongo, testValueMongo, 10*time.Second)

	if res, _ := cache.Fetch(testKeyMongo); res != testValueMongo {
		t.Errorf("fetch fail, wrong value : expected %s, got %s", testValueMongo, res)
	}

	_ = cache.Save(testKeyMongo, testValueMongo, 0)

	if !cache.Contains(testKeyMongo) {
		t.Errorf("contains failed: the key %s should be exist", testKeyMongo)
	}

	_ = cache.Save("bar", testValueMongo, 0)

	if values := cache.FetchMulti([]string{testKeyMongo, "bar"}); len(values) != 2 {
		fmt.Println(values)
		t.Errorf("fetch multi failed: expected %d, got %d", 2, len(values))
	}

	if err := cache.Flush(); err != nil {
		t.Errorf("flush failed: expected nil, got %v", err)
	}

	if cache.Contains(testKeyMongo) {
		t.Errorf("contains failed: the key %s should not be exist", testKeyMongo)
	}
}


================================================
FILE: redis/README.md
================================================
# Cachego - Redis driver
The drivers uses [go-redis](https://github.com/go-redis/redis) to store the cache data.

## Usage

```go
package main

import (
	"log"
	"time"

	rd "github.com/redis/go-redis/v9"

	"github.com/faabiosr/cachego/redis"
)

func main() {
	cache := redis.New(
		rd.NewClient(&rd.Options{
			Addr: ":6379",
		}),
	)

	if err := cache.Save("user_id", "1", 10*time.Second); err != nil {
		log.Fatal(err)
	}

	id, err := cache.Fetch("user_id")
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("user id: %s \n", id)
}
```


================================================
FILE: redis/redis.go
================================================
// Package redis providers a cache driver that stores the cache in Redis.
package redis

import (
	"context"
	"time"

	rd "github.com/redis/go-redis/v9"

	"github.com/faabiosr/cachego"
)

type redis struct {
	driver rd.Cmdable
}

// New creates an instance of Redis cache driver
func New(driver rd.Cmdable) cachego.Cache {
	return &redis{driver}
}

// Contains checks if cached key exists in Redis storage
func (r *redis) Contains(key string) bool {
	i, _ := r.driver.Exists(context.Background(), key).Result()
	return i > 0
}

// Delete the cached key from Redis storage
func (r *redis) Delete(key string) error {
	return r.driver.Del(context.Background(), key).Err()
}

// Fetch retrieves the cached value from key of the Redis storage
func (r *redis) Fetch(key string) (string, error) {
	return r.driver.Get(context.Background(), key).Result()
}

// FetchMulti retrieves multiple cached value from keys of the Redis storage
func (r *redis) FetchMulti(keys []string) map[string]string {
	result := make(map[string]string)

	items, err := r.driver.MGet(context.Background(), keys...).Result()
	if err != nil {
		return result
	}

	for i := 0; i < len(keys); i++ {
		if items[i] != nil {
			result[keys[i]] = items[i].(string)
		}
	}

	return result
}

// Flush removes all cached keys of the Redis storage
func (r *redis) Flush() error {
	return r.driver.FlushAll(context.Background()).Err()
}

// Save a value in Redis storage by key
func (r *redis) Save(key string, value string, lifeTime time.Duration) error {
	return r.driver.Set(context.Background(), key, value, lifeTime).Err()
}


================================================
FILE: redis/redis_test.go
================================================
package redis

import (
	"net"
	"testing"
	"time"

	rd "github.com/redis/go-redis/v9"
)

const (
	testKey   = "foo"
	testValue = "bar"
)

func TestRedis(t *testing.T) {
	conn := rd.NewClient(&rd.Options{
		Addr: ":6379",
	})

	if _, err := net.Dial("tcp", "localhost:6379"); err != nil {
		t.Skip(err)
	}

	c := New(conn)

	if err := c.Save(testKey, testValue, 10*time.Second); err != nil {
		t.Errorf("save fail: expected nil, got %v", err)
	}

	if res, _ := c.Fetch(testKey); res != testValue {
		t.Errorf("fetch fail, wrong value: expected %s, got %s", testValue, res)
	}

	if _, err := c.Fetch("bar"); err == nil {
		t.Errorf("fetch fail: expected an error, got %v", err)
	}

	if !c.Contains(testKey) {
		t.Errorf("contains failed: the key %s should be exist", testKey)
	}

	_ = c.Save("bar", testValue, 0)

	if values := c.FetchMulti([]string{testKey, "bar"}); len(values) == 0 {
		t.Errorf("fetch multi failed: expected %d, got %d", 2, len(values))
	}

	if err := c.Delete(testKey); err != nil {
		t.Errorf("delete failed: expected nil, got %v", err)
	}

	if err := c.Flush(); err != nil {
		t.Errorf("flush failed: expected nil, got %v", err)
	}

	if c.Contains(testKey) {
		t.Errorf("contains failed: the key %s should not be exist", testKey)
	}

	conn = rd.NewClient(&rd.Options{Addr: ":6380"})

	c = New(conn)

	if c.Contains(testKey) {
		t.Errorf("contains failed: the key %s should not be exist", testKey)
	}

	if values := c.FetchMulti([]string{testKey, "bar"}); len(values) != 0 {
		t.Errorf("fetch multi failed: expected %d, got %d", 0, len(values))
	}
}


================================================
FILE: sqlite3/README.md
================================================
# Cachego - SQLite3 driver
The drivers uses [go-sqlite3](https://github.com/mattn/go-sqlite3) to store the cache data.

## Usage

```go
package main

import (
	"database/sql"
	"log"
	"time"

	_ "github.com/mattn/go-sqlite3"

	"github.com/faabiosr/cachego/sqlite3"
)

func main() {
	db, err := sql.Open("sqlite3", "./cache.db")
	if err != nil {
		log.Fatal(err)
	}

	cache, err := sqlite3.New(db, "cache")
	if err != nil {
		log.Fatal(err)
	}

	if err := cache.Save("user_id", "1", 10*time.Second); err != nil {
		log.Fatal(err)
	}

	id, err := cache.Fetch("user_id")
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("user id: %s \n", id)
}
```


================================================
FILE: sqlite3/sqlite3.go
================================================
// Package sqlite3 providers a cache driver that stores the cache in SQLite3.
package sqlite3

import (
	"database/sql"
	"fmt"
	"time"

	"github.com/faabiosr/cachego"
)

type (
	sqlite3 struct {
		db    *sql.DB
		table string
	}
)

// New creates an instance of Sqlite3 cache driver
func New(db *sql.DB, table string) (cachego.Cache, error) {
	return &sqlite3{db, table}, createTable(db, table)
}

func createTable(db *sql.DB, table string) error {
	stmt := `CREATE TABLE IF NOT EXISTS %s (
        key text PRIMARY KEY,
        value text NOT NULL,
        lifetime integer NOT NULL
    );`

	_, err := db.Exec(fmt.Sprintf(stmt, table))
	return err
}

// Contains checks if cached key exists in Sqlite3 storage
func (s *sqlite3) Contains(key string) bool {
	_, err := s.Fetch(key)
	return err == nil
}

// Delete the cached key from Sqlite3 storage
func (s *sqlite3) Delete(key string) error {
	tx, err := s.db.Begin()
	if err != nil {
		return err
	}

	stmt, err := tx.Prepare(fmt.Sprintf(`
		DELETE FROM %s
		WHERE key = ?
	`, s.table))
	if err != nil {
		return err
	}

	defer func() {
		_ = stmt.Close()
	}()

	if _, err := stmt.Exec(key); err != nil {
		_ = tx.Rollback()
		return err
	}

	return tx.Commit()
}

// Fetch retrieves the cached value from key of the Sqlite3 storage
func (s *sqlite3) Fetch(key string) (string, error) {
	stmt, err := s.db.Prepare(fmt.Sprintf(`
		SELECT value, lifetime
		FROM %s WHERE key = ?
	`, s.table))
	if err != nil {
		return "", err
	}

	defer func() {
		_ = stmt.Close()
	}()

	var value string
	var lifetime int64

	if err := stmt.QueryRow(key).Scan(&value, &lifetime); err != nil {
		return "", err
	}

	if lifetime == 0 {
		return value, nil
	}

	if lifetime <= time.Now().Unix() {
		_ = s.Delete(key)
		return "", cachego.ErrCacheExpired
	}

	return value, nil
}

// FetchMulti retrieves multiple cached value from keys of the Sqlite3 storage
func (s *sqlite3) FetchMulti(keys []string) map[string]string {
	result := make(map[string]string)

	for _, key := range keys {
		if value, err := s.Fetch(key); err == nil {
			result[key] = value
		}
	}

	return result
}

// Flush removes all cached keys of the Sqlite3 storage
func (s *sqlite3) Flush() error {
	tx, err := s.db.Begin()
	if err != nil {
		return err
	}

	stmt, err := tx.Prepare(fmt.Sprintf("DELETE FROM %s", s.table))
	if err != nil {
		return err
	}

	defer func() {
		_ = stmt.Close()
	}()

	if _, err := stmt.Exec(); err != nil {
		_ = tx.Rollback()
		return err
	}

	return tx.Commit()
}

// Save a value in Sqlite3 storage by key
func (s *sqlite3) Save(key string, value string, lifeTime time.Duration) error {
	duration := int64(0)

	if lifeTime > 0 {
		duration = time.Now().Unix() + int64(lifeTime.Seconds())
	}

	tx, err := s.db.Begin()
	if err != nil {
		return err
	}

	stmt, err := tx.Prepare(fmt.Sprintf(`
		INSERT OR REPLACE INTO %s (key, value, lifetime)
		VALUES (?, ?, ?)
	`, s.table))
	if err != nil {
		return err
	}

	defer func() {
		_ = stmt.Close()
	}()

	if _, err := stmt.Exec(key, value, duration); err != nil {
		_ = tx.Rollback()
		return err
	}

	return tx.Commit()
}


================================================
FILE: sqlite3/sqlite3_test.go
================================================
package sqlite3

import (
	"database/sql"
	"fmt"
	"os"
	"testing"
	"time"

	_ "github.com/mattn/go-sqlite3"
)

const (
	testKey    = "foo"
	testValue  = "bar"
	testTable  = "cache"
	testDBPath = "/cache.db"
)

func TestSqlite3(t *testing.T) {
	dir, err := os.MkdirTemp("", t.Name())
	if err != nil {
		t.Fatal(err)
	}

	db, err := sql.Open("sqlite3", dir+testDBPath)
	if err != nil {
		t.Skip(err)
	}

	t.Cleanup(func() {
		_ = os.RemoveAll(dir)
	})

	c, err := New(db, testTable)
	if err != nil {
		t.Skip(err)
	}

	if err := c.Save(testKey, testValue, 1*time.Nanosecond); err != nil {
		t.Errorf("save fail: expected nil, got %v", err)
	}

	if _, err := c.Fetch(testKey); err == nil {
		t.Errorf("fetch fail: expected an error, got %v", err)
	}

	_ = c.Save(testKey, testValue, 10*time.Second)

	if res, _ := c.Fetch(testKey); res != testValue {
		t.Errorf("fetch fail, wrong value: expected %s, got %s", testValue, res)
	}

	_ = c.Save(testKey, testValue, 0)

	if !c.Contains(testKey) {
		t.Errorf("contains failed: the key %s should be exist", testKey)
	}

	_ = c.Save("bar", testValue, 0)

	if values := c.FetchMulti([]string{testKey, "bar"}); len(values) == 0 {
		t.Errorf("fetch multi failed: expected %d, got %d", 2, len(values))
	}

	if err := c.Flush(); err != nil {
		t.Errorf("flush failed: expected nil, got %v", err)
	}

	if c.Contains(testKey) {
		t.Errorf("contains failed: the key %s should not be exist", testKey)
	}
}

func TestSqlite3Fail(t *testing.T) {
	dir, err := os.MkdirTemp("", t.Name())
	if err != nil {
		t.Fatal(err)
	}

	db, _ := sql.Open("sqlite3", dir+testDBPath)
	_ = db.Close()

	t.Cleanup(func() {
		_ = os.RemoveAll(dir)
	})

	if _, err := New(db, testTable); err == nil {
		t.Errorf("constructor failed: expected an error, got %v", err)
	}

	db, _ = sql.Open("sqlite3", testDBPath)
	c, _ := New(db, testTable)
	_ = db.Close()

	if err := c.Save(testKey, testValue, 0); err == nil {
		t.Errorf("save failed: expected an error, got %v", err)
	}

	if err := c.Delete(testKey); err == nil {
		t.Errorf("delete failed: expected an error, got %v", err)
	}

	if err := c.Flush(); err == nil {
		t.Errorf("flush failed: expected an error, got %v", err)
	}

	db, _ = sql.Open("sqlite3", testDBPath)
	c, _ = New(db, testTable)

	_, _ = db.Exec(fmt.Sprintf("DROP TABLE %s;", testTable))

	if err := c.Save(testKey, testValue, 0); err == nil {
		t.Errorf("save failed: expected an error, got %v", err)
	}

	if err := c.Delete(testKey); err == nil {
		t.Errorf("delete failed: expected an error, got %v", err)
	}

	if err := c.Flush(); err == nil {
		t.Errorf("flush failed: expected an error, got %v", err)
	}
}


================================================
FILE: sync/README.md
================================================
# Cachego - Sync driver
The drivers uses [golang sync](https://golang.org/pkg/sync/#Map) to store the cache data.

## Usage

```go
package main

import (
	"log"
	"time"

	"github.com/faabiosr/cachego/sync"
)

func main() {
	cache := sync.New()

	if err := cache.Save("user_id", "1", 10*time.Second); err != nil {
		log.Fatal(err)
	}

	id, err := cache.Fetch("user_id")
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("user id: %s \n", id)
}
```


================================================
FILE: sync/map.go
================================================
// Package sync providers a cache driver that uses standard golang sync.Map.
package sync

import (
	"errors"
	"sync"
	"time"

	"github.com/faabiosr/cachego"
)

type (
	syncMapItem struct {
		data     string
		duration int64
	}

	syncMap struct {
		storage *sync.Map
	}
)

// New creates an instance of SyncMap cache driver
func New() cachego.Cache {
	return &syncMap{&sync.Map{}}
}

func (sm *syncMap) read(key string) (*syncMapItem, error) {
	v, ok := sm.storage.Load(key)
	if !ok {
		return nil, errors.New("key not found")
	}

	item := v.(*syncMapItem)

	if item.duration == 0 {
		return item, nil
	}

	if item.duration <= time.Now().Unix() {
		_ = sm.Delete(key)
		return nil, cachego.ErrCacheExpired
	}

	return item, nil
}

// Contains checks if cached key exists in SyncMap storage
func (sm *syncMap) Contains(key string) bool {
	_, err := sm.Fetch(key)
	return err == nil
}

// Delete the cached key from SyncMap storage
func (sm *syncMap) Delete(key string) error {
	sm.storage.Delete(key)
	return nil
}

// Fetch retrieves the cached value from key of the SyncMap storage
func (sm *syncMap) Fetch(key string) (string, error) {
	item, err := sm.read(key)
	if err != nil {
		return "", err
	}

	return item.data, nil
}

// FetchMulti retrieves multiple cached value from keys of the SyncMap storage
func (sm *syncMap) FetchMulti(keys []string) map[string]string {
	result := make(map[string]string)

	for _, key := range keys {
		if value, err := sm.Fetch(key); err == nil {
			result[key] = value
		}
	}

	return result
}

// Flush removes all cached keys of the SyncMap storage
func (sm *syncMap) Flush() error {
	sm.storage = &sync.Map{}
	return nil
}

// Save a value in SyncMap storage by key
func (sm *syncMap) Save(key string, value string, lifeTime time.Duration) error {
	duration := int64(0)

	if lifeTime > 0 {
		duration = time.Now().Unix() + int64(lifeTime.Seconds())
	}

	sm.storage.Store(key, &syncMapItem{value, duration})
	return nil
}


================================================
FILE: sync/map_test.go
================================================
package sync

import (
	"testing"
	"time"
)

const (
	testKey   = "foo"
	testValue = "bar"
)

func TestSyncMap(t *testing.T) {
	c := New()

	if err := c.Save(testKey, testValue, 1*time.Nanosecond); err != nil {
		t.Errorf("save fail: expected nil, got %v", err)
	}

	if _, err := c.Fetch(testKey); err == nil {
		t.Errorf("fetch fail: expected an error, got %v", err)
	}

	_ = c.Save(testKey, testValue, 10*time.Second)

	if res, _ := c.Fetch(testKey); res != testValue {
		t.Errorf("fetch fail, wrong value: expected %s, got %s", testValue, res)
	}

	_ = c.Save(testKey, testValue, 0)

	if !c.Contains(testKey) {
		t.Errorf("contains failed: the key %s should be exist", testKey)
	}

	_ = c.Save("bar", testValue, 0)

	if values := c.FetchMulti([]string{testKey, "bar"}); len(values) == 0 {
		t.Errorf("fetch multi failed: expected %d, got %d", 2, len(values))
	}

	if err := c.Flush(); err != nil {
		t.Errorf("flush failed: expected nil, got %v", err)
	}

	if c.Contains(testKey) {
		t.Errorf("contains failed: the key %s should not be exist", testKey)
	}
}
Download .txt
gitextract_vdd2xu1x/

├── .github/
│   └── workflows/
│       └── test.yml
├── .gitignore
├── .golangci.yaml
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── bolt/
│   ├── README.md
│   ├── bolt.go
│   └── bolt_test.go
├── cache.go
├── chain/
│   ├── README.md
│   ├── chain.go
│   └── chain_test.go
├── doc.go
├── docker-compose.yml
├── errors.go
├── errors_test.go
├── file/
│   ├── README.md
│   ├── file.go
│   └── file_test.go
├── go.mod
├── go.sum
├── memcached/
│   ├── README.md
│   ├── memcached.go
│   └── memcached_test.go
├── mongo/
│   ├── README.md
│   ├── mongo.go
│   └── mongo_test.go
├── redis/
│   ├── README.md
│   ├── redis.go
│   └── redis_test.go
├── sqlite3/
│   ├── README.md
│   ├── sqlite3.go
│   └── sqlite3_test.go
└── sync/
    ├── README.md
    ├── map.go
    └── map_test.go
Download .txt
SYMBOL INDEX (112 symbols across 19 files)

FILE: bolt/bolt.go
  type bolt (line 17) | type bolt struct
    method read (line 32) | func (b *bolt) read(key string) (*boltContent, error) {
    method Contains (line 65) | func (b *bolt) Contains(key string) bool {
    method Delete (line 71) | func (b *bolt) Delete(key string) error {
    method Fetch (line 82) | func (b *bolt) Fetch(key string) (string, error) {
    method FetchMulti (line 92) | func (b *bolt) FetchMulti(keys []string) map[string]string {
    method Flush (line 105) | func (b *bolt) Flush() error {
    method Save (line 112) | func (b *bolt) Save(key string, value string, lifeTime time.Duration) ...
  type boltContent (line 21) | type boltContent struct
  function New (line 28) | func New(db *bt.DB) cachego.Cache {

FILE: bolt/bolt_test.go
  constant testKey (line 13) | testKey   = "foo"
  constant testValue (line 14) | testValue = "bar"
  function TestBolt (line 17) | func TestBolt(t *testing.T) {

FILE: cache.go
  type Cache (line 9) | type Cache interface

FILE: chain/chain.go
  type chain (line 13) | type chain struct
    method Contains (line 24) | func (c *chain) Contains(key string) bool {
    method Delete (line 35) | func (c *chain) Delete(key string) error {
    method Fetch (line 46) | func (c *chain) Fetch(key string) (string, error) {
    method FetchMulti (line 59) | func (c *chain) FetchMulti(keys []string) map[string]string {
    method Flush (line 72) | func (c *chain) Flush() error {
    method Save (line 83) | func (c *chain) Save(key string, value string, lifeTime time.Duration)...
  function New (line 19) | func New(drivers ...cachego.Cache) cachego.Cache {

FILE: chain/chain_test.go
  constant testKey (line 13) | testKey   = "foo"
  constant testValue (line 14) | testValue = "bar"
  function TestChain (line 17) | func TestChain(t *testing.T) {

FILE: errors.go
  type err (line 3) | type err
    method Error (line 6) | func (e err) Error() string {
  constant ErrCacheExpired (line 12) | ErrCacheExpired = err("cache expired")
  constant ErrFlush (line 15) | ErrFlush = err("unable to flush")
  constant ErrSave (line 18) | ErrSave = err("unable to save")
  constant ErrDelete (line 21) | ErrDelete = err("unable to delete")
  constant ErrDecode (line 24) | ErrDecode = err("unable to decode")

FILE: errors_test.go
  function TestError (line 8) | func TestError(t *testing.T) {

FILE: file/file.go
  type file (line 18) | type file struct
    method createName (line 36) | func (f *file) createName(key string) string {
    method read (line 44) | func (f *file) read(key string) (*fileContent, error) {
    method Contains (line 66) | func (f *file) Contains(key string) bool {
    method Delete (line 80) | func (f *file) Delete(key string) error {
    method Fetch (line 93) | func (f *file) Fetch(key string) (string, error) {
    method isExpired (line 107) | func (f *file) isExpired(content *fileContent) bool {
    method FetchMulti (line 112) | func (f *file) FetchMulti(keys []string) map[string]string {
    method Flush (line 125) | func (f *file) Flush() error {
    method Save (line 148) | func (f *file) Save(key string, value string, lifeTime time.Duration) ...
  type fileContent (line 23) | type fileContent struct
  constant perm (line 29) | perm = 0o666
  function New (line 32) | func New(dir string) cachego.Cache {

FILE: file/file_test.go
  constant testKey (line 10) | testKey   = "foo"
  constant testValue (line 11) | testValue = "bar"
  function TestFile (line 14) | func TestFile(t *testing.T) {

FILE: memcached/memcached.go
  type memcached (line 11) | type memcached struct
    method Contains (line 21) | func (m *memcached) Contains(key string) bool {
    method Delete (line 27) | func (m *memcached) Delete(key string) error {
    method Fetch (line 32) | func (m *memcached) Fetch(key string) (string, error) {
    method FetchMulti (line 42) | func (m *memcached) FetchMulti(keys []string) map[string]string {
    method Flush (line 58) | func (m *memcached) Flush() error {
    method Save (line 63) | func (m *memcached) Save(key string, value string, lifeTime time.Durat...
  function New (line 16) | func New(driver *memcache.Client) cachego.Cache {

FILE: memcached/memcached_test.go
  constant testKey (line 12) | testKey   = "foo"
  constant testValue (line 13) | testValue = "bar"
  function TestMemcached (line 16) | func TestMemcached(t *testing.T) {

FILE: mongo/mongo.go
  type mongoCache (line 15) | type mongoCache struct
    method Contains (line 36) | func (m *mongoCache) Contains(key string) bool {
    method Delete (line 42) | func (m *mongoCache) Delete(key string) error {
    method Fetch (line 48) | func (m *mongoCache) Fetch(key string) (string, error) {
    method FetchMulti (line 73) | func (m *mongoCache) FetchMulti(keys []string) map[string]string {
    method Flush (line 98) | func (m *mongoCache) Flush() error {
    method Save (line 104) | func (m *mongoCache) Save(key string, value string, lifeTime time.Dura...
  type mongoContent (line 19) | type mongoContent struct
  function New (line 27) | func New(collection *mongo.Collection) cachego.Cache {
  function NewMongoDriver (line 32) | func NewMongoDriver(collection *mongo.Collection) cachego.Cache {

FILE: mongo/mongo_test.go
  constant testKeyMongo (line 13) | testKeyMongo   = "foo1"
  constant testValueMongo (line 14) | testValueMongo = "bar"
  function TestMongo (line 17) | func TestMongo(t *testing.T) {

FILE: redis/redis.go
  type redis (line 13) | type redis struct
    method Contains (line 23) | func (r *redis) Contains(key string) bool {
    method Delete (line 29) | func (r *redis) Delete(key string) error {
    method Fetch (line 34) | func (r *redis) Fetch(key string) (string, error) {
    method FetchMulti (line 39) | func (r *redis) FetchMulti(keys []string) map[string]string {
    method Flush (line 57) | func (r *redis) Flush() error {
    method Save (line 62) | func (r *redis) Save(key string, value string, lifeTime time.Duration)...
  function New (line 18) | func New(driver rd.Cmdable) cachego.Cache {

FILE: redis/redis_test.go
  constant testKey (line 12) | testKey   = "foo"
  constant testValue (line 13) | testValue = "bar"
  function TestRedis (line 16) | func TestRedis(t *testing.T) {

FILE: sqlite3/sqlite3.go
  type sqlite3 (line 13) | type sqlite3 struct
    method Contains (line 36) | func (s *sqlite3) Contains(key string) bool {
    method Delete (line 42) | func (s *sqlite3) Delete(key string) error {
    method Fetch (line 69) | func (s *sqlite3) Fetch(key string) (string, error) {
    method FetchMulti (line 102) | func (s *sqlite3) FetchMulti(keys []string) map[string]string {
    method Flush (line 115) | func (s *sqlite3) Flush() error {
    method Save (line 139) | func (s *sqlite3) Save(key string, value string, lifeTime time.Duratio...
  function New (line 20) | func New(db *sql.DB, table string) (cachego.Cache, error) {
  function createTable (line 24) | func createTable(db *sql.DB, table string) error {

FILE: sqlite3/sqlite3_test.go
  constant testKey (line 14) | testKey    = "foo"
  constant testValue (line 15) | testValue  = "bar"
  constant testTable (line 16) | testTable  = "cache"
  constant testDBPath (line 17) | testDBPath = "/cache.db"
  function TestSqlite3 (line 20) | func TestSqlite3(t *testing.T) {
  function TestSqlite3Fail (line 75) | func TestSqlite3Fail(t *testing.T) {

FILE: sync/map.go
  type syncMapItem (line 13) | type syncMapItem struct
  type syncMap (line 18) | type syncMap struct
    method read (line 28) | func (sm *syncMap) read(key string) (*syncMapItem, error) {
    method Contains (line 49) | func (sm *syncMap) Contains(key string) bool {
    method Delete (line 55) | func (sm *syncMap) Delete(key string) error {
    method Fetch (line 61) | func (sm *syncMap) Fetch(key string) (string, error) {
    method FetchMulti (line 71) | func (sm *syncMap) FetchMulti(keys []string) map[string]string {
    method Flush (line 84) | func (sm *syncMap) Flush() error {
    method Save (line 90) | func (sm *syncMap) Save(key string, value string, lifeTime time.Durati...
  function New (line 24) | func New() cachego.Cache {

FILE: sync/map_test.go
  constant testKey (line 9) | testKey   = "foo"
  constant testValue (line 10) | testValue = "bar"
  function TestSyncMap (line 13) | func TestSyncMap(t *testing.T) {
Condensed preview — 38 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (59K chars).
[
  {
    "path": ".github/workflows/test.yml",
    "chars": 1178,
    "preview": "name: test\n\non:\n  push:\n    branches:\n      - main\n      - develop\n  pull_request:\n    branches:\n      - main\n\njobs:\n  t"
  },
  {
    "path": ".gitignore",
    "chars": 29,
    "preview": "*.swp\n*.swo\ncover*\ncache-dir\n"
  },
  {
    "path": ".golangci.yaml",
    "chars": 465,
    "preview": "---\nrun:\n  timeout: \"240s\"\n\noutput:\n  formats:\n    - format: \"colored-line-number\"\n\nlinters:\n  enable:\n    - gocyclo\n   "
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 1663,
    "preview": "# Contributing\n\nBy participating to this project, you agree to abide our [code of conduct](/CODE_OF_CONDUCT.md).\n\n## Set"
  },
  {
    "path": "LICENSE",
    "chars": 1089,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2016 Fábio da Silva Ribeiro\n\nPermission is hereby granted, free of charge, to any p"
  },
  {
    "path": "Makefile",
    "chars": 604,
    "preview": ".DEFAULT_GOAL := test\n\n# Clean up\nclean:\n\t@rm -fR ./vendor/ ./cover.*\n.PHONY: clean\n\n# Run tests and generates html cove"
  },
  {
    "path": "README.md",
    "chars": 2256,
    "preview": "# Cachego\n\n[![Codecov branch](https://img.shields.io/codecov/c/github/faabiosr/cachego/main.svg?style=flat-square)](http"
  },
  {
    "path": "bolt/README.md",
    "chars": 558,
    "preview": "# Cachego - BoltDB driver\nThe drivers uses [etcd-io/bbolt](https://github.com/etcd-io/bbolt) to store the cache data.\n\n#"
  },
  {
    "path": "bolt/bolt.go",
    "chars": 2706,
    "preview": "// Package bolt providers a cache driver that stores the cache using BoltDB.\npackage bolt\n\nimport (\n\t\"encoding/json\"\n\t\"e"
  },
  {
    "path": "bolt/bolt_test.go",
    "chars": 1541,
    "preview": "package bolt\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\tbt \"go.etcd.io/bbolt\"\n)\n\nconst (\n\ttestKey   = \"foo\"\n\ttestValue "
  },
  {
    "path": "cache.go",
    "chars": 567,
    "preview": "package cachego\n\nimport (\n\t\"time\"\n)\n\ntype (\n\t// Cache is the top-level cache interface\n\tCache interface {\n\t\t// Contains "
  },
  {
    "path": "chain/README.md",
    "chars": 764,
    "preview": "# Cachego - Chain driver\nThe chain driver deals with multiple driver at same time, it could save the key in multiple dri"
  },
  {
    "path": "chain/chain.go",
    "chars": 1912,
    "preview": "// Package chain provides chaining cache drivers operations, in case of failure\n// the driver try to apply using the nex"
  },
  {
    "path": "chain/chain_test.go",
    "chars": 1693,
    "preview": "package chain\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/bradfitz/gomemcache/memcache\"\n\t\"github.com/faabiosr/cachego/mem"
  },
  {
    "path": "doc.go",
    "chars": 465,
    "preview": "// Package cachego provides a simple way to use cache drivers.\n//\n// # Example Usage\n//\n// The following is a simple exa"
  },
  {
    "path": "docker-compose.yml",
    "chars": 249,
    "preview": "---\nservices:\n  memcached:\n      image: \"memcached:alpine\"\n      ports:\n          - \"11211:11211\"\n\n  redis:\n      image:"
  },
  {
    "path": "errors.go",
    "chars": 588,
    "preview": "package cachego\n\ntype err string\n\n// Error returns the string error value.\nfunc (e err) Error() string {\n\treturn string("
  },
  {
    "path": "errors_test.go",
    "chars": 221,
    "preview": "package cachego\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nfunc TestError(t *testing.T) {\n\texpect := \"failed\"\n\ter := err(expect)\n\n\ti"
  },
  {
    "path": "file/README.md",
    "chars": 413,
    "preview": "# Cachego - File driver\nThe driver stores the cache data in file.\n\n## Usage\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\t\"time\""
  },
  {
    "path": "file/file.go",
    "chars": 3061,
    "preview": "// Package file providers a cache driver that stores the cache content in files.\npackage file\n\nimport (\n\t\"crypto/sha256\""
  },
  {
    "path": "file/file_test.go",
    "chars": 1488,
    "preview": "package file\n\nimport (\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n)\n\nconst (\n\ttestKey   = \"foo\"\n\ttestValue = \"bar\"\n)\n\nfunc TestFile(t *tes"
  },
  {
    "path": "go.mod",
    "chars": 871,
    "preview": "module github.com/faabiosr/cachego\n\ngo 1.21\n\nrequire (\n\tgithub.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b0387"
  },
  {
    "path": "go.sum",
    "chars": 6644,
    "preview": "github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous=\ngithub"
  },
  {
    "path": "memcached/README.md",
    "chars": 548,
    "preview": "# Cachego - Memcached driver\nThe drivers uses [gomemcache](https://github.com/bradfitz/gomemcache) to store the cache da"
  },
  {
    "path": "memcached/memcached.go",
    "chars": 1630,
    "preview": "// Package memcached providers a cache driver that stores the cache in Memcached.\npackage memcached\n\nimport (\n\t\"time\"\n\n\t"
  },
  {
    "path": "memcached/memcached_test.go",
    "chars": 1444,
    "preview": "package memcached\n\nimport (\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/bradfitz/gomemcache/memcache\"\n)\n\nconst (\n\ttestKey   "
  },
  {
    "path": "mongo/README.md",
    "chars": 697,
    "preview": "# Cachego - Mongo driver\nThe drivers uses [go-mgo](https://github.com/go-mgo/mgo) to store the cache data.\n\n## Usage\n\n``"
  },
  {
    "path": "mongo/mongo.go",
    "chars": 2678,
    "preview": "// Package mongo providers a cache driver that stores the cache in MongoDB.\npackage mongo\n\nimport (\n\t\"context\"\n\t\"time\"\n\n"
  },
  {
    "path": "mongo/mongo_test.go",
    "chars": 1596,
    "preview": "package mongo\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver"
  },
  {
    "path": "redis/README.md",
    "chars": 538,
    "preview": "# Cachego - Redis driver\nThe drivers uses [go-redis](https://github.com/go-redis/redis) to store the cache data.\n\n## Usa"
  },
  {
    "path": "redis/redis.go",
    "chars": 1588,
    "preview": "// Package redis providers a cache driver that stores the cache in Redis.\npackage redis\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\tr"
  },
  {
    "path": "redis/redis_test.go",
    "chars": 1570,
    "preview": "package redis\n\nimport (\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n\n\trd \"github.com/redis/go-redis/v9\"\n)\n\nconst (\n\ttestKey   = \"foo\"\n\ttes"
  },
  {
    "path": "sqlite3/README.md",
    "chars": 645,
    "preview": "# Cachego - SQLite3 driver\nThe drivers uses [go-sqlite3](https://github.com/mattn/go-sqlite3) to store the cache data.\n\n"
  },
  {
    "path": "sqlite3/sqlite3.go",
    "chars": 3109,
    "preview": "// Package sqlite3 providers a cache driver that stores the cache in SQLite3.\npackage sqlite3\n\nimport (\n\t\"database/sql\"\n"
  },
  {
    "path": "sqlite3/sqlite3_test.go",
    "chars": 2638,
    "preview": "package sqlite3\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t_ \"github.com/mattn/go-sqlite3\"\n)\n\nconst (\n\t"
  },
  {
    "path": "sync/README.md",
    "chars": 447,
    "preview": "# Cachego - Sync driver\nThe drivers uses [golang sync](https://golang.org/pkg/sync/#Map) to store the cache data.\n\n## Us"
  },
  {
    "path": "sync/map.go",
    "chars": 1962,
    "preview": "// Package sync providers a cache driver that uses standard golang sync.Map.\npackage sync\n\nimport (\n\t\"errors\"\n\t\"sync\"\n\t\""
  },
  {
    "path": "sync/map_test.go",
    "chars": 1061,
    "preview": "package sync\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nconst (\n\ttestKey   = \"foo\"\n\ttestValue = \"bar\"\n)\n\nfunc TestSyncMap(t *testin"
  }
]

About this extraction

This page contains the full source code of the faabiosr/cachego GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 38 files (51.9 KB), approximately 17.8k tokens, and a symbol index with 112 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!