github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/repo/fs/refstore.go (about)

     1  package fsrepo
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"sort"
     9  
    10  	"github.com/qri-io/qri/repo"
    11  	reporef "github.com/qri-io/qri/repo/ref"
    12  )
    13  
    14  // Refstore is a file-based implementation of the Refstore
    15  // interface. It stores names in a json file
    16  type Refstore struct {
    17  	basepath
    18  	file File
    19  }
    20  
    21  // PutRef adds a reference to the store
    22  func (rs Refstore) PutRef(r reporef.DatasetRef) (err error) {
    23  	var refs repo.RefList
    24  
    25  	// remove dataset reference, refstores only store reference details
    26  	r.Dataset = nil
    27  
    28  	if r.ProfileID == "" {
    29  		return repo.ErrPeerIDRequired
    30  	} else if r.Name == "" {
    31  		return repo.ErrNameRequired
    32  	} else if r.Path == "" && r.FSIPath == "" {
    33  		return repo.ErrPathRequired
    34  	} else if r.Peername == "" {
    35  		return repo.ErrPeernameRequired
    36  	}
    37  
    38  	if refs, err = rs.refs(); err != nil {
    39  		return err
    40  	}
    41  
    42  	matched := false
    43  	for i, ref := range refs {
    44  		if ref.Match(r) {
    45  			matched = true
    46  			refs[i] = r
    47  		}
    48  	}
    49  
    50  	if !matched {
    51  		refs = append(refs, r)
    52  	}
    53  
    54  	return rs.save(refs)
    55  }
    56  
    57  // GetRef completes a partially-known reference
    58  func (rs Refstore) GetRef(get reporef.DatasetRef) (reporef.DatasetRef, error) {
    59  	refs, err := rs.refs()
    60  	if err != nil {
    61  		return reporef.DatasetRef{}, err
    62  	}
    63  	for _, ref := range refs {
    64  		if ref.Match(get) {
    65  			return ref, nil
    66  		}
    67  	}
    68  	return reporef.DatasetRef{}, repo.ErrNotFound
    69  }
    70  
    71  // DeleteRef removes a name from the store
    72  func (rs Refstore) DeleteRef(del reporef.DatasetRef) error {
    73  	refs, err := rs.refs()
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	for i, ref := range refs {
    79  		if ref.Match(del) {
    80  			refs = append(refs[:i], refs[i+1:]...)
    81  			break
    82  		}
    83  	}
    84  
    85  	return rs.save(refs)
    86  }
    87  
    88  // References gives a set of dataset references from the store
    89  func (rs Refstore) References(offset, limit int) ([]reporef.DatasetRef, error) {
    90  	refs, err := rs.refs()
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  	res := make(repo.RefList, limit)
    95  	for i, ref := range refs {
    96  		if i < offset {
    97  			continue
    98  		}
    99  		if i-offset == limit {
   100  			return []reporef.DatasetRef(res), nil
   101  		}
   102  		res[i-offset] = ref
   103  	}
   104  	return res[:len(refs)-offset], nil
   105  }
   106  
   107  // RefCount returns the size of the Refstore
   108  func (rs Refstore) RefCount() (int, error) {
   109  	// TODO (b5) - there's no need to unmarshal here
   110  	// could just read the length of the flatbuffer ref vector
   111  	refs, err := rs.refs()
   112  	if err != nil {
   113  		log.Debug(err.Error())
   114  		return 0, err
   115  	}
   116  	return len(refs), nil
   117  }
   118  
   119  func (rs *Refstore) refs() (repo.RefList, error) {
   120  	data, err := ioutil.ReadFile(rs.filepath(rs.file))
   121  	if err != nil {
   122  		if os.IsNotExist(err) {
   123  			// empty is ok
   124  			return repo.RefList{}, nil
   125  		}
   126  		log.Debug(err.Error())
   127  		return nil, fmt.Errorf("error loading references: %s", err.Error())
   128  	}
   129  
   130  	return repo.UnmarshalRefsFlatbuffer(data)
   131  }
   132  
   133  func (rs *Refstore) jsonRefs() (repo.RefList, error) {
   134  	data, err := ioutil.ReadFile(rs.filepath(rs.file))
   135  	if err != nil {
   136  		if os.IsNotExist(err) {
   137  			// empty is ok
   138  			return repo.RefList{}, nil
   139  		}
   140  		log.Debug(err.Error())
   141  		return nil, fmt.Errorf("error loading names: %s", err.Error())
   142  	}
   143  
   144  	refs := repo.RefList{}
   145  	err = json.Unmarshal(data, &refs)
   146  	return refs, err
   147  }
   148  
   149  func (rs *Refstore) save(refs repo.RefList) error {
   150  	sort.Sort(refs)
   151  	path := rs.basepath.filepath(rs.file)
   152  	return ioutil.WriteFile(path, repo.FlatbufferBytes(refs), os.ModePerm)
   153  }