github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/lib/load.go (about) 1 package lib 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 8 "github.com/qri-io/dataset" 9 "github.com/qri-io/qri/base" 10 "github.com/qri-io/qri/base/dsfs" 11 "github.com/qri-io/qri/dsref" 12 qerr "github.com/qri-io/qri/errors" 13 ) 14 15 type datasetLoader struct { 16 inst *Instance 17 userOwner string 18 source string 19 } 20 21 func newDatasetLoader(inst *Instance, userOwner, source string) dsref.Loader { 22 return &datasetLoader{ 23 inst: inst, 24 userOwner: userOwner, 25 source: source, 26 } 27 } 28 29 // LoadDataset loads a dataset by resolving where it is available according to 30 // the source being used, and loading it from there 31 func (d *datasetLoader) LoadDataset(ctx context.Context, refstr string) (*dataset.Dataset, error) { 32 if d == nil { 33 return nil, fmt.Errorf("no datasetLoader") 34 } 35 if d.inst == nil { 36 return nil, fmt.Errorf("no instance") 37 } 38 39 ref, err := dsref.Parse(refstr) 40 if err != nil { 41 return nil, fmt.Errorf("%q is not a valid dataset reference: %w", refstr, err) 42 } 43 44 if ref.Username == "me" { 45 if d.userOwner == "" { 46 msg := fmt.Sprintf(`Can't use the "me" keyword to refer to a dataset in this context. 47 Replace "me" with your username for the reference: 48 %s`, refstr) 49 return nil, qerr.New(fmt.Errorf("invalid contextual reference"), msg) 50 } 51 ref.Username = d.userOwner 52 } 53 54 resolver, err := d.inst.resolverForSource(d.source) 55 if err != nil { 56 return nil, err 57 } 58 59 // Resolve the reference 60 location, err := resolver.ResolveRef(ctx, &ref) 61 if err != nil { 62 if errors.Is(err, dsref.ErrRefNotFound) { 63 return nil, qerr.New(err, fmt.Sprintf("reference %q not found", refstr)) 64 } 65 return nil, err 66 } 67 68 if ref.Path == "" { 69 err = qerr.New(dsref.ErrNoHistory, fmt.Sprintf("can't load dataset %q, it has no saved versions", ref.Human())) 70 return nil, err 71 } 72 73 return d.loadRefFromLocation(ctx, ref, location) 74 } 75 76 // LoadDataset fetches, dereferences and opens a dataset from a reference 77 // implements the dsfs.Loader interface 78 // this function expects the passed in reference is fully resolved 79 func (d *datasetLoader) loadRefFromLocation(ctx context.Context, ref dsref.Ref, location string) (*dataset.Dataset, error) { 80 if location == "" { 81 return d.loadLocalDataset(ctx, ref) 82 } 83 84 msg := fmt.Sprintf("pulling %s from %s ...\n", ref.Human(), location) 85 if d.inst.streams.Out != nil { 86 d.inst.streams.Out.Write([]byte(msg)) 87 } 88 89 // TODO (b5) - it'd be nice to use the returned dataset here, skipping the 90 // loadLocalDataset call entirely. For that to work dsfs.LoadDataset & 91 // inst.loadLocalDataset would have to behave in exactly the same way, and 92 // currently they don't 93 if _, err := d.inst.remoteClient.PullDataset(ctx, &ref, location); err != nil { 94 return nil, err 95 } 96 97 return d.loadLocalDataset(ctx, ref) 98 } 99 100 func (d *datasetLoader) loadLocalDataset(ctx context.Context, ref dsref.Ref) (*dataset.Dataset, error) { 101 // Load from dsfs 102 ds, err := dsfs.LoadDataset(ctx, d.inst.qfs, ref.Path) 103 if err != nil { 104 return nil, err 105 } 106 // Set transient info on the returned dataset 107 ds.Name = ref.Name 108 ds.Peername = ref.Username 109 ds.ID = ref.InitID 110 111 if err = base.OpenDataset(ctx, d.inst.repo.Filesystem(), ds); err != nil { 112 log.Debugf("Get dataset, base.OpenDataset failed, error: %s", err) 113 return nil, err 114 } 115 116 return ds, nil 117 }