package client

import "fmt"

// An EnsuredState represents some state that is needed in order for a function to execute.
type EnsuredState interface {
	// EnsureState will check if the state is active and activate it if that is not the case.
	// The boolean return value indicates if the state was activated or not.
	EnsureState() (bool, error)

	// Deactivate the state (i.e. quit, remove, disconnect)
	DeactivateState() error
}

// WithEnsuredState ensures the given state, calls the function, and then, if the state
// was activated, it is deactivated unless the retain flag is true.
func WithEnsuredState(r EnsuredState, retain bool, f func() error) (err error) {
	var wasAcquired bool
	wasAcquired, err = r.EnsureState()
	if wasAcquired && !retain {
		defer func() {
			if cerr := r.DeactivateState(); cerr != nil {
				if err == nil {
					err = cerr
				} else {
					err = fmt.Errorf("%s\n%s", err.Error(), cerr.Error())
				}
			}
		}()
	}
	if err != nil {
		return err
	}
	err = f()
	return
}

package client

import "fmt"

// An EnsuredState represents some state that is needed in order for a function to execute.
type EnsuredState interface {
	// EnsureState will check if the state is active and activate it if that is not the case.
	// The boolean return value indicates if the state was activated or not.
	EnsureState() (bool, error)

	// Deactivate the state (i.e. quit, remove, disconnect)
	DeactivateState() error
}

// WithEnsuredState ensures the given state, calls the function, and then, if the state
// was activated, it is deactivated unless the retain flag is true.
func WithEnsuredState(r EnsuredState, retain bool, f func() error) (err error) {
	var wasAcquired bool
	defer func() {
		// Always deactivate an acquired state unless there's no error
		// and a desire to retain it.
		if wasAcquired && !(err == nil && retain) {
			if cerr := r.DeactivateState(); cerr != nil {
				if err == nil {
					err = cerr
				} else {
					err = fmt.Errorf("%s\n%s", err.Error(), cerr.Error())
				}
			}
		}
	}()

	if wasAcquired, err = r.EnsureState(); err != nil {
		return err
	}
	return f()
}

Make EnsuredState rollback unconditionally on error
Make EnsuredState rollback unconditionally on error

It is very confusing if resources that got acquired (daemon, connector,
intercepts) as the result of issuing a command are retained although
the command failed. This command changes that behavior so that all
resources acquired by the command are released if the command fails.
