// Code generated by protoc-gen-go_temporal. DO NOT EDIT.
// versions:
//
//	protoc-gen-go_temporal dev (latest)
//	protoc (unknown)
//
// source: example/mutex/v1/mutex.proto
package mutexv1xns

import (
	"context"
	"errors"
	"fmt"
	v1 "github.com/cludden/protoc-gen-go-temporal/gen/example/mutex/v1"
	temporalv1 "github.com/cludden/protoc-gen-go-temporal/gen/temporal/v1"
	xnsv1 "github.com/cludden/protoc-gen-go-temporal/gen/temporal/xns/v1"
	convert "github.com/cludden/protoc-gen-go-temporal/pkg/convert"
	expression "github.com/cludden/protoc-gen-go-temporal/pkg/expression"
	xns "github.com/cludden/protoc-gen-go-temporal/pkg/xns"
	uuid "github.com/google/uuid"
	enumsv1 "go.temporal.io/api/enums/v1"
	activity "go.temporal.io/sdk/activity"
	client "go.temporal.io/sdk/client"
	temporal "go.temporal.io/sdk/temporal"
	worker "go.temporal.io/sdk/worker"
	workflow "go.temporal.io/sdk/workflow"
	anypb "google.golang.org/protobuf/types/known/anypb"
	durationpb "google.golang.org/protobuf/types/known/durationpb"
	"time"
)

// ExampleOptions is used to configure example.mutex.v1.Example xns activity registration
type ExampleOptions struct {
	// errorConverter is used to customize error
	errorConverter func(error) error
	// filter is used to filter xns activity registrations. It receives as
	// input the original activity name, and should return one of the following:
	// 1. the original activity name, for no changes
	// 2. a modified activity name, to override the original activity name
	// 3. an empty string, to skip registration
	filter func(string) string
}

// NewExampleOptions initializes a new ExampleOptions value
func NewExampleOptions() *ExampleOptions {
	return &ExampleOptions{}
}

// WithErrorConverter overrides the default error converter applied to xns activity errors
func (opts *ExampleOptions) WithErrorConverter(errorConverter func(error) error) *ExampleOptions {
	opts.errorConverter = errorConverter
	return opts
}

// Filter is used to filter registered xns activities or customize their name
func (opts *ExampleOptions) WithFilter(filter func(string) string) *ExampleOptions {
	opts.filter = filter
	return opts
}

// convertError is applied to all xns activity errors
func (opts *ExampleOptions) convertError(err error) error {
	if err == nil {
		return nil
	}
	if opts != nil && opts.errorConverter != nil {
		return opts.errorConverter(err)
	}
	return xns.ErrorToApplicationError(err)
}

// filterActivity is used to filter xns activity registrations
func (opts *ExampleOptions) filterActivity(name string) string {
	if opts == nil || opts.filter == nil {
		return name
	}
	return opts.filter(name)
}

// exampleOptions is a reference to the ExampleOptions initialized at registration
var exampleOptions *ExampleOptions

// RegisterExampleActivities registers example.mutex.v1.Example cross-namespace activities
func RegisterExampleActivities(r worker.ActivityRegistry, c v1.ExampleClient, options ...*ExampleOptions) {
	if exampleOptions == nil && len(options) > 0 && options[0] != nil {
		exampleOptions = options[0]
	}
	a := &exampleActivities{c}
	if name := exampleOptions.filterActivity("example.mutex.v1.Example.CancelWorkflow"); name != "" {
		r.RegisterActivityWithOptions(a.CancelWorkflow, activity.RegisterOptions{Name: name})
	}
	if name := exampleOptions.filterActivity(v1.MutexWorkflowName); name != "" {
		r.RegisterActivityWithOptions(a.Mutex, activity.RegisterOptions{Name: name})
	}
	if name := exampleOptions.filterActivity("example.mutex.v1.Example.GetMutex"); name != "" {
		r.RegisterActivityWithOptions(a.GetMutex, activity.RegisterOptions{Name: name})
	}
	if name := exampleOptions.filterActivity("example.mutex.v1.Example.MutexWithAcquireLock"); name != "" {
		r.RegisterActivityWithOptions(a.MutexWithAcquireLock, activity.RegisterOptions{Name: name})
	}
	if name := exampleOptions.filterActivity(v1.SampleWorkflowWithMutexWorkflowName); name != "" {
		r.RegisterActivityWithOptions(a.SampleWorkflowWithMutex, activity.RegisterOptions{Name: name})
	}
	if name := exampleOptions.filterActivity("example.mutex.v1.Example.GetSampleWorkflowWithMutex"); name != "" {
		r.RegisterActivityWithOptions(a.GetSampleWorkflowWithMutex, activity.RegisterOptions{Name: name})
	}
	if name := exampleOptions.filterActivity(v1.ReleaseLockSignalName); name != "" {
		r.RegisterActivityWithOptions(a.ReleaseLock, activity.RegisterOptions{Name: name})
	}
	if name := exampleOptions.filterActivity(v1.AcquireLockUpdateName); name != "" {
		r.RegisterActivityWithOptions(a.AcquireLock, activity.RegisterOptions{Name: name})
	}
}

// MutexWorkflowOptions are used to configure a(n) mutex.v1.Mutex workflow execution
type MutexWorkflowOptions struct {
	ActivityOptions      *workflow.ActivityOptions
	Detached             bool
	HeartbeatInterval    time.Duration
	HeartbeatTimeout     time.Duration
	ParentClosePolicy    enumsv1.ParentClosePolicy
	StartWorkflowOptions *client.StartWorkflowOptions
}

// NewMutexWorkflowOptions initializes a new MutexWorkflowOptions value
func NewMutexWorkflowOptions() *MutexWorkflowOptions {
	return &MutexWorkflowOptions{}
}

// Build initializes the activity context and input
func (opts *MutexWorkflowOptions) Build(ctx workflow.Context, input *v1.MutexInput) (workflow.Context, *xnsv1.WorkflowRequest, error) {
	// initialize start workflow options
	swo := client.StartWorkflowOptions{}
	if opts.StartWorkflowOptions != nil {
		swo = *opts.StartWorkflowOptions
	}

	// initialize workflow id if not set
	if swo.ID == "" {
		if err := workflow.SideEffect(ctx, func(ctx workflow.Context) any {
			id, err := expression.EvalExpression(v1.MutexIdexpression, input.ProtoReflect())
			if err != nil {
				workflow.GetLogger(ctx).Error("error evaluating id expression for \"example.mutex.v1.Example.Mutex\" workflow", "error", err)
				return nil
			}
			return id
		}).Get(&swo.ID); err != nil {
			return nil, nil, err
		}
	}
	if swo.ID == "" {
		if err := workflow.SideEffect(ctx, func(ctx workflow.Context) any {
			id, err := uuid.NewRandom()
			if err != nil {
				workflow.GetLogger(ctx).Error("error generating workflow id", "error", err)
				return nil
			}
			return id
		}).Get(&swo.ID); err != nil {
			return nil, nil, err
		}
	}
	if swo.ID == "" {
		return nil, nil, temporal.NewNonRetryableApplicationError("workflow id is required", "InvalidArgument", nil)
	}

	// marshal workflow request protobuf message
	inputpb, err := anypb.New(input)
	if err != nil {
		return ctx, nil, fmt.Errorf("error marshalling workflow request: %w", err)
	}

	// marshal start workflow options protobuf message
	swopb, err := xns.MarshalStartWorkflowOptions(swo)
	if err != nil {
		return ctx, nil, fmt.Errorf("error marshalling start workflow options: %w", err)
	}

	// marshal parent close policy protobuf message
	var parentClosePolicy temporalv1.ParentClosePolicy
	switch opts.ParentClosePolicy {
	case enumsv1.PARENT_CLOSE_POLICY_ABANDON:
		parentClosePolicy = temporalv1.ParentClosePolicy_PARENT_CLOSE_POLICY_ABANDON
	case enumsv1.PARENT_CLOSE_POLICY_REQUEST_CANCEL:
		parentClosePolicy = temporalv1.ParentClosePolicy_PARENT_CLOSE_POLICY_REQUEST_CANCEL
	case enumsv1.PARENT_CLOSE_POLICY_TERMINATE:
		parentClosePolicy = temporalv1.ParentClosePolicy_PARENT_CLOSE_POLICY_TERMINATE
	}

	// initialize xns activity options
	ao := workflow.ActivityOptions{}
	if opts.ActivityOptions != nil {
		ao = *opts.ActivityOptions
	}

	if ao.HeartbeatTimeout == 0 {
		ao.HeartbeatTimeout = time.Second * 60
	}

	if ao.RetryPolicy == nil {
		ao.RetryPolicy = &temporal.RetryPolicy{
			BackoffCoefficient: 2.0,
			InitialInterval:    1000000000,
			MaximumAttempts:    int32(5),
			MaximumInterval:    60000000000,
		}
	}
	if ao.StartToCloseTimeout == 0 && ao.ScheduleToCloseTimeout == 0 {
		ao.ScheduleToCloseTimeout = time.Hour * 24
	}

	// WaitForCancellation must be set otherwise the underlying workflow is not guaranteed to be canceled
	ao.WaitForCancellation = true

	// configure heartbeat interval
	if opts.HeartbeatInterval == 0 {
		opts.HeartbeatInterval = ao.HeartbeatTimeout / 2
	}

	ctx = workflow.WithActivityOptions(ctx, ao)

	return ctx, &xnsv1.WorkflowRequest{
		Detached:             opts.Detached,
		HeartbeatInterval:    durationpb.New(opts.HeartbeatInterval),
		ParentClosePolicy:    parentClosePolicy,
		Request:              inputpb,
		StartWorkflowOptions: swopb,
	}, nil
}

// WithActivityOptions can be used to customize the activity options
func (opts *MutexWorkflowOptions) WithActivityOptions(ao workflow.ActivityOptions) *MutexWorkflowOptions {
	opts.ActivityOptions = &ao
	return opts
}

