github.com/wrgl/wrgl@v0.14.0/pkg/ref/fs/store.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright © 2022 Wrangle Ltd
     3  
     4  package reffs
     5  
     6  import (
     7  	"container/list"
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"path"
    12  	"time"
    13  
    14  	"github.com/google/uuid"
    15  	"github.com/wrgl/wrgl/pkg/ref"
    16  )
    17  
    18  type Store struct {
    19  	rootDir string
    20  }
    21  
    22  func NewStore(rootDir string) *Store {
    23  	return &Store{
    24  		rootDir: rootDir,
    25  	}
    26  }
    27  
    28  func (s *Store) createParentDir(p string) error {
    29  	return os.MkdirAll(path.Dir(p), 0755)
    30  }
    31  
    32  func (s *Store) writeFile(p string, b []byte) error {
    33  	err := s.createParentDir(p)
    34  	if err != nil {
    35  		return err
    36  	}
    37  	return os.WriteFile(p, b, 0666)
    38  }
    39  
    40  func (s *Store) openFileToAppend(p string) (*os.File, error) {
    41  	err := s.createParentDir(p)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  	return os.OpenFile(p, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
    46  }
    47  
    48  func (s *Store) refPath(key string) string {
    49  	return path.Join(s.rootDir, "refs", key)
    50  }
    51  
    52  func (s *Store) logPath(key string) string {
    53  	return path.Join(s.rootDir, "logs", key)
    54  }
    55  
    56  func (s *Store) Set(key string, val []byte) error {
    57  	return s.writeFile(s.refPath(key), val)
    58  }
    59  
    60  func (s *Store) SetWithLog(key string, val []byte, log *ref.Reflog) error {
    61  	f, err := s.openFileToAppend(s.logPath(key))
    62  	if err != nil {
    63  		return err
    64  	}
    65  	_, err = log.WriteTo(f)
    66  	if err != nil {
    67  		return err
    68  	}
    69  	_, err = f.Write([]byte{'\n'})
    70  	if err != nil {
    71  		return err
    72  	}
    73  	err = f.Close()
    74  	if err != nil {
    75  		return err
    76  	}
    77  	return s.Set(key, val)
    78  }
    79  
    80  func (s *Store) Get(key string) (val []byte, err error) {
    81  	val, err = os.ReadFile(s.refPath(key))
    82  	if err != nil {
    83  		return nil, ref.ErrKeyNotFound
    84  	}
    85  	return val, nil
    86  }
    87  
    88  func (s *Store) Delete(key string) error {
    89  	err := os.Remove(s.refPath(key))
    90  	if err != nil {
    91  		return err
    92  	}
    93  	p := s.logPath(key)
    94  	if _, err := os.Stat(p); err == nil {
    95  		return os.Remove(p)
    96  	}
    97  	return nil
    98  }
    99  
   100  func (s *Store) Filter(prefixes, notPrefixes []string) (m map[string][]byte, err error) {
   101  	keys, err := s.FilterKey(prefixes, notPrefixes)
   102  	if err != nil {
   103  		return
   104  	}
   105  	m = map[string][]byte{}
   106  	for _, k := range keys {
   107  		b, err := os.ReadFile(s.refPath(k))
   108  		if err != nil {
   109  			return nil, err
   110  		}
   111  		m[k] = b
   112  	}
   113  	return
   114  }
   115  
   116  func (s *Store) FilterKey(prefixes, notPrefixes []string) (keys []string, err error) {
   117  	prefix := ""
   118  	if len(prefixes) > 0 {
   119  		prefix = prefixes[0]
   120  	}
   121  	paths := list.New()
   122  	paths.PushBack(prefix)
   123  	for e := paths.Front(); e != nil; e = e.Next() {
   124  		p := e.Value.(string)
   125  		entries, err := os.ReadDir(s.refPath(p))
   126  		if err != nil {
   127  			if _, ok := err.(*os.PathError); ok {
   128  				continue
   129  			}
   130  			return nil, err
   131  		}
   132  		for _, f := range entries {
   133  			if f.IsDir() {
   134  				paths.PushBack(path.Join(p, f.Name()))
   135  			} else {
   136  				keys = append(keys, path.Join(p, f.Name()))
   137  			}
   138  		}
   139  	}
   140  	return
   141  }
   142  
   143  func (s *Store) Rename(oldKey, newKey string) (err error) {
   144  	p := s.refPath(newKey)
   145  	err = s.createParentDir(p)
   146  	if err != nil {
   147  		return
   148  	}
   149  	err = os.Rename(s.refPath(oldKey), p)
   150  	if err != nil {
   151  		return
   152  	}
   153  	p = s.logPath(oldKey)
   154  	if _, err := os.Stat(p); err == nil {
   155  		q := s.logPath(newKey)
   156  		err = s.createParentDir(q)
   157  		if err != nil {
   158  			return err
   159  		}
   160  		return os.Rename(p, q)
   161  	}
   162  	return
   163  }
   164  
   165  func (s *Store) Copy(srcKey, dstKey string) (err error) {
   166  	srcRef, err := os.Open(s.refPath(srcKey))
   167  	if err != nil {
   168  		return
   169  	}
   170  	defer srcRef.Close()
   171  	p := s.refPath(dstKey)
   172  	err = s.createParentDir(p)
   173  	if err != nil {
   174  		return
   175  	}
   176  	dstRef, err := os.Create(p)
   177  	if err != nil {
   178  		return
   179  	}
   180  	defer dstRef.Close()
   181  	_, err = io.Copy(dstRef, srcRef)
   182  	if err != nil {
   183  		return
   184  	}
   185  
   186  	srcLog, err := os.Open(s.logPath(srcKey))
   187  	if err != nil {
   188  		return
   189  	}
   190  	defer srcLog.Close()
   191  	p = s.logPath(dstKey)
   192  	err = s.createParentDir(p)
   193  	if err != nil {
   194  		return
   195  	}
   196  	dstLog, err := os.Create(p)
   197  	if err != nil {
   198  		return
   199  	}
   200  	defer dstLog.Close()
   201  	_, err = io.Copy(dstLog, srcLog)
   202  	return err
   203  }
   204  
   205  func (s *Store) LogReader(key string) (ref.ReflogReader, error) {
   206  	f, err := os.Open(s.logPath(key))
   207  	if err != nil {
   208  		return nil, ref.ErrKeyNotFound
   209  	}
   210  	return NewReflogReader(f)
   211  }
   212  
   213  func (s *Store) NewTransaction(tx *ref.Transaction) (*uuid.UUID, error) {
   214  	return nil, fmt.Errorf("not implemented")
   215  }
   216  
   217  func (s *Store) GetTransaction(id uuid.UUID) (*ref.Transaction, error) {
   218  	return nil, fmt.Errorf("not implemented")
   219  }
   220  
   221  func (s *Store) UpdateTransaction(tx *ref.Transaction) error {
   222  	return fmt.Errorf("not implemented")
   223  }
   224  
   225  func (s *Store) DeleteTransaction(id uuid.UUID) error {
   226  	return fmt.Errorf("not implemented")
   227  }
   228  
   229  func (s *Store) GCTransactions(txTTL time.Duration) (ids []uuid.UUID, err error) {
   230  	return nil, fmt.Errorf("not implemented")
   231  }
   232  
   233  func (s *Store) GetTransactionLogs(txid uuid.UUID) (logs map[string]*ref.Reflog, err error) {
   234  	return nil, fmt.Errorf("not implemented")
   235  }
   236  
   237  func (s *Store) ListTransactions(offset, limit int) (txs []*ref.Transaction, err error) {
   238  	return nil, fmt.Errorf("not implemented")
   239  }