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

     1  // Package fsrepo is a file-system implementation of repo
     2  package fsrepo
     3  
     4  import (
     5  	"context"
     6  	"fmt"
     7  	"os"
     8  	"sync"
     9  
    10  	golog "github.com/ipfs/go-log"
    11  	"github.com/qri-io/qfs/muxfs"
    12  	"github.com/qri-io/qri/dscache"
    13  	"github.com/qri-io/qri/dsref"
    14  	"github.com/qri-io/qri/event"
    15  	"github.com/qri-io/qri/logbook"
    16  	"github.com/qri-io/qri/profile"
    17  	"github.com/qri-io/qri/repo"
    18  	reporef "github.com/qri-io/qri/repo/ref"
    19  )
    20  
    21  var log = golog.Logger("fsrepo")
    22  
    23  // Repo is a filesystem-based implementation of the Repo interface
    24  type Repo struct {
    25  	basepath
    26  
    27  	repo.Refstore
    28  
    29  	bus     event.Bus
    30  	fsys    *muxfs.Mux
    31  	logbook *logbook.Book
    32  	dscache *dscache.Dscache
    33  
    34  	profiles profile.Store
    35  
    36  	doneWg  sync.WaitGroup
    37  	doneCh  chan struct{}
    38  	doneErr error
    39  }
    40  
    41  var _ repo.Repo = (*Repo)(nil)
    42  
    43  // NewRepo creates a new file-based repository
    44  func NewRepo(ctx context.Context, path string, fsys *muxfs.Mux, book *logbook.Book, cache *dscache.Dscache, pro profile.Store, bus event.Bus) (repo.Repo, error) {
    45  	if err := os.MkdirAll(path, os.ModePerm); err != nil {
    46  		log.Error(err)
    47  		return nil, err
    48  	}
    49  	bp := basepath(path)
    50  
    51  	r := &Repo{
    52  		bus:      bus,
    53  		fsys:     fsys,
    54  		basepath: bp,
    55  		logbook:  book,
    56  		dscache:  cache,
    57  
    58  		Refstore: Refstore{basepath: bp, file: FileRefs},
    59  		profiles: pro,
    60  
    61  		doneCh: make(chan struct{}),
    62  	}
    63  
    64  	r.doneWg.Add(1)
    65  	go func() {
    66  		<-r.fsys.Done()
    67  		r.doneErr = r.fsys.DoneErr()
    68  		r.doneWg.Done()
    69  	}()
    70  
    71  	go func() {
    72  		r.doneWg.Wait()
    73  		close(r.doneCh)
    74  	}()
    75  
    76  	if _, err := maybeCreateFlatbufferRefsFile(path); err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	own := pro.Owner(ctx)
    81  	// add our own profile to the store if it doesn't already exist.
    82  	if _, e := r.Profiles().GetProfile(ctx, own.ID); e != nil {
    83  		if err := r.Profiles().PutProfile(ctx, own); err != nil {
    84  			return nil, err
    85  		}
    86  	}
    87  
    88  	return r, nil
    89  }
    90  
    91  // ResolveRef implements the dsref.RefResolver interface
    92  func (r *Repo) ResolveRef(ctx context.Context, ref *dsref.Ref) (string, error) {
    93  	if r == nil {
    94  		return "", dsref.ErrRefNotFound
    95  	}
    96  
    97  	// TODO (b5) - not totally sure why, but memRepo doesn't seem to be wiring up
    98  	// dscache correctly in in tests
    99  	// if r.dscache != nil {
   100  	// 	return r.dscache.ResolveRef(ctx, ref)
   101  	// }
   102  
   103  	if r.logbook == nil {
   104  		return "", fmt.Errorf("cannot resolve local references without logbook")
   105  	}
   106  
   107  	if ref.InitID != "" {
   108  		res, err := r.logbook.Ref(ctx, ref.InitID)
   109  		if err != nil {
   110  			return "", err
   111  		}
   112  
   113  		*ref = res
   114  		return "", nil
   115  	}
   116  
   117  	// Preserve the input ref path, and convert to the old style dataset ref for repo.
   118  	origPath := ref.Path
   119  	datasetRef := reporef.DatasetRef{
   120  		Peername: ref.Username,
   121  		Name:     ref.Name,
   122  	}
   123  
   124  	// Get the reference from the refstore. This has everything but initID
   125  	match, err := r.GetRef(datasetRef)
   126  	if err != nil {
   127  		return "", dsref.ErrRefNotFound
   128  	}
   129  	// Create our resolved reference. If the input ref had a path, reassign that
   130  	*ref = reporef.ConvertToDsref(match)
   131  	if origPath != "" {
   132  		ref.Path = origPath
   133  	}
   134  
   135  	// Get just the initID from logbook
   136  	ref.InitID, err = r.logbook.RefToInitID(*ref)
   137  	return "", err
   138  }
   139  
   140  // Path returns the path to the root of the repo directory
   141  func (r *Repo) Path() string {
   142  	return string(r.basepath)
   143  }
   144  
   145  // Bus accesses the repo's bus
   146  func (r *Repo) Bus() event.Bus {
   147  	return r.bus
   148  }
   149  
   150  // Filesystem returns this repo's Filesystem
   151  func (r *Repo) Filesystem() *muxfs.Mux {
   152  	return r.fsys
   153  }
   154  
   155  // SetFilesystem implements QFSSetter, currently used during lib contstruction
   156  func (r *Repo) SetFilesystem(fs *muxfs.Mux) {
   157  	r.fsys = fs
   158  }
   159  
   160  // Logbook stores operation logs for coordinating state across peers
   161  func (r *Repo) Logbook() *logbook.Book {
   162  	return r.logbook
   163  }
   164  
   165  // Dscache returns a dscache
   166  func (r *Repo) Dscache() *dscache.Dscache {
   167  	return r.dscache
   168  }
   169  
   170  // Profiles returns this repo's Peers implementation
   171  func (r *Repo) Profiles() profile.Store {
   172  	return r.profiles
   173  }
   174  
   175  // Done returns a channel that the repo will send on when the repo is finished
   176  // closing
   177  func (r *Repo) Done() <-chan struct{} {
   178  	return r.doneCh
   179  }
   180  
   181  // DoneErr gives an error that occurred during the shutdown process
   182  func (r *Repo) DoneErr() error {
   183  	return r.doneErr
   184  }
   185  
   186  // Destroy destroys this repository
   187  func (r *Repo) Destroy() error {
   188  	return os.RemoveAll(string(r.basepath))
   189  }