Skip to content

Instantly share code, notes, and snippets.

@peterhellberg
Last active January 27, 2025 21:23
Show Gist options
  • Save peterhellberg/c531189dff8a126540d587f2c4501422 to your computer and use it in GitHub Desktop.
Save peterhellberg/c531189dff8a126540d587f2c4501422 to your computer and use it in GitHub Desktop.
Minimal parametric JSON file store in Go
package main
import (
"encoding/json"
"io"
"os"
"path/filepath"
"sync"
)
type (
Name string
Habitat []string
)
type Fish struct {
ID string
Scientific Name
English Name
Swedish Name
Habitat Habitat
}
func main() {
if err := run("fishes", os.Stdout); err != nil {
panic(err)
}
}
func run(dir string, out io.Writer) error {
cacheDir, err := os.UserCacheDir()
if err != nil {
return err
}
path := filepath.Join(cacheDir, dir)
fishes, err := Store[Fish](path)
if err != nil {
return err
}
// Data grabbed from https://raw.githubusercontent.com/You-now-Who/dataset/refs/heads/main/List%20of%20fishes%20found%20in%20Sweden/List%20of%20fishes%20found%20in%20Sweden.csv
fishes.Set("abborre", Fish{
ID: "abborre",
Scientific: "Perca fluviatilis",
English: "European perch",
Swedish: "Abborre",
Habitat: Habitat{
"fresh",
"brackish",
},
})
fishes.Set("gädda", Fish{
ID: "gädda",
Scientific: "Esox lucius",
English: "Pike",
Swedish: "Gädda",
Habitat: Habitat{
"fresh",
"brackish",
},
})
fishes.Set("makrill", Fish{
ID: "makrill",
Scientific: "Scomber scombrus",
English: "Atlantic mackerel",
Swedish: "Makrill",
Habitat: Habitat{
"marine",
},
})
a, err := fishes.Get("abborre")
if err != nil {
return err
}
g, err := fishes.Get("gädda")
if err != nil {
return err
}
m, err := fishes.Get("makrill")
if err != nil {
return err
}
return encodeJSON(out, []Fish{a, g, m})
}
type Disk[T any] struct {
mu sync.RWMutex
path string
}
func Store[T any](path string) (*Disk[T], error) {
perm := os.ModeDir | os.ModePerm
if err := os.MkdirAll(path, perm); err != nil {
return nil, err
}
return &Disk[T]{path: path}, nil
}
func (d *Disk[T]) Set(id string, v T) error {
d.mu.Lock()
defer d.mu.Unlock()
f, err := os.Create(d.name(id))
if err != nil {
return err
}
defer f.Close()
return encodeJSON(f, v)
}
func (d *Disk[T]) Get(id string) (v T, err error) {
d.mu.RLock()
defer d.mu.RUnlock()
f, err := os.Open(d.name(id))
if err != nil {
return v, err
}
defer f.Close()
return v, json.NewDecoder(f).Decode(&v)
}
func (d *Disk[T]) name(id string) string {
return filepath.Join(d.path, id+".json")
}
func encodeJSON(w io.Writer, v any) error {
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
return enc.Encode(v)
}
@peterhellberg
Copy link
Author

Go Playground: https://go.dev/play/p/b1eteN_FBh5

Now with support for All and Vals methods.

@peterhellberg
Copy link
Author

I asked deepseek-r1:14b to describe what the latest version does:

The provided Go program is designed to store and retrieve fish data locally in a 
structured manner. Here's a step-by-step explanation:

1. **Setup and Data Structure**:
   - The program defines a `Fish` struct to hold information about different fish 
species, including their scientific, English, and Swedish names, along with their 
habitats.

2. **Storage Mechanism**:
   - It uses a key-value store approach where each fish entry is stored as a 
separate JSON file in a cache directory (`$HOME/.cache/fishes` by default).
   - Each entry is uniquely identified by an ID (e.g., "abborre").

3. **Storing Fish Data**:
   - The program adds three fish entries to the store using their respective IDs and 
detailed information.

4. **Retrieving All Entries**:
   - After storing the entries, it retrieves all stored fish data, which includes 
reading each JSON file in the cache directory.

5. **Output**:
   - Finally, it prints all retrieved fish data as a single JSON array to standard 
output.

This program is useful for applications needing local persistence of fish data, 
demonstrating how to use Go's file system and JSON encoding/decoding capabilities 
effectively.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment