// Copyright 2017 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.package spec

// Package errors integrates logging with the juju errors package.
//
// This will lose the original location for logging
// However, I believe ngaut/log already loses location
// We just need to expose a logging function that allows the calldepth to be passed in
package errors

import (
	"github.com/juju/errors"
	"github.com/ngaut/log"
)

// DeferError helps capture errors from defer expressions
// The first error will be written to the second, which should be returned from the function
func DeferError(deferred error, returned *error) {
	if deferred != nil && *returned == nil {
		*returned = Trace(deferred, 2)
	}
}

// Logf logs the error and returns an annotated error (errors.Annotate)
// Just like logErr but also takes a format string and arguments for the format
// The logged message will additionally have the error appended
// This function will panic if the input is nil
func Logf(err error, msg string, formats ...interface{}) error {
	newErr := errors.NewErrWithCause(err, msg, formats...)
	newErr.SetLocation(1)
	log.Error(newErr.Message())
	return &newErr
}

// Log logs the error and returns a traced error (errors.Trace)
// This function will panic if the input is nil
func Log(err error) error {
	log.Error(err)
	// Would be better to avoid the overhead of a
	// funcdion call, type assertion, and an overriding additional SetLocation
	// Will need to patch the errors package for that
	tracedErr := errors.Trace(err).(*errors.Err)
	tracedErr.SetLocation(1)
	return tracedErr
}

// Trace is just a convenience to export the underlying errors.Trace.
// However, it will panic rather than return nil if the input is nil
//
// Optionally a second argument for the calldepth can be passed in
// By default the calldepth is 1
func Trace(err error, args ...int) error {
	tracedErr := errors.Trace(err).(*errors.Err)
	if len(args) == 1 {
		tracedErr.SetLocation(args[0])
	} else {
		tracedErr.SetLocation(1)
	}
	return tracedErr
}

// Errorf creates a new error from a format string.
// Exactly the same as errors.Errorf
//
// If you have an existing error, don't use this function or link your errors together.
// Generally use Logf.
func Errorf(format string, args ...interface{}) error {
	err := errors.NewErr(format, args...)
	err.SetLocation(1)
	return &err
}
