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 }