// WithDetached can be used to start a workflow execution and exit immediately
func (opts *MutexWorkflowOptions) WithDetached(d bool) *MutexWorkflowOptions {
	opts.Detached = d
	return opts
}

// WithHeartbeatInterval can be used to customize the activity heartbeat interval
func (opts *MutexWorkflowOptions) WithHeartbeatInterval(d time.Duration) *MutexWorkflowOptions {
	opts.HeartbeatInterval = d
	return opts
}

// WithHeartbeatTimeout can be used to customize the activity heartbeat timeout
func (opts *MutexWorkflowOptions) WithHeartbeatTimeout(d time.Duration) *MutexWorkflowOptions {
	opts.HeartbeatTimeout = d
	return opts
}

// WithParentClosePolicy can be used to customize the cancellation propagation behavior
func (opts *MutexWorkflowOptions) WithParentClosePolicy(policy enumsv1.ParentClosePolicy) *MutexWorkflowOptions {
	opts.ParentClosePolicy = policy
	return opts
}

// WithStartWorkflowOptions can be used to customize the start workflow options
func (opts *MutexWorkflowOptions) WithStartWorkflow(swo client.StartWorkflowOptions) *MutexWorkflowOptions {
	opts.StartWorkflowOptions = &swo
	return opts
}

// MutexRun provides a handle to a mutex.v1.Mutex workflow execution
type MutexRun interface {
	// Cancel cancels the workflow
	Cancel(workflow.Context) error

	// Future returns the inner workflow.Future
	Future() workflow.Future

	// Get returns the inner workflow.Future
	Get(workflow.Context) error

	// ID returns the workflow id
	ID() string

	// ReleaseLock releases a lock on a resource identified by `lease_id`.
	ReleaseLock(workflow.Context, *v1.ReleaseLockInput, ...*ReleaseLockSignalOptions) error

	// ReleaseLock releases a lock on a resource identified by `lease_id`.
	ReleaseLockAsync(workflow.Context, *v1.ReleaseLockInput, ...*ReleaseLockSignalOptions) (ReleaseLockSignalHandle, error)

	// AcquireLock requests a lock on a resource identified by `resource_id`
	// and blocks until the lock is acquired, returning a `lease_id` that
	// can be used to release the lock.
	AcquireLock(workflow.Context, *v1.AcquireLockInput, ...*AcquireLockUpdateOptions) (*v1.AcquireLockOutput, error)

	// AcquireLock requests a lock on a resource identified by `resource_id`
	// and blocks until the lock is acquired, returning a `lease_id` that
	// can be used to release the lock.
	AcquireLockAsync(workflow.Context, *v1.AcquireLockInput, ...*AcquireLockUpdateOptions) (AcquireLockHandle, error)
}

// mutexRun provides a(n) MutexRun implementation
type mutexRun struct {
	cancel            func()
	ctx               workflow.Context
	future            workflow.Future
	id                string
	heartbeatInterval time.Duration
	parentClosePolicy enumsv1.ParentClosePolicy
}

// Cancel the underlying workflow execution
func (r *mutexRun) Cancel(ctx workflow.Context) error {
	if r.cancel != nil {
		r.cancel()
		if err := r.Get(ctx); err != nil && !errors.Is(err, workflow.ErrCanceled) {
			return err
		}
		return nil
	}
	return CancelExampleWorkflow(ctx, r.id, "")
}

// Future returns the underlying activity future
func (r *mutexRun) Future() workflow.Future {
	if r.future == nil {
		rr := GetMutexAsync(r.ctx, r.id, "").(*mutexRun)
		r.future = rr.future
		r.cancel = rr.cancel
	}
	return r.future
}

// Get blocks on activity completion and returns the underlying workflow result
func (r *mutexRun) Get(ctx workflow.Context) error {
	ctx, cancel := workflow.WithCancel(ctx)
	if r.future == nil {
		rr := GetMutexAsync(ctx, r.id, "", NewGetMutexOptions().WithParentClosePolicy(r.parentClosePolicy).WithHeartbeatInterval(r.heartbeatInterval)).(*mutexRun)
		r.future = rr.future
		r.cancel = cancel
	}
	if err := r.future.Get(ctx, nil); err != nil {
		return err
	}
	return nil
}

// ID returns the underlying workflow id
func (r *mutexRun) ID() string {
	return r.id
}

// ReleaseLock releases a lock on a resource identified by `lease_id`.
func (r *mutexRun) ReleaseLock(ctx workflow.Context, req *v1.ReleaseLockInput, opts ...*ReleaseLockSignalOptions) error {
	return ReleaseLock(ctx, r.ID(), "", req, opts...)
}

// ReleaseLock releases a lock on a resource identified by `lease_id`.
func (r *mutexRun) ReleaseLockAsync(ctx workflow.Context, req *v1.ReleaseLockInput, opts ...*ReleaseLockSignalOptions) (ReleaseLockSignalHandle, error) {
	return ReleaseLockAsync(ctx, r.ID(), "", req, opts...)
}

// AcquireLock requests a lock on a resource identified by `resource_id`
// and blocks until the lock is acquired, returning a `lease_id` that
// can be used to release the lock.
func (r *mutexRun) AcquireLock(ctx workflow.Context, req *v1.AcquireLockInput, opts ...*AcquireLockUpdateOptions) (*v1.AcquireLockOutput, error) {
	return AcquireLock(ctx, r.ID(), "", req, opts...)
}

// AcquireLock requests a lock on a resource identified by `resource_id`
// and blocks until the lock is acquired, returning a `lease_id` that
// can be used to release the lock.
func (r *mutexRun) AcquireLockAsync(ctx workflow.Context, req *v1.AcquireLockInput, opts ...*AcquireLockUpdateOptions) (AcquireLockHandle, error) {
	return AcquireLockAsync(ctx, r.ID(), "", req, opts...)
}

// Mutex is a workflow that manages concurrent access to a resource
// identified by `resource_id`.
func Mutex(ctx workflow.Context, req *v1.MutexInput, opts ...*MutexWorkflowOptions) error {
	run, err := MutexAsync(ctx, req, opts...)
	if err != nil {
		return err
	}
	return run.Get(ctx)
}

// Mutex is a workflow that manages concurrent access to a resource
// identified by `resource_id`.
func MutexAsync(ctx workflow.Context, input *v1.MutexInput, opts ...*MutexWorkflowOptions) (MutexRun, error) {
	activityName := exampleOptions.filterActivity(v1.MutexWorkflowName)
	if activityName == "" {
		return nil, temporal.NewNonRetryableApplicationError(
			fmt.Sprintf("no activity registered for %s", v1.MutexWorkflowName),
			"Unimplemented",
			nil,
		)
	}

	var opt *MutexWorkflowOptions
	if len(opts) > 0 && opts[0] != nil {
		opt = opts[0]
	} else {
		opt = NewMutexWorkflowOptions()
	}
	ctx, req, err := opt.Build(ctx, input)
	if err != nil {
		return nil, exampleOptions.convertError(err)
	}
	ctx, cancel := workflow.WithCancel(ctx)
	return &mutexRun{
		cancel: cancel,
		future: workflow.ExecuteActivity(ctx, activityName, req),
		id:     req.GetStartWorkflowOptions().GetId(),
	}, nil
}

// GetMutex returns a(n) mutex.v1.Mutex workflow execution
func GetMutex(ctx workflow.Context, workflowID string, runID string, options ...*GetMutexOptions) (err error) {
	err = GetMutexAsync(ctx, workflowID, runID, options...).Get(ctx)
	if err != nil {
		return err
	}
	return nil
}

// GetMutexAsync returns a handle to a(n) mutex.v1.Mutex workflow execution
func GetMutexAsync(ctx workflow.Context, workflowID string, runID string, options ...*GetMutexOptions) MutexRun {
	activityName := exampleOptions.filterActivity("example.mutex.v1.Example.GetMutex")
	if activityName == "" {
		f, set := workflow.NewFuture(ctx)
		set.SetError(temporal.NewNonRetryableApplicationError(fmt.Sprintf("no activity registered for %s", activityName), "Unimplemented", nil))
		return &mutexRun{
			future: f,
			id:     workflowID,
		}
	}
	var opt *GetMutexOptions
	if len(options) > 0 && options[0] != nil {
		opt = options[0]
	} else {
		opt = NewGetMutexOptions()
	}
	ctx, req, err := opt.Build(ctx, workflowID, runID)
	if err != nil {
		f, set := workflow.NewFuture(ctx)
		set.SetError(exampleOptions.convertError(temporal.NewNonRetryableApplicationError(fmt.Sprintf("no activity registered for %s", activityName), "Unimplemented", nil)))
		return &mutexRun{
			future: f,
			id:     workflowID,
		}
	}
	ctx, cancel := workflow.WithCancel(ctx)
	return &mutexRun{
		cancel: cancel,
		future: workflow.ExecuteActivity(ctx, activityName, req),
		id:     workflowID,
	}
}

// GetMutexOptions are used to configure a(n) mutex.v1.Mutex workflow execution getter activity
type GetMutexOptions struct {
	activityOptions   *workflow.ActivityOptions
	heartbeatInterval time.Duration
	parentClosePolicy enumsv1.ParentClosePolicy
}

// NewGetMutexOptions initializes a new GetMutexOptions value
func NewGetMutexOptions() *GetMutexOptions {
	return &GetMutexOptions{}
}

// Build initializes the activity context and input
func (opt *GetMutexOptions) Build(ctx workflow.Context, workflowID string, runID string) (workflow.Context, *xnsv1.GetWorkflowRequest, error) {
	if opt.heartbeatInterval == 0 {
		opt.heartbeatInterval = 30000000000 // 30 seconds
	}

	// configure activity options
	var ao workflow.ActivityOptions
	if opt.activityOptions != nil {
		ao = *opt.activityOptions
	} else {
		ao = workflow.ActivityOptions{}
	}
	if ao.HeartbeatTimeout == 0 {
		ao.HeartbeatTimeout = 60000000000 // 1 minute
	}
	// WaitForCancellation must be set otherwise the underlying workflow is not guaranteed to be canceled
	ao.WaitForCancellation = true

	if ao.StartToCloseTimeout == 0 && ao.ScheduleToCloseTimeout == 0 {
		ao.ScheduleToCloseTimeout = 86400000000000 // 1 day
	}
	ctx = workflow.WithActivityOptions(ctx, ao)

	return ctx, &xnsv1.GetWorkflowRequest{
		HeartbeatInterval: durationpb.New(opt.heartbeatInterval),
		ParentClosePolicy: opt.parentClosePolicy,
		RunId:             runID,
		WorkflowId:        workflowID,
	}, nil
}

// WithActivityOptions can be used to customize the activity options
func (o *GetMutexOptions) WithActivityOptions(ao workflow.ActivityOptions) *GetMutexOptions {
	o.activityOptions = &ao
	return o
}

// WithHeartbeatInterval can be used to customize the activity heartbeat interval
func (o *GetMutexOptions) WithHeartbeatInterval(d time.Duration) *GetMutexOptions {
	o.heartbeatInterval = d
	return o
}

// WithParentClosePolicy can be used to customize the cancellation propagation behavior
func (o *GetMutexOptions) WithParentClosePolicy(policy enumsv1.ParentClosePolicy) *GetMutexOptions {
	o.parentClosePolicy = policy
	return o
}

// MutexWithAcquireLockOptions are used to configure a(n) mutex.v1.AcquireLock update for a(n) mutex.v1.Mutex workflow
type MutexWithAcquireLockOptions struct {
	activityOptions   *workflow.ActivityOptions
	heartbeatInterval time.Duration
	updateOptions     *AcquireLockUpdateOptions
	parentClosePolicy enumsv1.ParentClosePolicy
	workflowOptions   *MutexWorkflowOptions
}

// NewMutexWithAcquireLockOptions initializes a new MutexWithAcquireLockOptions value
func NewMutexWithAcquireLockOptions() *MutexWithAcquireLockOptions {
	return &MutexWithAcquireLockOptions{}
}

// Build builds the activity context and input for an update with start workflow activity
func (o *MutexWithAcquireLockOptions) Build(ctx workflow.Context, input *v1.MutexInput, update *v1.AcquireLockInput) (workflow.Context, *xnsv1.UpdateWithStartRequest, error) {
	wo := o.workflowOptions
	if wo == nil {
		wo = NewMutexWorkflowOptions()
	}

	_, swreq, err := wo.Build(ctx, input)
	if err != nil {
		return nil, nil, fmt.Errorf("error building start workflow options: %w", err)
	}

	uo := o.updateOptions
	if uo == nil {
		uo = NewAcquireLockUpdateOptions()
	}

	ctx, ureq, err := uo.Build(ctx, swreq.GetStartWorkflowOptions().GetId(), "", update)
	if err != nil {
		return nil, nil, fmt.Errorf("error building update options: %w", err)
	}

	var ao workflow.ActivityOptions
	if o.activityOptions != nil {
		ao = *o.activityOptions
	}

	if ao.HeartbeatTimeout == 0 {
		ao.HeartbeatTimeout = 60000000000 // 1 minute
	}

	if ao.StartToCloseTimeout == 0 && ao.ScheduleToCloseTimeout == 0 {
		ao.ScheduleToCloseTimeout = time.Hour * 24
	}

	// WaitForCancellation must be set otherwise the underlying workflow is not guaranteed to be canceled
	ao.WaitForCancellation = true

	// configure heartbeat interval
	if o.heartbeatInterval == 0 {
		o.heartbeatInterval = 30000000000 // 30 seconds
	}

	ctx = workflow.WithActivityOptions(ctx, ao)

	return ctx, &xnsv1.UpdateWithStartRequest{
		HeartbeatInterval:     durationpb.New(o.heartbeatInterval),
		Input:                 swreq.GetRequest(),
		ParentClosePolicy:     convert.ToParentClosePolicy(o.parentClosePolicy),
		StartWorkflowOptions:  swreq.GetStartWorkflowOptions(),
		Update:                ureq.GetRequest(),
		UpdateWorkflowOptions: ureq.GetUpdateWorkflowOptions(),
	}, nil
}

// WithActivityOptions can be used to customize the activity options
func (o *MutexWithAcquireLockOptions) WithActivityOptions(ao workflow.ActivityOptions) *MutexWithAcquireLockOptions {
	o.activityOptions = &ao
	return o
}

// WithHeartbeatInterval can be used to customize the activity heartbeat interval
func (o *MutexWithAcquireLockOptions) WithHeartbeatInterval(d time.Duration) *MutexWithAcquireLockOptions {
	o.heartbeatInterval = d
	return o
}

// WithParentClosePolicy can be used to customize the parent close policy for the workflow
func (o *MutexWithAcquireLockOptions) WithParentClosePolicy(p enumsv1.ParentClosePolicy) *MutexWithAcquireLockOptions {
	o.parentClosePolicy = p
	return o
}

// WithUpdateOptions can be used to customize the update options
func (o *MutexWithAcquireLockOptions) WithUpdateOptions(uo *AcquireLockUpdateOptions) *MutexWithAcquireLockOptions {
	o.updateOptions = uo
	return o
}

// WithWorkflowOptions can be used to customize the workflow options
func (o *MutexWithAcquireLockOptions) WithWorkflowOptions(wo *MutexWorkflowOptions) *MutexWithAcquireLockOptions {
	o.workflowOptions = wo
	return o
}

// MutexWithAcquireLock executes a(n) mutex.v1.AcquireLock update for a(n) mutex.v1.Mutex workflow, starting it if necessary, and blocks until error or update is complete
func MutexWithAcquireLock(ctx workflow.Context, input *v1.MutexInput, update *v1.AcquireLockInput, options ...*MutexWithAcquireLockOptions) (*v1.AcquireLockOutput, MutexRun, error) {
	handle, run, err := MutexWithAcquireLockAsync(ctx, input, update, options...)
	if err != nil {
		return nil, run, err
	}
	if out, err := handle.Get(ctx); err != nil {
		return nil, run, err
	} else {
		return out, run, nil
	}
}

// MutexWithAcquireLockAsync executes a(n) mutex.v1.AcquireLock update for a(n) mutex.v1.Mutex workflow, starting it if necessary, and returns a handle to the update and workflow execution
func MutexWithAcquireLockAsync(ctx workflow.Context, input *v1.MutexInput, update *v1.AcquireLockInput, options ...*MutexWithAcquireLockOptions) (AcquireLockHandle, MutexRun, error) {
	activityName := exampleOptions.filterActivity("example.mutex.v1.Example.MutexWithAcquireLock")
	if activityName == "" {
		return nil, nil, exampleOptions.convertError(temporal.NewNonRetryableApplicationError(fmt.Sprintf("no activity registered for %s", activityName), "Unimplemented", nil))
	}
	var o *MutexWithAcquireLockOptions
	if len(options) > 0 && options[0] != nil {
		o = options[0]
	} else {
		o = NewMutexWithAcquireLockOptions()
	}
	ctx, req, err := o.Build(ctx, input, update)
	if err != nil {
		return nil, nil, exampleOptions.convertError(err)
	}
	var parentClosePolicy enumsv1.ParentClosePolicy
	if p := req.GetParentClosePolicy(); p != temporalv1.ParentClosePolicy_PARENT_CLOSE_POLICY_UNSPECIFIED {
		parentClosePolicy = enumsv1.ParentClosePolicy(p)
	}
	ctx, cancel := workflow.WithCancel(ctx)
	handle := &acquireLockHandle{
		cancel: cancel,
		future: workflow.ExecuteActivity(ctx, activityName, req),
		id:     req.GetUpdateWorkflowOptions().GetUpdateId(),
	}
	run := &mutexRun{
		cancel:            cancel,
		ctx:               ctx,
		heartbeatInterval: req.GetHeartbeatInterval().AsDuration(),
		id:                req.GetStartWorkflowOptions().GetId(),
		parentClosePolicy: parentClosePolicy,
	}
	return handle, run, nil
}

// SampleWorkflowWithMutexWorkflowOptions are used to configure a(n) example.mutex.v1.Example.SampleWorkflowWithMutex workflow execution
type SampleWorkflowWithMutexWorkflowOptions struct {
	ActivityOptions      *workflow.ActivityOptions
	Detached             bool
	HeartbeatInterval    time.Duration
	HeartbeatTimeout     time.Duration
	ParentClosePolicy    enumsv1.ParentClosePolicy
	StartWorkflowOptions *client.StartWorkflowOptions
}

// NewSampleWorkflowWithMutexWorkflowOptions initializes a new SampleWorkflowWithMutexWorkflowOptions value
func NewSampleWorkflowWithMutexWorkflowOptions() *SampleWorkflowWithMutexWorkflowOptions {
	return &SampleWorkflowWithMutexWorkflowOptions{}
}

// Build initializes the activity context and input
func (opts *SampleWorkflowWithMutexWorkflowOptions) Build(ctx workflow.Context, input *v1.SampleWorkflowWithMutexInput) (workflow.Context, *xnsv1.WorkflowRequest, error) {
	// initialize start workflow options
	swo := client.StartWorkflowOptions{}
	if opts.StartWorkflowOptions != nil {
		swo = *opts.StartWorkflowOptions
	}

	// initialize workflow id if not set
	if swo.ID == "" {
		if err := workflow.SideEffect(ctx, func(ctx workflow.Context) any {
			id, err := expression.EvalExpression(v1.SampleWorkflowWithMutexIdexpression, input.ProtoReflect())
			if err != nil {
				workflow.GetLogger(ctx).Error("error evaluating id expression for \"example.mutex.v1.Example.SampleWorkflowWithMutex\" workflow", "error", err)
				return nil
			}
			return id
		}).Get(&swo.ID); err != nil {
			return nil, nil, err
		}
	}
	if swo.ID == "" {
		if err := workflow.SideEffect(ctx, func(ctx workflow.Context) any {
			id, err := uuid.NewRandom()
			if err != nil {
				workflow.GetLogger(ctx).Error("error generating workflow id", "error", err)
				return nil
			}
			return id
		}).Get(&swo.ID); err != nil {
			return nil, nil, err
		}
	}
	if swo.ID == "" {
		return nil, nil, temporal.NewNonRetryableApplicationError("workflow id is required", "InvalidArgument", nil)
	}

	// marshal workflow request protobuf message
	inputpb, err := anypb.New(input)
	if err != nil {
		return ctx, nil, fmt.Errorf("error marshalling workflow request: %w", err)
	}

	// marshal start workflow options protobuf message
	swopb, err := xns.MarshalStartWorkflowOptions(swo)
	if err != nil {
		return ctx, nil, fmt.Errorf("error marshalling start workflow options: %w", err)
	}

	// marshal parent close policy protobuf message
	var parentClosePolicy temporalv1.ParentClosePolicy
	switch opts.ParentClosePolicy {
	case enumsv1.PARENT_CLOSE_POLICY_ABANDON:
		parentClosePolicy = temporalv1.ParentClosePolicy_PARENT_CLOSE_POLICY_ABANDON
	case enumsv1.PARENT_CLOSE_POLICY_REQUEST_CANCEL:
		parentClosePolicy = temporalv1.ParentClosePolicy_PARENT_CLOSE_POLICY_REQUEST_CANCEL
	case enumsv1.PARENT_CLOSE_POLICY_TERMINATE:
		parentClosePolicy = temporalv1.ParentClosePolicy_PARENT_CLOSE_POLICY_TERMINATE
	}

	// initialize xns activity options
	ao := workflow.ActivityOptions{}
	if opts.ActivityOptions != nil {
		ao = *opts.ActivityOptions
	}

	if ao.HeartbeatTimeout == 0 {
		ao.HeartbeatTimeout = time.Second * 60
	}

	if ao.StartToCloseTimeout == 0 && ao.ScheduleToCloseTimeout == 0 {
		ao.ScheduleToCloseTimeout = time.Hour * 24
	}

	// WaitForCancellation must be set otherwise the underlying workflow is not guaranteed to be canceled
	ao.WaitForCancellation = true

	// configure heartbeat interval
	if opts.HeartbeatInterval == 0 {
		opts.HeartbeatInterval = ao.HeartbeatTimeout / 2
	}

	ctx = workflow.WithActivityOptions(ctx, ao)

	return ctx, &xnsv1.WorkflowRequest{
		Detached:             opts.Detached,
		HeartbeatInterval:    durationpb.New(opts.HeartbeatInterval),
		ParentClosePolicy:    parentClosePolicy,
		Request:              inputpb,
		StartWorkflowOptions: swopb,
	}, nil
}

// WithActivityOptions can be used to customize the activity options
func (opts *SampleWorkflowWithMutexWorkflowOptions) WithActivityOptions(ao workflow.ActivityOptions) *SampleWorkflowWithMutexWorkflowOptions {
	opts.ActivityOptions = &ao
	return opts
}

// WithDetached can be used to start a workflow execution and exit immediately
func (opts *SampleWorkflowWithMutexWorkflowOptions) WithDetached(d bool) *SampleWorkflowWithMutexWorkflowOptions {
	opts.Detached = d
	return opts
}

// WithHeartbeatInterval can be used to customize the activity heartbeat interval
func (opts *SampleWorkflowWithMutexWorkflowOptions) WithHeartbeatInterval(d time.Duration) *SampleWorkflowWithMutexWorkflowOptions {
	opts.HeartbeatInterval = d
	return opts
}

// WithHeartbeatTimeout can be used to customize the activity heartbeat timeout
func (opts *SampleWorkflowWithMutexWorkflowOptions) WithHeartbeatTimeout(d time.Duration) *SampleWorkflowWithMutexWorkflowOptions {
	opts.HeartbeatTimeout = d
	return opts
}

// WithParentClosePolicy can be used to customize the cancellation propagation behavior
func (opts *SampleWorkflowWithMutexWorkflowOptions) WithParentClosePolicy(policy enumsv1.ParentClosePolicy) *SampleWorkflowWithMutexWorkflowOptions {
	opts.ParentClosePolicy = policy
	return opts
}

// WithStartWorkflowOptions can be used to customize the start workflow options
func (opts *SampleWorkflowWithMutexWorkflowOptions) WithStartWorkflow(swo client.StartWorkflowOptions) *SampleWorkflowWithMutexWorkflowOptions {
	opts.StartWorkflowOptions = &swo
	return opts
}

// SampleWorkflowWithMutexRun provides a handle to a example.mutex.v1.Example.SampleWorkflowWithMutex workflow execution
type SampleWorkflowWithMutexRun interface {
	// Cancel cancels the workflow
	Cancel(workflow.Context) error

	// Future returns the inner workflow.Future
	Future() workflow.Future

	// Get returns the inner workflow.Future
	Get(workflow.Context) error

	// ID returns the workflow id
	ID() string
}

// sampleWorkflowWithMutexRun provides a(n) SampleWorkflowWithMutexRun implementation
type sampleWorkflowWithMutexRun struct {
	cancel            func()
	ctx               workflow.Context
	future            workflow.Future
	id                string
	heartbeatInterval time.Duration
	parentClosePolicy enumsv1.ParentClosePolicy
}

// Cancel the underlying workflow execution
func (r *sampleWorkflowWithMutexRun) Cancel(ctx workflow.Context) error {
	if r.cancel != nil {
		r.cancel()
		if err := r.Get(ctx); err != nil && !errors.Is(err, workflow.ErrCanceled) {
			return err
		}
		return nil
	}
	return CancelExampleWorkflow(ctx, r.id, "")
}

// Future returns the underlying activity future
func (r *sampleWorkflowWithMutexRun) Future() workflow.Future {
	if r.future == nil {
		rr := GetSampleWorkflowWithMutexAsync(r.ctx, r.id, "").(*sampleWorkflowWithMutexRun)
		r.future = rr.future
		r.cancel = rr.cancel
	}
	return r.future
}

// Get blocks on activity completion and returns the underlying workflow result
func (r *sampleWorkflowWithMutexRun) Get(ctx workflow.Context) error {
	ctx, cancel := workflow.WithCancel(ctx)
	if r.future == nil {
		rr := GetSampleWorkflowWithMutexAsync(ctx, r.id, "", NewGetSampleWorkflowWithMutexOptions().WithParentClosePolicy(r.parentClosePolicy).WithHeartbeatInterval(r.heartbeatInterval)).(*sampleWorkflowWithMutexRun)
		r.future = rr.future
		r.cancel = cancel
	}
	if err := r.future.Get(ctx, nil); err != nil {
		return err
	}
	return nil
}

// ID returns the underlying workflow id
func (r *sampleWorkflowWithMutexRun) ID() string {
	return r.id
}

// SampleWorkflowWithMutex is a sample workflow that demonstrates how to
// use the Mutex service.
func SampleWorkflowWithMutex(ctx workflow.Context, req *v1.SampleWorkflowWithMutexInput, opts ...*SampleWorkflowWithMutexWorkflowOptions) error {
	run, err := SampleWorkflowWithMutexAsync(ctx, req, opts...)
	if err != nil {
		return err
	}
	return run.Get(ctx)
}

// SampleWorkflowWithMutex is a sample workflow that demonstrates how to
// use the Mutex service.
func SampleWorkflowWithMutexAsync(ctx workflow.Context, input *v1.SampleWorkflowWithMutexInput, opts ...*SampleWorkflowWithMutexWorkflowOptions) (SampleWorkflowWithMutexRun, error) {
	activityName := exampleOptions.filterActivity(v1.SampleWorkflowWithMutexWorkflowName)
	if activityName == "" {
		return nil, temporal.NewNonRetryableApplicationError(
			fmt.Sprintf("no activity registered for %s", v1.SampleWorkflowWithMutexWorkflowName),
			"Unimplemented",
			nil,
		)
	}

	var opt *SampleWorkflowWithMutexWorkflowOptions
	if len(opts) > 0 && opts[0] != nil {
		opt = opts[0]
	} else {
		opt = NewSampleWorkflowWithMutexWorkflowOptions()
	}
	ctx, req, err := opt.Build(ctx, input)
	if err != nil {
		return nil, exampleOptions.convertError(err)
	}
	ctx, cancel := workflow.WithCancel(ctx)
	return &sampleWorkflowWithMutexRun{
		cancel: cancel,
		future: workflow.ExecuteActivity(ctx, activityName, req),
		id:     req.GetStartWorkflowOptions().GetId(),
	}, nil
}

// GetSampleWorkflowWithMutex returns a(n) example.mutex.v1.Example.SampleWorkflowWithMutex workflow execution
func GetSampleWorkflowWithMutex(ctx workflow.Context, workflowID string, runID string, options ...*GetSampleWorkflowWithMutexOptions) (err error) {
	err = GetSampleWorkflowWithMutexAsync(ctx, workflowID, runID, options...).Get(ctx)
	if err != nil {
		return err
	}
	return nil
}

// GetSampleWorkflowWithMutexAsync returns a handle to a(n) example.mutex.v1.Example.SampleWorkflowWithMutex workflow execution
func GetSampleWorkflowWithMutexAsync(ctx workflow.Context, workflowID string, runID string, options ...*GetSampleWorkflowWithMutexOptions) SampleWorkflowWithMutexRun {
	activityName := exampleOptions.filterActivity("example.mutex.v1.Example.GetSampleWorkflowWithMutex")
	if activityName == "" {
		f, set := workflow.NewFuture(ctx)
		set.SetError(temporal.NewNonRetryableApplicationError(fmt.Sprintf("no activity registered for %s", activityName), "Unimplemented", nil))
		return &sampleWorkflowWithMutexRun{
			future: f,
			id:     workflowID,
		}
	}
	var opt *GetSampleWorkflowWithMutexOptions
	if len(options) > 0 && options[0] != nil {
		opt = options[0]
	} else {
		opt = NewGetSampleWorkflowWithMutexOptions()
	}
	ctx, req, err := opt.Build(ctx, workflowID, runID)
	if err != nil {
		f, set := workflow.NewFuture(ctx)
		set.SetError(exampleOptions.convertError(temporal.NewNonRetryableApplicationError(fmt.Sprintf("no activity registered for %s", activityName), "Unimplemented", nil)))
		return &sampleWorkflowWithMutexRun{
			future: f,
			id:     workflowID,
		}
	}
	ctx, cancel := workflow.WithCancel(ctx)
	return &sampleWorkflowWithMutexRun{
		cancel: cancel,
		future: workflow.ExecuteActivity(ctx, activityName, req),
		id:     workflowID,
	}
}

// GetSampleWorkflowWithMutexOptions are used to configure a(n) example.mutex.v1.Example.SampleWorkflowWithMutex workflow execution getter activity
type GetSampleWorkflowWithMutexOptions struct {
	activityOptions   *workflow.ActivityOptions
	heartbeatInterval time.Duration
	parentClosePolicy enumsv1.ParentClosePolicy
}

// NewGetSampleWorkflowWithMutexOptions initializes a new GetSampleWorkflowWithMutexOptions value
func NewGetSampleWorkflowWithMutexOptions() *GetSampleWorkflowWithMutexOptions {
	return &GetSampleWorkflowWithMutexOptions{}
}

// Build initializes the activity context and input
func (opt *GetSampleWorkflowWithMutexOptions) Build(ctx workflow.Context, workflowID string, runID string) (workflow.Context, *xnsv1.GetWorkflowRequest, error) {
	if opt.heartbeatInterval == 0 {
		opt.heartbeatInterval = 30000000000 // 30 seconds
	}

	// configure activity options
	var ao workflow.ActivityOptions
	if opt.activityOptions != nil {
		ao = *opt.activityOptions
	} else {
		ao = workflow.ActivityOptions{}
	}
	if ao.HeartbeatTimeout == 0 {
		ao.HeartbeatTimeout = 60000000000 // 1 minute
	}
	// WaitForCancellation must be set otherwise the underlying workflow is not guaranteed to be canceled
	ao.WaitForCancellation = true

	if ao.StartToCloseTimeout == 0 && ao.ScheduleToCloseTimeout == 0 {
		ao.ScheduleToCloseTimeout = 86400000000000 // 1 day
	}
	ctx = workflow.WithActivityOptions(ctx, ao)

	return ctx, &xnsv1.GetWorkflowRequest{
		HeartbeatInterval: durationpb.New(opt.heartbeatInterval),
		ParentClosePolicy: opt.parentClosePolicy,
		RunId:             runID,
		WorkflowId:        workflowID,
	}, nil
}

// WithActivityOptions can be used to customize the activity options
func (o *GetSampleWorkflowWithMutexOptions) WithActivityOptions(ao workflow.ActivityOptions) *GetSampleWorkflowWithMutexOptions {
	o.activityOptions = &ao
	return o
}

// WithHeartbeatInterval can be used to customize the activity heartbeat interval
func (o *GetSampleWorkflowWithMutexOptions) WithHeartbeatInterval(d time.Duration) *GetSampleWorkflowWithMutexOptions {
	o.heartbeatInterval = d
	return o
}

// WithParentClosePolicy can be used to customize the cancellation propagation behavior
func (o *GetSampleWorkflowWithMutexOptions) WithParentClosePolicy(policy enumsv1.ParentClosePolicy) *GetSampleWorkflowWithMutexOptions {
	o.parentClosePolicy = policy
	return o
}

// ReleaseLockSignalOptions are used to configure a(n) mutex.v1.ReleaseLock signal execution
type ReleaseLockSignalOptions struct {
	ActivityOptions   *workflow.ActivityOptions
	HeartbeatInterval time.Duration
}

// NewReleaseLockSignalOptions initializes a new ReleaseLockSignalOptions value
func NewReleaseLockSignalOptions() *ReleaseLockSignalOptions {
	return &ReleaseLockSignalOptions{}
}

// WithActivityOptions can be used to customize the activity options
func (opts *ReleaseLockSignalOptions) WithActivityOptions(ao workflow.ActivityOptions) *ReleaseLockSignalOptions {
	opts.ActivityOptions = &ao
	return opts
}

// WithHeartbeatInterval can be used to customize the activity heartbeat interval
func (opts *ReleaseLockSignalOptions) WithHeartbeatInterval(d time.Duration) *ReleaseLockSignalOptions {
	opts.HeartbeatInterval = d
	return opts
}

// ReleaseLockSignalHandle provides a handle for a example.mutex.v1.Example.ReleaseLock signal activity
type ReleaseLockSignalHandle interface {
	// Cancel cancels the workflow
	Cancel(workflow.Context) error
	// Future returns the inner workflow.Future
	Future() workflow.Future
	// Get returns the inner workflow.Future
	Get(workflow.Context) error
}

// releaseLockSignalHandle provides a(n) ReleaseLockQueryHandle implementation
type releaseLockSignalHandle struct {
	cancel func()
	future workflow.Future
}

// Cancel the underlying signal activity
func (r *releaseLockSignalHandle) Cancel(ctx workflow.Context) error {
	r.cancel()
	if err := r.Get(ctx); err != nil && !errors.Is(err, workflow.ErrCanceled) {
		return err
	}
	return nil
}

// Future returns the underlying activity future
func (r *releaseLockSignalHandle) Future() workflow.Future {
	return r.future
}

// Get blocks on activity completion
func (r *releaseLockSignalHandle) Get(ctx workflow.Context) error {
	return r.future.Get(ctx, nil)
}

// ReleaseLock releases a lock on a resource identified by `lease_id`.
func ReleaseLock(ctx workflow.Context, workflowID string, runID string, req *v1.ReleaseLockInput, opts ...*ReleaseLockSignalOptions) error {
	handle, err := ReleaseLockAsync(ctx, workflowID, runID, req, opts...)
	if err != nil {
		return err
	}
	return handle.Get(ctx)
}

// ReleaseLockAsync executes a(n) mutex.v1.ReleaseLock signal
func ReleaseLockAsync(ctx workflow.Context, workflowID string, runID string, req *v1.ReleaseLockInput, opts ...*ReleaseLockSignalOptions) (ReleaseLockSignalHandle, error) {
	activityName := exampleOptions.filterActivity(v1.ReleaseLockSignalName)
	if activityName == "" {
		return nil, temporal.NewNonRetryableApplicationError(
			fmt.Sprintf("no activity registered for %s", v1.ReleaseLockSignalName),
			"Unimplemented",
			nil,
		)
	}

	opt := &ReleaseLockSignalOptions{}
	if len(opts) > 0 && opts[0] != nil {
		opt = opts[0]
	}

	if opt.HeartbeatInterval == 0 {
		opt.HeartbeatInterval = time.Second * 30
	}

	// configure activity options
	var ao workflow.ActivityOptions
	if opt.ActivityOptions != nil {
		ao = *opt.ActivityOptions
	} else {
		ao = workflow.ActivityOptions{}
	}
	if ao.HeartbeatTimeout == 0 {
		ao.HeartbeatTimeout = opt.HeartbeatInterval * 2
	}
	// WaitForCancellation must be set otherwise the underlying workflow is not guaranteed to be canceled
	ao.WaitForCancellation = true

	if ao.StartToCloseTimeout == 0 && ao.ScheduleToCloseTimeout == 0 {
		ao.ScheduleToCloseTimeout = 60000000000 // 1 minute
	}
	ctx = workflow.WithActivityOptions(ctx, ao)

	// marshal workflow request
	wreq, err := anypb.New(req)
	if err != nil {
		return nil, fmt.Errorf("error marshalling workflow request: %w", err)
	}

	ctx, cancel := workflow.WithCancel(ctx)
	return &releaseLockSignalHandle{
		cancel: cancel,
		future: workflow.ExecuteActivity(ctx, activityName, &xnsv1.SignalRequest{
			HeartbeatInterval: durationpb.New(opt.HeartbeatInterval),
			WorkflowId:        workflowID,
			RunId:             runID,
			Request:           wreq,
		}),
	}, nil
}

// AcquireLockUpdateOptions are used to configure a(n) mutex.v1.AcquireLock update execution
type AcquireLockUpdateOptions struct {
	ActivityOptions       *workflow.ActivityOptions
	HeartbeatInterval     time.Duration
	UpdateWorkflowOptions *client.UpdateWorkflowOptions
}

// NewAcquireLockUpdateOptions initializes a new AcquireLockUpdateOptions value
func NewAcquireLockUpdateOptions() *AcquireLockUpdateOptions {
	return &AcquireLockUpdateOptions{}
}

