src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

collection.go (4322B)


      1 package store
      2 
      3 import (
      4 	"encoding/json"
      5 	"fmt"
      6 	"os"
      7 	"path/filepath"
      8 	"sync"
      9 	"time"
     10 
     11 	"code.dwrz.net/src/pkg/log"
     12 )
     13 
     14 type Collection struct {
     15 	log  *log.Logger
     16 	path string
     17 	name string
     18 }
     19 
     20 func (s *Store) NewCollection(name string) error {
     21 	var c = &Collection{
     22 		log:  s.log,
     23 		name: name,
     24 		path: filepath.Join(s.path, name),
     25 	}
     26 
     27 	if _, err := os.Stat(c.path); os.IsNotExist(err) {
     28 		if err := os.MkdirAll(c.path, os.ModeDir|fm); err != nil {
     29 			return fmt.Errorf(
     30 				"failed to create directory: %w", err,
     31 			)
     32 		}
     33 	}
     34 
     35 	s.mu.Lock()
     36 	s.collections[c.name] = c
     37 	s.mu.Unlock()
     38 
     39 	return nil
     40 }
     41 
     42 func (s *Store) Collection(name string) *Collection {
     43 	s.mu.Lock()
     44 	defer s.mu.Unlock()
     45 
     46 	return s.collections[name]
     47 }
     48 
     49 func (c *Collection) All() ([]*Document, error) {
     50 	names, err := c.List()
     51 	if err != nil {
     52 		return nil, fmt.Errorf("failed to list: %w", err)
     53 	}
     54 
     55 	var (
     56 		wg sync.WaitGroup
     57 
     58 		mu   sync.Mutex
     59 		errs []error
     60 		docs []*Document
     61 	)
     62 	for _, name := range names {
     63 		wg.Add(1)
     64 
     65 		go func(name string) {
     66 			defer wg.Done()
     67 
     68 			d, err := c.FindId(name)
     69 			if err != nil {
     70 				mu.Lock()
     71 				errs = append(errs, fmt.Errorf(
     72 					"failed to load document: %v", err,
     73 				))
     74 				mu.Unlock()
     75 
     76 				return
     77 			}
     78 
     79 			mu.Lock()
     80 			docs = append(docs, d)
     81 			mu.Unlock()
     82 		}(name)
     83 	}
     84 
     85 	wg.Wait()
     86 
     87 	if len(errs) > 0 {
     88 		for _, err := range errs {
     89 			c.log.Error.Print(err)
     90 		}
     91 
     92 		return nil, fmt.Errorf("failed to load all quotes")
     93 	}
     94 
     95 	return docs, nil
     96 }
     97 
     98 func (c *Collection) Count() (int, error) {
     99 	f, err := os.Open(c.path)
    100 	if err != nil {
    101 		return 0, fmt.Errorf("failed to open: %w", err)
    102 	}
    103 	defer f.Close()
    104 
    105 	entries, err := f.ReadDir(0)
    106 	if err != nil {
    107 		return 0, fmt.Errorf("failed to read directory: %w", err)
    108 	}
    109 
    110 	var count int
    111 	for _, e := range entries {
    112 		// Nested collections are not allowed.
    113 		if e.IsDir() {
    114 			continue
    115 		}
    116 
    117 		// Ignore any files without our file extension.
    118 		if !hasExt(e.Name()) {
    119 			continue
    120 		}
    121 
    122 		count++
    123 	}
    124 
    125 	return count, nil
    126 }
    127 
    128 func (c *Collection) FindId(id string) (*Document, error) {
    129 	if !hasExt(id) {
    130 		id += ext
    131 	}
    132 
    133 	path := filepath.Join(c.path, id)
    134 	data, err := os.ReadFile(path)
    135 	if err != nil {
    136 		return nil, fmt.Errorf("failed to read file %s: %w", path, err)
    137 	}
    138 
    139 	var d = &Document{path: path}
    140 	if err := json.Unmarshal(data, d); err != nil {
    141 		return nil, fmt.Errorf(
    142 			"failed to json unmarshal document %s: %w", path, err,
    143 		)
    144 	}
    145 
    146 	return d, nil
    147 }
    148 
    149 func (c *Collection) List() ([]string, error) {
    150 	entries, err := os.ReadDir(c.path)
    151 	if err != nil {
    152 		return nil, fmt.Errorf("failed to read directory: %w", err)
    153 	}
    154 
    155 	var names []string
    156 	for _, e := range entries {
    157 		// Nested collections are not allowed.
    158 		if e.IsDir() {
    159 			continue
    160 		}
    161 
    162 		// Ignore any files without a JSON file extension.
    163 		var name = e.Name()
    164 		if !hasExt(name) {
    165 			continue
    166 		}
    167 
    168 		names = append(names, name)
    169 	}
    170 
    171 	return names, nil
    172 }
    173 
    174 func (c *Collection) Create(id string, v any) (*Document, error) {
    175 	data, err := json.MarshalIndent(v, "", "  ")
    176 	if err != nil {
    177 		return nil, fmt.Errorf("failed to json marshal: %w", err)
    178 	}
    179 
    180 	now := time.Now()
    181 	var d = &Document{
    182 		Created: now,
    183 		Data:    data,
    184 		Id:      id,
    185 		Updated: now,
    186 	}
    187 
    188 	data, err = json.MarshalIndent(d, "", "  ")
    189 	if err != nil {
    190 		return nil, fmt.Errorf("failed to json marshal: %w", err)
    191 	}
    192 
    193 	path := filepath.Join(c.path, d.Id+ext)
    194 	if err := os.WriteFile(path, data, fm); err != nil {
    195 		return nil, fmt.Errorf("failed to write file %s: %w", path, err)
    196 	}
    197 
    198 	return d, nil
    199 }
    200 
    201 func (c *Collection) Delete(id string) error {
    202 	var path = filepath.Join(c.path, id)
    203 	if err := os.Remove(path); err != nil {
    204 		return fmt.Errorf("failed to remove file: %v", err)
    205 	}
    206 
    207 	return nil
    208 }
    209 
    210 func (c *Collection) Random() (*Document, error) {
    211 	entries, err := os.ReadDir(c.path)
    212 	if err != nil {
    213 		return nil, fmt.Errorf("failed to read directory: %w", err)
    214 	}
    215 	if len(entries) == 0 {
    216 		return nil, fmt.Errorf("empty collection")
    217 	}
    218 
    219 	name := entries[random.Intn(len(entries))].Name()
    220 	path := filepath.Join(c.path, name)
    221 	data, err := os.ReadFile(path)
    222 	if err != nil {
    223 		return nil, fmt.Errorf("failed to read file %s: %w", path, err)
    224 	}
    225 
    226 	var d = &Document{
    227 		path: path,
    228 	}
    229 	if err := json.Unmarshal(data, d); err != nil {
    230 		return nil, fmt.Errorf(
    231 			"failed to json unmarshal document %s: %w", path, err,
    232 		)
    233 	}
    234 
    235 	return d, nil
    236 }