package operation

import "context"

type key string

var (
	viewKey = key("operation-view")
)

// A View provides limited access to an Op. It is generally attached to a
// Context value, so may be available to functions that are incidentally
// involved in processing the operation.
//
// Since View does not allow access to "dangerous" methods on Op such as
// "End", it can be safely used wherever Context values are appropriate.
type View struct {
	op *Op

	// TODO: allow dependent calls to use the "start" time for their metrics

	// TODO: figure out a real logging API, use this value to pass annotations
}

// ApplyToContext returns a Context with the Op's request-related data
// attached. If an OpMonitor has provided an "ApplyToContext" callback, it
// will be called to attach the data.
func (view *View) ApplyToContext(ctx context.Context) context.Context {
	if view == nil {
		return ctx
	}
	return view.op.ApplyToContext(ctx)
}

// ViewFromContext returns the View value attached to ctx, or nil if none is
// attached.
func ViewFromContext(ctx context.Context) *View {
	view, _ := ctx.Value(viewKey).(*View)
	return view
}

// NewViewContext returns a Context with the provided View attached.
func NewViewContext(ctx context.Context, view *View) context.Context {
	return context.WithValue(ctx, viewKey, view)
}

// opView allows for a compile-time check that Op and View have matching
// methods.
type opView interface {
	ApplyToContext(ctx context.Context) context.Context
}

var (
	_ opView = (*Op)(nil)
	_ opView = (*View)(nil)
)