// Build initializes the update options
func (opt *AcquireLockUpdateOptions) Build(ctx workflow.Context, workflowID string, runID string, input *v1.AcquireLockInput) (workflow.Context, *xnsv1.UpdateRequest, error) {
	// configure activity options
	var ao workflow.ActivityOptions
	if opt.ActivityOptions != nil {
		ao = *opt.ActivityOptions
	} else {
		ao = workflow.ActivityOptions{}
	}
	if ao.HeartbeatTimeout == 0 {
		ao.HeartbeatTimeout = 60000000000 // 1 minute
	}
	if ao.StartToCloseTimeout == 0 && ao.ScheduleToCloseTimeout == 0 {
		ao.ScheduleToCloseTimeout = time.Hour * 24
	}

	// WaitForCancellation must be set otherwise the underlying workflow is not guaranteed to be canceled
	ao.WaitForCancellation = true

	// configure heartbeat interval
	if opt.HeartbeatInterval == 0 {
		opt.HeartbeatInterval = 30000000000 // 30 seconds
	}

	ctx = workflow.WithActivityOptions(ctx, ao)

	uo := client.UpdateWorkflowOptions{}
	if opt.UpdateWorkflowOptions != nil {
		uo = *opt.UpdateWorkflowOptions
	}
	uo.WorkflowID = workflowID
	uo.RunID = runID
	if uo.UpdateID == "" {
		if err := workflow.SideEffect(ctx, func(ctx workflow.Context) any {
			id, err := uuid.NewRandom()
			if err != nil {
				workflow.GetLogger(ctx).Error("error generating update id", "error", err)
				return nil
			}
			return id
		}).Get(&uo.UpdateID); err != nil {
			return nil, nil, err
		}
	}
	if uo.UpdateID == "" {
		return nil, nil, temporal.NewNonRetryableApplicationError("update id is required", "InvalidArgument", nil)
	}

	uopb, err := xns.MarshalUpdateWorkflowOptions(uo)
	if err != nil {
		return nil, nil, fmt.Errorf("error marshalling update workflow options: %w", err)
	}

	inpb, err := anypb.New(input)
	if err != nil {
		return nil, nil, fmt.Errorf("error marshalling update request: %w", err)
	}

	return ctx, &xnsv1.UpdateRequest{
		HeartbeatInterval:     durationpb.New(opt.HeartbeatInterval),
		Request:               inpb,
		UpdateWorkflowOptions: uopb,
	}, nil
}

// WithActivityOptions can be used to customize the activity options
func (opts *AcquireLockUpdateOptions) WithActivityOptions(ao workflow.ActivityOptions) *AcquireLockUpdateOptions {
	opts.ActivityOptions = &ao
	return opts
}

// WithHeartbeatInterval can be used to customize the activity heartbeat interval
func (opts *AcquireLockUpdateOptions) WithHeartbeatInterval(d time.Duration) *AcquireLockUpdateOptions {
	opts.HeartbeatInterval = d
	return opts
}

// WithUpdateWorkflowOptions can be used to customize the update workflow options
func (opts *AcquireLockUpdateOptions) WithUpdateWorkflowOptions(uwo client.UpdateWorkflowOptions) *AcquireLockUpdateOptions {
	opts.UpdateWorkflowOptions = &uwo
	return opts
}

// AcquireLockHandle provides a handle to a example.mutex.v1.Example.AcquireLock workflow update
type AcquireLockHandle interface {
	// Cancel cancels the update activity
	Cancel(workflow.Context) error

	// Future returns the inner workflow.Future
	Future() workflow.Future

	// Get blocks on update completion and returns the result
	Get(workflow.Context) (*v1.AcquireLockOutput, error)

	// ID returns the update id
	ID() string
}

// acquireLockHandle provides a(n) AcquireLockHandle implementation
type acquireLockHandle struct {
	cancel func()
	future workflow.Future
	id     string
}

// Cancel the underlying workflow update
func (r *acquireLockHandle) Cancel(ctx workflow.Context) error {
	r.cancel()
	if _, err := r.Get(ctx); err != nil && !errors.Is(err, workflow.ErrCanceled) {
		return err
	}
	return nil
}

// Future returns the underlying activity future
func (r *acquireLockHandle) Future() workflow.Future {
	return r.future
}

