Skip to content

Commit

Permalink
fix(lib): Improve context passing and visibility of internal structs
Browse files Browse the repository at this point in the history
  • Loading branch information
dustmop committed Mar 2, 2021
1 parent afaf06d commit 8f6509b
Show file tree
Hide file tree
Showing 10 changed files with 62 additions and 75 deletions.
2 changes: 1 addition & 1 deletion cmd/checkout.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func (o *CheckoutOptions) Complete(f Factory, args []string) (err error) {

// Run executes the `checkout` command
func (o *CheckoutOptions) Run() (err error) {
ctx := context.Background()
ctx := context.TODO()
inst := o.Instance

if !o.Refs.IsExplicit() {
Expand Down
8 changes: 4 additions & 4 deletions cmd/fsi.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ func NewFSICommand(f Factory, ioStreams ioes.IOStreams) *cobra.Command {
if err := o.Complete(f, args); err != nil {
return err
}
return o.Unlink()
ctx := context.TODO()
return o.Unlink(ctx)
},
}

Expand Down Expand Up @@ -87,7 +88,7 @@ func (o *FSIOptions) Link() (err error) {
return err
}

ctx := context.Background()
ctx := context.TODO()
inst := o.Instance

p := &lib.LinkParams{
Expand All @@ -104,8 +105,7 @@ func (o *FSIOptions) Link() (err error) {
}

// Unlink executes the fsi unlink command
func (o *FSIOptions) Unlink() error {
ctx := context.Background()
func (o *FSIOptions) Unlink(ctx context.Context) error {
inst := o.Instance

for _, ref := range o.Refs.RefList() {
Expand Down
2 changes: 1 addition & 1 deletion cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func (o *InitOptions) Complete(f Factory, args []string) (err error) {

// Run executes the `init` command
func (o *InitOptions) Run() (err error) {
ctx := context.Background()
ctx := context.TODO()
inst := o.Instance

// An empty dir means the current dir
Expand Down
2 changes: 1 addition & 1 deletion cmd/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func (o *RestoreOptions) Complete(f Factory, args []string) (err error) {
func (o *RestoreOptions) Run() (err error) {
printRefSelect(o.ErrOut, o.Refs)

ctx := context.Background()
ctx := context.TODO()
inst := o.Instance

ref := o.Refs.Ref()
Expand Down
2 changes: 1 addition & 1 deletion cmd/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ const ColumnPositionForMtime = 40
func (o *StatusOptions) Run() (err error) {
printRefSelect(o.ErrOut, o.Refs)

ctx := context.Background()
ctx := context.TODO()
inst := o.Instance

params := lib.LinkParams{Dir: o.Refs.Dir()}
Expand Down
2 changes: 1 addition & 1 deletion cmd/whatchanged.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (o *WhatChangedOptions) Complete(f Factory, args []string) (err error) {
func (o *WhatChangedOptions) Run() (err error) {
printRefSelect(o.ErrOut, o.Refs)

ctx := context.Background()
ctx := context.TODO()
inst := o.Instance

params := lib.LinkParams{Refstr: o.Refs.Ref()}
Expand Down
55 changes: 25 additions & 30 deletions lib/dispatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"strings"
)

// Dispatch is system for handling calls to lib. Currently only implemented for FSI methods.
// Dispatch is a system for handling calls to lib. Should only be called by top-level lib methods.
//
// When programs are using qri as a library (such as the `cmd` package), calls to `lib` will
// arrive at dispatch, before being routed to the actual implementation routine. This solves
Expand All @@ -22,13 +22,6 @@ import (
// as the input and output parameters for those methods, and associates a string name for each
// method. Dispatch works by looking up that method name, constructing the necessary input,
// then invoking the actual implementation.

// RegMethodSet represents a set of registered methods
type RegMethodSet struct {
reg map[string]callable
}

// Dispatch looks up the method, and invokes it with the given input
func (inst *Instance) Dispatch(ctx context.Context, method string, param interface{}) (res interface{}, err error) {
if inst == nil {
return nil, fmt.Errorf("instance is nil, cannot dispatch")
Expand Down Expand Up @@ -60,12 +53,12 @@ func (inst *Instance) Dispatch(ctx context.Context, method string, param interfa
// TODO(dustmop): Add user authentication, profile, identity, etc
// TODO(dustmop): Also determine if the method is read-only vs read-write,
// and only execute a single read-write method at a time
// Eventually, the data that lives in Scope should be immutable for its lifetime,
// Eventually, the data that lives in scope should be immutable for its lifetime,
// or use copy-on-write semantics, so that one method running at the same time as
// another cannot modify the out-of-scope data of the other. This will mostly
// involve making copies of the right things
scope := Scope{
ctx: ctx,
scope := scope{
ctx: ctx,
inst: inst,
}

Expand Down Expand Up @@ -110,8 +103,13 @@ func (inst *Instance) NewInputParam(method string) interface{} {
return nil
}

// regMethodSet represents a set of registered methods
type regMethodSet struct {
reg map[string]callable
}

// lookup finds the callable structure with the given method name
func (r *RegMethodSet) lookup(method string) (*callable, bool) {
func (r *regMethodSet) lookup(method string) (*callable, bool) {
if c, ok := r.reg[method]; ok {
return &c, true
}
Expand All @@ -129,7 +127,7 @@ type callable struct {
func (inst *Instance) RegisterMethods() {
reg := make(map[string]callable)
inst.registerOne("fsi", &FSIImpl{}, reg)
inst.regMethods = &RegMethodSet{reg: reg}
inst.regMethods = &regMethodSet{reg: reg}
}

func (inst *Instance) registerOne(ourName string, impl interface{}, reg map[string]callable) {
Expand All @@ -139,51 +137,44 @@ func (inst *Instance) registerOne(ourName string, impl interface{}, reg map[stri
for k := 0; k < num; k++ {
m := implType.Method(k)
lowerName := strings.ToLower(m.Name)
funcName := ourName + "." + lowerName
funcName := fmt.Sprintf("%s.%s", ourName, lowerName)

// Validate the parameters to the method
// should have 3 input parameters: (receiver, scope, input struct)
// should have 2 output parametres: (output value, error)
// TODO(dustmop): allow variadic returns: error only, cursor for pagination
f := m.Type
if f.NumIn() != 3 {
log.Errorf("%s: bad number of inputs: %d", funcName, f.NumIn())
continue
log.Fatalf("%s: bad number of inputs: %d", funcName, f.NumIn())
}
if f.NumOut() != 2 {
log.Errorf("%s: bad number of outputs: %d", funcName, f.NumOut())
continue
log.Fatalf("%s: bad number of outputs: %d", funcName, f.NumOut())
}
// First input must be the receiver
inType := f.In(0)
if inType != implType {
log.Errorf("%s: first input param should be impl, got %v", funcName, inType)
continue
log.Fatalf("%s: first input param should be impl, got %v", funcName, inType)
}
// Second input must be a scope
inType = f.In(1)
if inType.Name() != "Scope" {
log.Errorf("%s: second input param should be scope, got %v", funcName, inType)
continue
if inType.Name() != "scope" {
log.Fatalf("%s: second input param should be scope, got %v", funcName, inType)
}
// Third input is a pointer to the input struct
inType = f.In(2)
if inType.Kind() != reflect.Ptr {
log.Errorf("%s: third input param must be a struct pointer, got %v", funcName, inType)
continue
log.Fatalf("%s: third input param must be a struct pointer, got %v", funcName, inType)
}
inType = inType.Elem()
if inType.Kind() != reflect.Struct {
log.Errorf("%s: third input param must be a struct pointer, got %v", funcName, inType)
continue
log.Fatalf("%s: third input param must be a struct pointer, got %v", funcName, inType)
}
// First output is anything
outType := f.Out(0)
// Second output must be an error
outErrType := f.Out(1)
if outErrType.Name() != "error" {
log.Errorf("%s: second output param should be error, got %v", funcName, inType)
continue
log.Fatalf("%s: second output param should be error, got %v", funcName, outErrType)
}

// Save the method to the registration table
Expand All @@ -193,12 +184,16 @@ func (inst *Instance) registerOne(ourName string, impl interface{}, reg map[stri
InType: inType,
OutType: outType,
}
log.Infof("%d: registered %s(*%s) %v", k, funcName, inType, outType)
log.Debugf("%d: registered %s(*%s) %v", k, funcName, inType, outType)
}
}

// methodEndpoint returns a method name and returns the API endpoint for it
func methodEndpoint(method string) APIEndpoint {
// TODO(dustmop): This is here temporarily. /fsi/write/ works differently than
// other methods; their http API endpoints are only their method name, for
// exmaple /status/. This should be replaced with an explicit mapping from
// method names to endpoints.
if method == "fsi.write" {
return "/fsi/write/"
}
Expand Down
20 changes: 10 additions & 10 deletions lib/fsi.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func (m *FSIMethods) EnsureRef(ctx context.Context, p *LinkParams) (*dsref.Versi
type FSIImpl struct{}

// CreateLink creates a connection between a working drirectory and a dataset history
func (*FSIImpl) CreateLink(scope Scope, p *LinkParams) (*dsref.VersionInfo, error) {
func (*FSIImpl) CreateLink(scope scope, p *LinkParams) (*dsref.VersionInfo, error) {
ctx := scope.Context()

ref, _, err := scope.ParseAndResolveRef(ctx, p.Refstr, "local")
Expand All @@ -189,7 +189,7 @@ func (*FSIImpl) CreateLink(scope Scope, p *LinkParams) (*dsref.VersionInfo, erro
// Unlink removes the connection between a working directory and a dataset. If given only a
// directory, will remove the link file from that directory. If given only a reference,
// will remove the fsi path from that reference, and remove the link file from that fsi path
func (*FSIImpl) Unlink(scope Scope, p *LinkParams) (string, error) {
func (*FSIImpl) Unlink(scope scope, p *LinkParams) (string, error) {
ctx := scope.Context()

if p.Dir != "" && p.Refstr != "" {
Expand Down Expand Up @@ -225,7 +225,7 @@ func (*FSIImpl) Unlink(scope Scope, p *LinkParams) (string, error) {

// Status checks for any modifications or errors in a linked directory against its previous
// version in the repo. Must only be called if FSI is enabled for this dataset.
func (*FSIImpl) Status(scope Scope, p *LinkParams) ([]StatusItem, error) {
func (*FSIImpl) Status(scope scope, p *LinkParams) ([]StatusItem, error) {
ctx := scope.Context()

if p.Dir == "" && p.Refstr == "" {
Expand All @@ -252,7 +252,7 @@ func (*FSIImpl) Status(scope Scope, p *LinkParams) ([]StatusItem, error) {

// WhatChanged gets changes that happened at a particular version in the history of the given
// dataset reference.
func (*FSIImpl) WhatChanged(scope Scope, p *LinkParams) ([]StatusItem, error) {
func (*FSIImpl) WhatChanged(scope scope, p *LinkParams) ([]StatusItem, error) {
ctx := scope.Context()

ref, _, err := scope.ParseAndResolveRef(ctx, p.Refstr, "local")
Expand All @@ -265,7 +265,7 @@ func (*FSIImpl) WhatChanged(scope Scope, p *LinkParams) ([]StatusItem, error) {

// Checkout method writes a dataset to a directory as individual files.
// TODO(dustmop): Returned string is not used, remove it once dispatch is compatible
func (*FSIImpl) Checkout(scope Scope, p *LinkParams) (string, error) {
func (*FSIImpl) Checkout(scope scope, p *LinkParams) (string, error) {
ctx := scope.Context()

// Require a non-empty, absolute path for the checkout
Expand Down Expand Up @@ -330,7 +330,7 @@ func (*FSIImpl) Checkout(scope Scope, p *LinkParams) (string, error) {
}

// Write mutates a linked dataset on the filesystem
func (*FSIImpl) Write(scope Scope, p *FSIWriteParams) ([]StatusItem, error) {
func (*FSIImpl) Write(scope scope, p *FSIWriteParams) ([]StatusItem, error) {
ctx := scope.Context()

if p.Ds == nil {
Expand Down Expand Up @@ -363,7 +363,7 @@ func (*FSIImpl) Write(scope Scope, p *FSIWriteParams) ([]StatusItem, error) {

// Restore method restores a component or all of the component files of a dataset from the repo
// TODO(dustmop): Returned string is not used, remove it once dispatch is compatible
func (*FSIImpl) Restore(scope Scope, p *RestoreParams) (string, error) {
func (*FSIImpl) Restore(scope scope, p *RestoreParams) (string, error) {
ctx := scope.Context()

ref, _, err := scope.ParseAndResolveRef(ctx, p.Refstr, "local")
Expand Down Expand Up @@ -428,7 +428,7 @@ func (*FSIImpl) Restore(scope Scope, p *RestoreParams) (string, error) {
}

// Init creates a new dataset in a working directory
func (*FSIImpl) Init(scope Scope, p *InitDatasetParams) (string, error) {
func (*FSIImpl) Init(scope scope, p *InitDatasetParams) (string, error) {
ctx := scope.Context()

if p.UseDscache {
Expand All @@ -440,14 +440,14 @@ func (*FSIImpl) Init(scope Scope, p *InitDatasetParams) (string, error) {
}

// CanInitDatasetWorkDir returns nil if the directory can init a dataset, or an error if not
func (*FSIImpl) CanInitDatasetWorkDir(scope Scope, p *InitDatasetParams) (bool, error) {
func (*FSIImpl) CanInitDatasetWorkDir(scope scope, p *InitDatasetParams) (bool, error) {
// TODO(dustmop): Change dispatch so that implementations can only return 1 value
// if that value is an error
return true, scope.FSISubsystem().CanInitDatasetWorkDir(p.TargetDir, p.BodyPath)
}

// EnsureRef will modify the directory path in the repo for the given reference
func (*FSIImpl) EnsureRef(scope Scope, p *LinkParams) (*dsref.VersionInfo, error) {
func (*FSIImpl) EnsureRef(scope scope, p *LinkParams) (*dsref.VersionInfo, error) {
ctx := scope.Context()

ref, err := dsref.Parse(p.Refstr)
Expand Down
2 changes: 1 addition & 1 deletion lib/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -701,7 +701,7 @@ type Instance struct {
repoPath string
cfg *config.Config

regMethods *RegMethodSet
regMethods *regMethodSet

streams ioes.IOStreams
repo repo.Repo
Expand Down

0 comments on commit 8f6509b

Please sign in to comment.