// Get blocks on activity completion and returns the underlying update result
func (r *acquireLockHandle) Get(ctx workflow.Context) (*v1.AcquireLockOutput, error) {
	var resp v1.AcquireLockOutput
	if err := r.future.Get(ctx, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// ID returns the underlying workflow id
func (r *acquireLockHandle) ID() string {
	return r.id
}

// AcquireLock requests a lock on a resource identified by `resource_id`
// and blocks until the lock is acquired, returning a `lease_id` that
// can be used to release the lock.
func AcquireLock(ctx workflow.Context, workflowID string, runID string, req *v1.AcquireLockInput, opts ...*AcquireLockUpdateOptions) (*v1.AcquireLockOutput, error) {
	run, err := AcquireLockAsync(ctx, workflowID, runID, req, opts...)
	if err != nil {
		return nil, err
	}
	return run.Get(ctx)
}

// AcquireLockAsync executes a(n) mutex.v1.AcquireLock update and blocks until error or response received
func AcquireLockAsync(ctx workflow.Context, workflowID string, runID string, input *v1.AcquireLockInput, opts ...*AcquireLockUpdateOptions) (AcquireLockHandle, error) {
	activityName := exampleOptions.filterActivity(v1.AcquireLockUpdateName)
	if activityName == "" {
		return nil, temporal.NewNonRetryableApplicationError(
			fmt.Sprintf("no activity registered for %s", v1.AcquireLockUpdateName),
			"Unimplemented",
			nil,
		)
	}

	var opt *AcquireLockUpdateOptions
	if len(opts) > 0 && opts[0] != nil {
		opt = opts[0]
	} else {
		opt = NewAcquireLockUpdateOptions()
	}

	ctx, req, err := opt.Build(ctx, workflowID, runID, input)
	if err != nil {
		return nil, err
	}
	ctx, cancel := workflow.WithCancel(ctx)
	return &acquireLockHandle{
		cancel: cancel,
		id:     req.GetUpdateWorkflowOptions().GetUpdateId(),
		future: workflow.ExecuteActivity(ctx, activityName, req),
	}, nil
}

// CancelExampleWorkflow cancels an existing workflow
func CancelExampleWorkflow(ctx workflow.Context, workflowID string, runID string) error {
	return CancelExampleWorkflowAsync(ctx, workflowID, runID).Get(ctx, nil)
}

// CancelExampleWorkflowAsync cancels an existing workflow
func CancelExampleWorkflowAsync(ctx workflow.Context, workflowID string, runID string) workflow.Future {
	activityName := exampleOptions.filterActivity("example.mutex.v1.Example.CancelWorkflow")
	if activityName == "" {
		f, s := workflow.NewFuture(ctx)
		s.SetError(temporal.NewNonRetryableApplicationError(
			"no activity registered for example.mutex.v1.Example.CancelWorkflow",
			"Unimplemented",
			nil,
		))
		return f
	}
	ao := workflow.GetActivityOptions(ctx)
	if ao.StartToCloseTimeout == 0 && ao.ScheduleToCloseTimeout == 0 {
		ao.StartToCloseTimeout = time.Minute
	}
	ctx = workflow.WithActivityOptions(ctx, ao)
	return workflow.ExecuteActivity(ctx, activityName, workflowID, runID)
}

// exampleActivities provides activities that can be used to interact with a(n) Example service's workflow, queries, signals, and updates across namespaces
type exampleActivities struct {
	client v1.ExampleClient
}

// CancelWorkflow cancels an existing workflow execution
func (a *exampleActivities) CancelWorkflow(ctx context.Context, workflowID string, runID string) error {
	return a.client.CancelWorkflow(ctx, workflowID, runID)
}

// GetMutex retrieves a(n) mutex.v1.Mutex workflow via an activity
func (a *exampleActivities) GetMutex(ctx context.Context, input *xnsv1.GetWorkflowRequest) (err error) {
	heartbeatInterval := input.GetHeartbeatInterval().AsDuration()
	if heartbeatInterval == 0 {
		heartbeatInterval = time.Second * 30
	}

	actx, cancel := context.WithCancel(context.Background())
	defer cancel()
	run := a.client.GetMutex(actx, input.GetWorkflowId(), input.GetRunId())
	done := make(chan struct{})
	go func() {
		defer close(done)
		err = run.Get(actx)
	}()

	for {
		select {
		// send heartbeats periodically
		case <-time.After(heartbeatInterval):
			activity.RecordHeartbeat(ctx)

		// return retryable error if the worker is stopping
		case <-activity.GetWorkerStopChannel(ctx):
			return exampleOptions.convertError(temporal.NewApplicationError("worker is stopping", "WorkerStopped"))

		// catch parent activity context cancellation. in most cases, this should indicate a
		// server-sent cancellation, but there's a non-zero possibility that this cancellation
		// is received due to the worker stopping, prior to detecting the closing of the worker
		// stop channel. to give us an opportunity to detect a cancellation stemming from the
		// worker closing, we again check to see if the worker stop channel is closed before
		// propagating the cancellation
		case <-ctx.Done():
			select {
			case <-activity.GetWorkerStopChannel(ctx):
				activity.GetLogger(ctx).Info("worker is stopping")
				return exampleOptions.convertError(temporal.NewApplicationError("worker is stopping", "WorkerStopped"))
			default:
				parentClosePolicy := input.GetParentClosePolicy()
				if parentClosePolicy == enumsv1.PARENT_CLOSE_POLICY_REQUEST_CANCEL || parentClosePolicy == enumsv1.PARENT_CLOSE_POLICY_TERMINATE {
					disconnectedCtx, cancel := context.WithTimeout(context.Background(), time.Minute)
					defer cancel()
					if parentClosePolicy == enumsv1.PARENT_CLOSE_POLICY_REQUEST_CANCEL {
						err = run.Cancel(disconnectedCtx)
					} else {
						err = run.Terminate(disconnectedCtx, "xns activity cancellation received", "error", ctx.Err())
					}
					if err != nil {
						return exampleOptions.convertError(err)
					}
				}
				return exampleOptions.convertError(temporal.NewCanceledError(ctx.Err().Error()))
			}

		// handle workflow completion
		case <-done:
			return exampleOptions.convertError(err)
		}
	}
}

// Mutex executes a(n) mutex.v1.Mutex workflow via an activity
func (a *exampleActivities) Mutex(ctx context.Context, input *xnsv1.WorkflowRequest) (err error) {
	// unmarshal workflow request
	var req v1.MutexInput
	if err := input.Request.UnmarshalTo(&req); err != nil {
		return exampleOptions.convertError(temporal.NewNonRetryableApplicationError(
			fmt.Sprintf("error unmarshalling workflow request of type %s as github.com/cludden/protoc-gen-go-temporal/gen/example/mutex/v1.MutexInput", input.Request.GetTypeUrl()),
			"InvalidArgument",
			err,
		))
	}

	// initialize workflow execution
	actx := ctx
	if !input.GetDetached() {
		var cancel context.CancelFunc
		actx, cancel = context.WithCancel(context.Background())
		defer cancel()
	}
	var run v1.MutexRun
	run, err = a.client.MutexAsync(actx, &req, v1.NewMutexOptions().WithStartWorkflowOptions(
		xns.UnmarshalStartWorkflowOptions(input.GetStartWorkflowOptions()),
	))
	if err != nil {
		return exampleOptions.convertError(err)
	}

	// exit early if detached enabled
	if input.GetDetached() {
		return nil
	}

	// otherwise, wait for execution to complete in child goroutine
	doneCh := make(chan struct{})
	go func() {
		err = run.Get(actx)
		close(doneCh)
	}()

	heartbeatInterval := input.GetHeartbeatInterval().AsDuration()
	if heartbeatInterval == 0 {
		heartbeatInterval = time.Second * 30
	}

	// heartbeat activity while waiting for workflow execution to complete
	for {
		select {
		// send heartbeats periodically
		case <-time.After(heartbeatInterval):
			activity.RecordHeartbeat(ctx, run.ID())

		// return retryable error on worker close
		case <-activity.GetWorkerStopChannel(ctx):
			return temporal.NewApplicationError("worker is stopping", "WorkerStopped")

		// catch parent activity context cancellation. in most cases, this should indicate a
		// server-sent cancellation, but there's a non-zero possibility that this cancellation
		// is received due to the worker stopping, prior to detecting the closing of the worker
		// stop channel. to give us an opportunity to detect a cancellation stemming from the
		// worker closing, we again check to see if the worker stop channel is closed before
		// propagating the cancellation
		case <-ctx.Done():
			select {
			case <-activity.GetWorkerStopChannel(ctx):
				return temporal.NewApplicationError("worker is stopping", "WorkerStopped")
			default:
				parentClosePolicy := input.GetParentClosePolicy()
				if parentClosePolicy == temporalv1.ParentClosePolicy_PARENT_CLOSE_POLICY_REQUEST_CANCEL || parentClosePolicy == temporalv1.ParentClosePolicy_PARENT_CLOSE_POLICY_TERMINATE {
					disconnectedCtx, cancel := context.WithTimeout(context.Background(), time.Minute)
					defer cancel()
					if parentClosePolicy == temporalv1.ParentClosePolicy_PARENT_CLOSE_POLICY_REQUEST_CANCEL {
						err = run.Cancel(disconnectedCtx)
					} else {
						err = run.Terminate(disconnectedCtx, "xns activity cancellation received", "error", ctx.Err())
					}
					if err != nil {
						return exampleOptions.convertError(err)
					}
				}
				return exampleOptions.convertError(temporal.NewCanceledError(ctx.Err().Error()))
			}

		// handle workflow completion
		case <-doneCh:
			return exampleOptions.convertError(err)
		}
	}
}

// MutexWithAcquireLock executes a(n) mutex.v1.Mutex workflow with a(n) mutex.v1.AcquireLock update via an activity
func (a *exampleActivities) MutexWithAcquireLock(ctx context.Context, input *xnsv1.UpdateWithStartRequest) (out *v1.AcquireLockOutput, err error) {
	// unmarshal workflow request
	var req v1.MutexInput
	if err := input.GetInput().UnmarshalTo(&req); err != nil {
		return nil, exampleOptions.convertError(temporal.NewNonRetryableApplicationError(
			fmt.Sprintf("error unmarshalling workflow request of type %s as github.com/cludden/protoc-gen-go-temporal/gen/example/mutex/v1.MutexInput", input.GetInput().GetTypeUrl()),
			"InvalidArgument",
			err,
		))
	}

	// unmarshal update request
	var update v1.AcquireLockInput
	if err := input.GetUpdate().UnmarshalTo(&update); err != nil {
		return nil, exampleOptions.convertError(temporal.NewNonRetryableApplicationError(
			fmt.Sprintf("error unmarshalling update request of type %s as github.com/cludden/protoc-gen-go-temporal/gen/example/mutex/v1.AcquireLockInput", input.GetUpdate().GetTypeUrl()),
			"InvalidArgument",
			err,
		))
	}

	// unmarshal workflow and update options
	swo := xns.UnmarshalStartWorkflowOptions(input.GetStartWorkflowOptions())
	uwo := xns.UnmarshalUpdateWorkflowOptions(input.GetUpdateWorkflowOptions())

	var run v1.MutexRun
	var handle v1.AcquireLockHandle
	if activity.HasHeartbeatDetails(ctx) {
		// attach to existing update and execution
		var workflowID, runID, updateID string
		if err := activity.GetHeartbeatDetails(ctx, &workflowID, &runID, &updateID); err != nil {
			return nil, exampleOptions.convertError(fmt.Errorf("error getting heartbeat details: %w", err))
		} else if workflowID == "" || runID == "" || updateID == "" {
			return nil, exampleOptions.convertError(fmt.Errorf("invalid heartbeat details: workflowID=%q runID=%q updateID=%s", workflowID, runID, updateID))
		}
		run = a.client.GetMutex(ctx, workflowID, runID)
		handle, err = a.client.GetAcquireLock(ctx, client.GetWorkflowUpdateHandleOptions{
			RunID:      runID,
			UpdateID:   updateID,
			WorkflowID: workflowID,
		})
		if err != nil {
			return nil, exampleOptions.convertError(fmt.Errorf("error getting update with id %s: %w", updateID, err))
		}
	} else {
		// execute update with start asynchronously
		handle, run, err = a.client.MutexWithAcquireLockAsync(
			ctx,
			&req,
			&update,
			v1.NewMutexWithAcquireLockOptions().WithMutexOptions(
				v1.NewMutexOptions().WithStartWorkflowOptions(swo),
			).WithAcquireLockOptions(
				v1.NewAcquireLockOptions().WithUpdateWorkflowOptions(uwo).WithWaitPolicy(client.WorkflowUpdateStageAccepted),
			),
		)
		if err != nil {
			return nil, exampleOptions.convertError(fmt.Errorf("error executing update with start: %w", err))
		}
		activity.RecordHeartbeat(ctx, run.ID(), run.RunID(), handle.UpdateID())
	}

	// return early if detached
	if input.GetDetached() {
		return nil, nil
	}

	// initialize heartbeat interval duration
	heartbeatInterval := input.GetHeartbeatInterval().AsDuration()
	if heartbeatInterval == 0 {
		heartbeatTimeout := activity.GetInfo(ctx).HeartbeatTimeout
		if heartbeatTimeout > 0 {
			heartbeatInterval = heartbeatTimeout / 2
		} else {
			heartbeatInterval = time.Second * 30
		}
	}

	// wait for update to complete in child goroutine
	doneCh := make(chan struct{})
	go func() {
		defer close(doneCh)
		out, err = handle.Get(ctx)
	}()

	// heartbeat activity while waiting for update to complete
	for {
		select {
		case <-time.After(heartbeatInterval):
			activity.RecordHeartbeat(ctx, run.ID(), run.RunID(), handle.UpdateID())

		case <-activity.GetWorkerStopChannel(ctx):
			return nil, exampleOptions.convertError(temporal.NewApplicationError("worker is stopping", "WorkerStopping"))

		// catch parent activity context cancellation. in most cases, this should indicate a
		// server-sent cancellation, but there's a non-zero possibility that this cancellation
		// is received due to the worker stopping, prior to detecting the closing of the worker
		// stop channel. to give us an opportunity to detect a cancellation stemming from the
		// worker closing, we again check to see if the worker stop channel is closed before
		// propagating the cancellation
		case <-ctx.Done():
			select {
			case <-activity.GetWorkerStopChannel(ctx):
				return nil, exampleOptions.convertError(temporal.NewApplicationError("worker is stopping", "WorkerStopping"))
			default:
				parentClosePolicy := input.GetParentClosePolicy()
				if parentClosePolicy == temporalv1.ParentClosePolicy_PARENT_CLOSE_POLICY_REQUEST_CANCEL || parentClosePolicy == temporalv1.ParentClosePolicy_PARENT_CLOSE_POLICY_TERMINATE {
					disconnectedCtx, cancel := context.WithTimeout(ctx, time.Minute)
					defer cancel()
					if parentClosePolicy == temporalv1.ParentClosePolicy_PARENT_CLOSE_POLICY_REQUEST_CANCEL {
						err = run.Cancel(disconnectedCtx)
					} else {
						err = run.Terminate(disconnectedCtx, "xns activity cancellation received", "error", ctx.Err())
					}
					if err != nil {
						return nil, exampleOptions.convertError(err)
					}
				}
				return nil, exampleOptions.convertError(temporal.NewCanceledError(ctx.Err().Error()))
			}

		case <-doneCh:
			return out, exampleOptions.convertError(err)
		}
	}
}

// GetSampleWorkflowWithMutex retrieves a(n) example.mutex.v1.Example.SampleWorkflowWithMutex workflow via an activity
func (a *exampleActivities) GetSampleWorkflowWithMutex(ctx context.Context, input *xnsv1.GetWorkflowRequest) (err error) {
	heartbeatInterval := input.GetHeartbeatInterval().AsDuration()
	if heartbeatInterval == 0 {
		heartbeatInterval = time.Second * 30
	}

	actx, cancel := context.WithCancel(context.Background())
	defer cancel()
	run := a.client.GetSampleWorkflowWithMutex(actx, input.GetWorkflowId(), input.GetRunId())
	done := make(chan struct{})
	go func() {
		defer close(done)
		err = run.Get(actx)
	}()

	for {
		select {
		// send heartbeats periodically
		case <-time.After(heartbeatInterval):
			activity.RecordHeartbeat(ctx)

		// return retryable error if the worker is stopping
		case <-activity.GetWorkerStopChannel(ctx):
			return exampleOptions.convertError(temporal.NewApplicationError("worker is stopping", "WorkerStopped"))

		// catch parent activity context cancellation. in most cases, this should indicate a
		// server-sent cancellation, but there's a non-zero possibility that this cancellation
		// is received due to the worker stopping, prior to detecting the closing of the worker
		// stop channel. to give us an opportunity to detect a cancellation stemming from the
		// worker closing, we again check to see if the worker stop channel is closed before
		// propagating the cancellation
		case <-ctx.Done():
			select {
			case <-activity.GetWorkerStopChannel(ctx):
				activity.GetLogger(ctx).Info("worker is stopping")
				return exampleOptions.convertError(temporal.NewApplicationError("worker is stopping", "WorkerStopped"))
			default:
				parentClosePolicy := input.GetParentClosePolicy()
				if parentClosePolicy == enumsv1.PARENT_CLOSE_POLICY_REQUEST_CANCEL || parentClosePolicy == enumsv1.PARENT_CLOSE_POLICY_TERMINATE {
					disconnectedCtx, cancel := context.WithTimeout(context.Background(), time.Minute)
					defer cancel()
					if parentClosePolicy == enumsv1.PARENT_CLOSE_POLICY_REQUEST_CANCEL {
						err = run.Cancel(disconnectedCtx)
					} else {
						err = run.Terminate(disconnectedCtx, "xns activity cancellation received", "error", ctx.Err())
					}
					if err != nil {
						return exampleOptions.convertError(err)
					}
				}
				return exampleOptions.convertError(temporal.NewCanceledError(ctx.Err().Error()))
			}

		// handle workflow completion
		case <-done:
			return exampleOptions.convertError(err)
		}
	}
}

// SampleWorkflowWithMutex executes a(n) example.mutex.v1.Example.SampleWorkflowWithMutex workflow via an activity
func (a *exampleActivities) SampleWorkflowWithMutex(ctx context.Context, input *xnsv1.WorkflowRequest) (err error) {
	// unmarshal workflow request
	var req v1.SampleWorkflowWithMutexInput
	if err := input.Request.UnmarshalTo(&req); err != nil {
		return exampleOptions.convertError(temporal.NewNonRetryableApplicationError(
			fmt.Sprintf("error unmarshalling workflow request of type %s as github.com/cludden/protoc-gen-go-temporal/gen/example/mutex/v1.SampleWorkflowWithMutexInput", input.Request.GetTypeUrl()),
			"InvalidArgument",
			err,
		))
	}

	// initialize workflow execution
	actx := ctx
	if !input.GetDetached() {
		var cancel context.CancelFunc
		actx, cancel = context.WithCancel(context.Background())
		defer cancel()
	}
	var run v1.SampleWorkflowWithMutexRun
	run, err = a.client.SampleWorkflowWithMutexAsync(actx, &req, v1.NewSampleWorkflowWithMutexOptions().WithStartWorkflowOptions(
		xns.UnmarshalStartWorkflowOptions(input.GetStartWorkflowOptions()),
	))
	if err != nil {
		return exampleOptions.convertError(err)
	}

	// exit early if detached enabled
	if input.GetDetached() {
		return nil
	}

	// otherwise, wait for execution to complete in child goroutine
	doneCh := make(chan struct{})
	go func() {
		err = run.Get(actx)
		close(doneCh)
	}()

	heartbeatInterval := input.GetHeartbeatInterval().AsDuration()
	if heartbeatInterval == 0 {
		heartbeatInterval = time.Second * 30
	}

	// heartbeat activity while waiting for workflow execution to complete
	for {
		select {
		// send heartbeats periodically
		case <-time.After(heartbeatInterval):
			activity.RecordHeartbeat(ctx, run.ID())

		// return retryable error on worker close
		case <-activity.GetWorkerStopChannel(ctx):
			return temporal.NewApplicationError("worker is stopping", "WorkerStopped")

		// catch parent activity context cancellation. in most cases, this should indicate a
		// server-sent cancellation, but there's a non-zero possibility that this cancellation
		// is received due to the worker stopping, prior to detecting the closing of the worker
		// stop channel. to give us an opportunity to detect a cancellation stemming from the
		// worker closing, we again check to see if the worker stop channel is closed before
		// propagating the cancellation
		case <-ctx.Done():
			select {
			case <-activity.GetWorkerStopChannel(ctx):
				return temporal.NewApplicationError("worker is stopping", "WorkerStopped")
			default:
				parentClosePolicy := input.GetParentClosePolicy()
				if parentClosePolicy == temporalv1.ParentClosePolicy_PARENT_CLOSE_POLICY_REQUEST_CANCEL || parentClosePolicy == temporalv1.ParentClosePolicy_PARENT_CLOSE_POLICY_TERMINATE {
					disconnectedCtx, cancel := context.WithTimeout(context.Background(), time.Minute)
					defer cancel()
					if parentClosePolicy == temporalv1.ParentClosePolicy_PARENT_CLOSE_POLICY_REQUEST_CANCEL {
						err = run.Cancel(disconnectedCtx)
					} else {
						err = run.Terminate(disconnectedCtx, "xns activity cancellation received", "error", ctx.Err())
					}
					if err != nil {
						return exampleOptions.convertError(err)
					}
				}
				return exampleOptions.convertError(temporal.NewCanceledError(ctx.Err().Error()))
			}

		// handle workflow completion
		case <-doneCh:
			return exampleOptions.convertError(err)
		}
	}
}

// ReleaseLock executes a(n) mutex.v1.ReleaseLock signal via an activity
func (a *exampleActivities) ReleaseLock(ctx context.Context, input *xnsv1.SignalRequest) (err error) {
	// unmarshal signal request
	var req v1.ReleaseLockInput
	if err := input.Request.UnmarshalTo(&req); err != nil {
		return exampleOptions.convertError(temporal.NewNonRetryableApplicationError(
			fmt.Sprintf("error unmarshalling signal request of type %s as github.com/cludden/protoc-gen-go-temporal/gen/example/mutex/v1.ReleaseLockInput", input.Request.GetTypeUrl()),
			"InvalidArgument",
			err,
		))
	}
	// execute signal in child goroutine
	doneCh := make(chan struct{})
	go func() {
		err = a.client.ReleaseLock(ctx, input.GetWorkflowId(), input.GetRunId(), &req)
		close(doneCh)
	}()

	heartbeatInterval := input.GetHeartbeatInterval().AsDuration()
	if heartbeatInterval == 0 {
		heartbeatInterval = time.Second * 10
	}

	// heartbeat activity while waiting for signal to complete
	for {
		select {
		case <-time.After(heartbeatInterval):
			activity.RecordHeartbeat(ctx)
		case <-ctx.Done():
			exampleOptions.convertError(ctx.Err())
		case <-doneCh:
			return exampleOptions.convertError(err)
		}
	}
}

// AcquireLock executes a(n) mutex.v1.AcquireLock update via an activity
func (a *exampleActivities) AcquireLock(ctx context.Context, input *xnsv1.UpdateRequest) (resp *v1.AcquireLockOutput, err error) {
	var handle v1.AcquireLockHandle
	if activity.HasHeartbeatDetails(ctx) {
		// extract update id from heartbeat details
		var updateID string
		if err := activity.GetHeartbeatDetails(ctx, &updateID); err != nil {
			return nil, exampleOptions.convertError(err)
		}

		// retrieve handle for existing update
		handle, err = a.client.GetAcquireLock(ctx, client.GetWorkflowUpdateHandleOptions{
			WorkflowID: input.GetUpdateWorkflowOptions().GetWorkflowId(),
			RunID:      input.GetUpdateWorkflowOptions().GetRunId(),
			UpdateID:   updateID,
		})
		if err != nil {
			return nil, exampleOptions.convertError(err)
		}
	} else {
		// unmarshal update request
		var req v1.AcquireLockInput
		if err := input.Request.UnmarshalTo(&req); err != nil {
			return nil, exampleOptions.convertError(temporal.NewNonRetryableApplicationError(
				fmt.Sprintf("error unmarshalling update request of type %s as github.com/cludden/protoc-gen-go-temporal/gen/example/mutex/v1.AcquireLockInput", input.Request.GetTypeUrl()),
				"InvalidArgument",
				err,
			))
		}

		uo := xns.UnmarshalUpdateWorkflowOptions(input.GetUpdateWorkflowOptions())
		uo.WaitForStage = client.WorkflowUpdateStageAccepted

		// initialize update execution
		handle, err = a.client.AcquireLockAsync(
			ctx,
			input.GetUpdateWorkflowOptions().GetWorkflowId(),
			input.GetUpdateWorkflowOptions().GetRunId(),
			&req,
			v1.NewAcquireLockOptions().WithUpdateWorkflowOptions(uo),
		)
		if err != nil {
			return nil, exampleOptions.convertError(err)
		}
		activity.RecordHeartbeat(ctx, handle.UpdateID())
	}

	// wait for update to complete in child goroutine
	doneCh := make(chan struct{})
	go func() {
		resp, err = handle.Get(ctx)
		close(doneCh)
	}()

	heartbeatInterval := input.GetHeartbeatInterval().AsDuration()
	if heartbeatInterval == 0 {
		heartbeatInterval = time.Minute
	}

	// heartbeat activity while waiting for workflow update to complete
	for {
		select {
		case <-time.After(heartbeatInterval):
			activity.RecordHeartbeat(ctx, handle.UpdateID())
		case <-ctx.Done():
			return nil, exampleOptions.convertError(ctx.Err())
		case <-doneCh:
			return resp, exampleOptions.convertError(err)
		}
	}
}
