<commit_before>package invoicesrpc

import (
	"bytes"
	"context"
	"crypto/rand"
	"errors"
	"fmt"
	"math"
	"time"

	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcutil"
	"github.com/davecgh/go-spew/spew"

	"github.com/lightningnetwork/lnd/channeldb"
	"github.com/lightningnetwork/lnd/lntypes"
	"github.com/lightningnetwork/lnd/lnwire"
	"github.com/lightningnetwork/lnd/netann"
	"github.com/lightningnetwork/lnd/zpay32"
)

// AddInvoiceConfig contains dependencies for invoice creation.
type AddInvoiceConfig struct {
	// AddInvoice is called to add the invoice to the registry.
	AddInvoice func(invoice *channeldb.Invoice, paymentHash lntypes.Hash) (
		uint64, error)

	// IsChannelActive is used to generate valid hop hints.
	IsChannelActive func(chanID lnwire.ChannelID) bool

	// ChainParams are required to properly decode invoice payment requests
	// that are marshalled over rpc.
	ChainParams *chaincfg.Params

	// NodeSigner is an implementation of the MessageSigner implementation
	// that's backed by the identity private key of the running lnd node.
	NodeSigner *netann.NodeSigner

	// MaxPaymentMSat is the maximum allowed payment.
	MaxPaymentMSat lnwire.MilliSatoshi

	// DefaultCLTVExpiry is the default invoice expiry if no values is
	// specified.
	DefaultCLTVExpiry uint32

	// ChanDB is a global boltdb instance which is needed to access the
	// channel graph.
	ChanDB *channeldb.DB
}

// AddInvoiceData contains the required data to create a new invoice.
type AddInvoiceData struct {
	// An optional memo to attach along with the invoice. Used for record
	// keeping purposes for the invoice's creator, and will also be set in
	// the description field of the encoded payment request if the
	// description_hash field is not being used.
	Memo string

	// Deprecated. An optional cryptographic receipt of payment which is not
	// implemented.
	Receipt []byte

	// The preimage which will allow settling an incoming HTLC payable to
	// this preimage. If Preimage is set, Hash should be nil. If both
	// Preimage and Hash are nil, a random preimage is generated.
	Preimage *lntypes.Preimage

	// The hash of the preimage. If Hash is set, Preimage should be nil.
	// This condition indicates that we have a 'hold invoice' for which the
	// htlc will be accepted and held until the preimage becomes known.
	Hash *lntypes.Hash

	// The value of this invoice in satoshis.
	Value btcutil.Amount

	// Hash (SHA-256) of a description of the payment. Used if the
	// description of payment (memo) is too long to naturally fit within the
	// description field of an encoded payment request.
	DescriptionHash []byte

	// Payment request expiry time in seconds. Default is 3600 (1 hour).
	Expiry int64

	// Fallback on-chain address.
	FallbackAddr string

	// Delta to use for the time-lock of the CLTV extended to the final hop.
	CltvExpiry uint64

	// Whether this invoice should include routing hints for private
	// channels.
	Private bool
}

// AddInvoice attempts to add a new invoice to the invoice database. Any
// duplicated invoices are rejected, therefore all invoices *must* have a
// unique payment preimage.
func AddInvoice(ctx context.Context, cfg *AddInvoiceConfig,
	invoice *AddInvoiceData) (*lntypes.Hash, *channeldb.Invoice, error) {

	var (
		paymentPreimage lntypes.Preimage
		paymentHash     lntypes.Hash
	)

	switch {

	// Only either preimage or hash can be set.
	case invoice.Preimage != nil && invoice.Hash != nil:
		return nil, nil,
			errors.New("preimage and hash both set")

	// Prevent the unknown preimage magic value from being used for a
	// regular invoice. This would cause the invoice the be handled as if it
	// was a hold invoice.
	case invoice.Preimage != nil &&
		*invoice.Preimage == channeldb.UnknownPreimage:

		return nil, nil,
			fmt.Errorf("cannot use all zeroes as a preimage")

	// Prevent the hash of the unknown preimage magic value to be used for a
	// hold invoice. This would make it impossible to settle the invoice,
	// because it would still be interpreted as not having a preimage.
	case invoice.Hash != nil &&
		*invoice.Hash == channeldb.UnknownPreimage.Hash():

		return nil, nil,
			fmt.Errorf("cannot use hash of all zeroes preimage")

	// If no hash or preimage is given, generate a random preimage.
	case invoice.Preimage == nil && invoice.Hash == nil:
		if _, err := rand.Read(paymentPreimage[:]); err != nil {
			return nil, nil, err
		}
		paymentHash = paymentPreimage.Hash()

	// If just a hash is given, we create a hold invoice by setting the
	// preimage to unknown.
	case invoice.Preimage == nil && invoice.Hash != nil:
		paymentPreimage = channeldb.UnknownPreimage
		paymentHash = *invoice.Hash

	// A specific preimage was supplied. Use that for the invoice.
	case invoice.Preimage != nil && invoice.Hash == nil:
		paymentPreimage = *invoice.Preimage
		paymentHash = invoice.Preimage.Hash()
	}

	// The size of the memo, receipt and description hash attached must not
	// exceed the maximum values for either of the fields.
	if len(invoice.Memo) > channeldb.MaxMemoSize {
		return nil, nil, fmt.Errorf("memo too large: %v bytes "+
			"(maxsize=%v)", len(invoice.Memo), channeldb.MaxMemoSize)
	}
	if len(invoice.Receipt) > channeldb.MaxReceiptSize {
		return nil, nil, fmt.Errorf("receipt too large: %v bytes "+
			"(maxsize=%v)", len(invoice.Receipt), channeldb.MaxReceiptSize)
	}
	if len(invoice.DescriptionHash) > 0 && len(invoice.DescriptionHash) != 32 {
		return nil, nil, fmt.Errorf("description hash is %v bytes, must be %v",
			len(invoice.DescriptionHash), channeldb.MaxPaymentRequestSize)
	}

	// The value of the invoice must not be negative.
	if invoice.Value < 0 {
		return nil, nil, fmt.Errorf("payments of negative value "+
			"are not allowed, value is %v", invoice.Value)
	}

	amtMSat := lnwire.NewMSatFromSatoshis(invoice.Value)

	// The value of the invoice must also not exceed the current soft-limit
	// on the largest payment within the network.
	if amtMSat > cfg.MaxPaymentMSat {
		return nil, nil, fmt.Errorf("payment of %v is too large, max "+
			"payment allowed is %v", invoice.Value,
			cfg.MaxPaymentMSat.ToSatoshis(),
		)
	}

	// We also create an encoded payment request which allows the
	// caller to compactly send the invoice to the payer. We'll create a
	// list of options to be added to the encoded payment request. For now
	// we only support the required fields description/description_hash,
	// expiry, fallback address, and the amount field.
	var options []func(*zpay32.Invoice)

	// We only include the amount in the invoice if it is greater than 0.
	// By not including the amount, we enable the creation of invoices that
	// allow the payee to specify the amount of satoshis they wish to send.
	if amtMSat > 0 {
		options = append(options, zpay32.Amount(amtMSat))
	}

	// If specified, add a fallback address to the payment request.
	if len(invoice.FallbackAddr) > 0 {
		addr, err := btcutil.DecodeAddress(invoice.FallbackAddr,
			cfg.ChainParams)
		if err != nil {
			return nil, nil, fmt.Errorf("invalid fallback address: %v",
				err)
		}
		options = append(options, zpay32.FallbackAddr(addr))
	}

	// If expiry is set, specify it. If it is not provided, no expiry time
	// will be explicitly added to this payment request, which will imply
	// the default 3600 seconds.
	if invoice.Expiry > 0 {

		// We'll ensure that the specified expiry is restricted to sane
		// number of seconds. As a result, we'll reject an invoice with
		// an expiry greater than 1 year.
		maxExpiry := time.Hour * 24 * 365
		expSeconds := invoice.Expiry

		if float64(expSeconds) > maxExpiry.Seconds() {
			return nil, nil, fmt.Errorf("expiry of %v seconds "+
				"greater than max expiry of %v seconds",
				float64(expSeconds), maxExpiry.Seconds())
		}

		expiry := time.Duration(invoice.Expiry) * time.Second
		options = append(options, zpay32.Expiry(expiry))
	}

	// If the description hash is set, then we add it do the list of options.
	// If not, use the memo field as the payment request description.
	if len(invoice.DescriptionHash) > 0 {
		var descHash [32]byte
		copy(descHash[:], invoice.DescriptionHash[:])
		options = append(options, zpay32.DescriptionHash(descHash))
	} else {
		// Use the memo field as the description. If this is not set
		// this will just be an empty string.
		options = append(options, zpay32.Description(invoice.Memo))
	}

	// We'll use our current default CLTV value unless one was specified as
	// an option on the command line when creating an invoice.
	switch {
	case invoice.CltvExpiry > math.MaxUint16:
		return nil, nil, fmt.Errorf("CLTV delta of %v is too large, max "+
			"accepted is: %v", invoice.CltvExpiry, math.MaxUint16)
	case invoice.CltvExpiry != 0:
		options = append(options,
			zpay32.CLTVExpiry(invoice.CltvExpiry))
	default:
		// TODO(roasbeef): assumes set delta between versions
		defaultDelta := cfg.DefaultCLTVExpiry
		options = append(options, zpay32.CLTVExpiry(uint64(defaultDelta)))
	}

	// If we were requested to include routing hints in the invoice, then
	// we'll fetch all of our available private channels and create routing
	// hints for them.
	if invoice.Private {
		openChannels, err := cfg.ChanDB.FetchAllChannels()
		if err != nil {
			return nil, nil, fmt.Errorf("could not fetch all channels")
		}

		graph := cfg.ChanDB.ChannelGraph()

		numHints := 0
		for _, channel := range openChannels {
			// We'll restrict the number of individual route hints
			// to 20 to avoid creating overly large invoices.
			if numHints >= 20 {
				break
			}

			// Since we're only interested in our private channels,
			// we'll skip public ones.
			isPublic := channel.ChannelFlags&lnwire.FFAnnounceChannel != 0
			if isPublic {
				continue
			}

			// Make sure the counterparty has enough balance in the
			// channel for our amount. We do this in order to reduce
			// payment errors when attempting to use this channel
			// as a hint.
			chanPoint := lnwire.NewChanIDFromOutPoint(
				&channel.FundingOutpoint,
			)
			if amtMSat >= channel.LocalCommitment.RemoteBalance {
				log.Debugf("Skipping channel %v due to "+
					"not having enough remote balance",
					chanPoint)
				continue
			}

			// Make sure the channel is active.
			if !cfg.IsChannelActive(chanPoint) {
				log.Debugf("Skipping channel %v due to not "+
					"being eligible to forward payments",
					chanPoint)
				continue
			}

			// To ensure we don't leak unadvertised nodes, we'll
			// make sure our counterparty is publicly advertised
			// within the network. Otherwise, we'll end up leaking
			// information about nodes that intend to stay
			// unadvertised, like in the case of a node only having
			// private channels.
			var remotePub [33]byte
			copy(remotePub[:], channel.IdentityPub.SerializeCompressed())
			isRemoteNodePublic, err := graph.IsPublicNode(remotePub)
			if err != nil {
				log.Errorf("Unable to determine if node %x "+
					"is advertised: %v", remotePub, err)
				continue
			}

			if !isRemoteNodePublic {
				log.Debugf("Skipping channel %v due to "+
					"counterparty %x being unadvertised",
					chanPoint, remotePub)
				continue
			}

			// Fetch the policies for each end of the channel.
			chanID := channel.ShortChanID().ToUint64()
			info, p1, p2, err := graph.FetchChannelEdgesByID(chanID)
			if err != nil {
				log.Errorf("Unable to fetch the routing "+
					"policies for the edges of the channel "+
					"%v: %v", chanPoint, err)
				continue
			}

			// Now, we'll need to determine which is the correct
			// policy for HTLCs being sent from the remote node.
			var remotePolicy *channeldb.ChannelEdgePolicy
			if bytes.Equal(remotePub[:], info.NodeKey1Bytes[:]) {
				remotePolicy = p1
			} else {
				remotePolicy = p2
			}

			// If for some reason we don't yet have the edge for
			// the remote party, then we'll just skip adding this
			// channel as a routing hint.
			if remotePolicy == nil {
				continue
			}

			// Finally, create the routing hint for this channel and
			// add it to our list of route hints.
			hint := zpay32.HopHint{
				NodeID:      channel.IdentityPub,
				ChannelID:   chanID,
				FeeBaseMSat: uint32(remotePolicy.FeeBaseMSat),
				FeeProportionalMillionths: uint32(
					remotePolicy.FeeProportionalMillionths,
				),
				CLTVExpiryDelta: remotePolicy.TimeLockDelta,
			}

			// Include the route hint in our set of options that
			// will be used when creating the invoice.
			routeHint := []zpay32.HopHint{hint}
			options = append(options, zpay32.RouteHint(routeHint))

			numHints++
		}

	}

	// Create and encode the payment request as a bech32 (zpay32) string.
	creationDate := time.Now()
	payReq, err := zpay32.NewInvoice(
		cfg.ChainParams, paymentHash, creationDate, options...,
	)
	if err != nil {
		return nil, nil, err
	}

	payReqString, err := payReq.Encode(
		zpay32.MessageSigner{
			SignCompact: cfg.NodeSigner.SignDigestCompact,
		},
	)
	if err != nil {
		return nil, nil, err
	}

	newInvoice := &channeldb.Invoice{
		CreationDate:   creationDate,
		Memo:           []byte(invoice.Memo),
		Receipt:        invoice.Receipt,
		PaymentRequest: []byte(payReqString),
		FinalCltvDelta: int32(payReq.MinFinalCLTVExpiry()),
		Expiry:         payReq.Expiry(),
		Terms: channeldb.ContractTerm{
			Value:           amtMSat,
			PaymentPreimage: paymentPreimage,
		},
	}

	log.Tracef("[addinvoice] adding new invoice %v",
		newLogClosure(func() string {
			return spew.Sdump(newInvoice)
		}),
	)

	// With all sanity checks passed, write the invoice to the database.
	_, err = cfg.AddInvoice(newInvoice, paymentHash)
	if err != nil {
		return nil, nil, err
	}

	return &paymentHash, newInvoice, nil
}
<commit_msg>Fix error message for wrong size description_hash.<commit_after>package invoicesrpc

import (
	"bytes"
	"context"
	"crypto/rand"
	"errors"
	"fmt"
	"math"
	"time"

	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcutil"
	"github.com/davecgh/go-spew/spew"

	"github.com/lightningnetwork/lnd/channeldb"
	"github.com/lightningnetwork/lnd/lntypes"
	"github.com/lightningnetwork/lnd/lnwire"
	"github.com/lightningnetwork/lnd/netann"
	"github.com/lightningnetwork/lnd/zpay32"
)

// AddInvoiceConfig contains dependencies for invoice creation.
type AddInvoiceConfig struct {
	// AddInvoice is called to add the invoice to the registry.
	AddInvoice func(invoice *channeldb.Invoice, paymentHash lntypes.Hash) (
		uint64, error)

	// IsChannelActive is used to generate valid hop hints.
	IsChannelActive func(chanID lnwire.ChannelID) bool

	// ChainParams are required to properly decode invoice payment requests
	// that are marshalled over rpc.
	ChainParams *chaincfg.Params

	// NodeSigner is an implementation of the MessageSigner implementation
	// that's backed by the identity private key of the running lnd node.
	NodeSigner *netann.NodeSigner

	// MaxPaymentMSat is the maximum allowed payment.
	MaxPaymentMSat lnwire.MilliSatoshi

	// DefaultCLTVExpiry is the default invoice expiry if no values is
	// specified.
	DefaultCLTVExpiry uint32

	// ChanDB is a global boltdb instance which is needed to access the
	// channel graph.
	ChanDB *channeldb.DB
}

// AddInvoiceData contains the required data to create a new invoice.
type AddInvoiceData struct {
	// An optional memo to attach along with the invoice. Used for record
	// keeping purposes for the invoice's creator, and will also be set in
	// the description field of the encoded payment request if the
	// description_hash field is not being used.
	Memo string

	// Deprecated. An optional cryptographic receipt of payment which is not
	// implemented.
	Receipt []byte

	// The preimage which will allow settling an incoming HTLC payable to
	// this preimage. If Preimage is set, Hash should be nil. If both
	// Preimage and Hash are nil, a random preimage is generated.
	Preimage *lntypes.Preimage

	// The hash of the preimage. If Hash is set, Preimage should be nil.
	// This condition indicates that we have a 'hold invoice' for which the
	// htlc will be accepted and held until the preimage becomes known.
	Hash *lntypes.Hash

	// The value of this invoice in satoshis.
	Value btcutil.Amount

	// Hash (SHA-256) of a description of the payment. Used if the
	// description of payment (memo) is too long to naturally fit within the
	// description field of an encoded payment request.
	DescriptionHash []byte

	// Payment request expiry time in seconds. Default is 3600 (1 hour).
	Expiry int64

	// Fallback on-chain address.
	FallbackAddr string

	// Delta to use for the time-lock of the CLTV extended to the final hop.
	CltvExpiry uint64

	// Whether this invoice should include routing hints for private
	// channels.
	Private bool
}

// AddInvoice attempts to add a new invoice to the invoice database. Any
// duplicated invoices are rejected, therefore all invoices *must* have a
// unique payment preimage.
func AddInvoice(ctx context.Context, cfg *AddInvoiceConfig,
	invoice *AddInvoiceData) (*lntypes.Hash, *channeldb.Invoice, error) {

	var (
		paymentPreimage lntypes.Preimage
		paymentHash     lntypes.Hash
	)

	switch {

	// Only either preimage or hash can be set.
	case invoice.Preimage != nil && invoice.Hash != nil:
		return nil, nil,
			errors.New("preimage and hash both set")

	// Prevent the unknown preimage magic value from being used for a
	// regular invoice. This would cause the invoice the be handled as if it
	// was a hold invoice.
	case invoice.Preimage != nil &&
		*invoice.Preimage == channeldb.UnknownPreimage:

		return nil, nil,
			fmt.Errorf("cannot use all zeroes as a preimage")

	// Prevent the hash of the unknown preimage magic value to be used for a
	// hold invoice. This would make it impossible to settle the invoice,
	// because it would still be interpreted as not having a preimage.
	case invoice.Hash != nil &&
		*invoice.Hash == channeldb.UnknownPreimage.Hash():

		return nil, nil,
			fmt.Errorf("cannot use hash of all zeroes preimage")

	// If no hash or preimage is given, generate a random preimage.
	case invoice.Preimage == nil && invoice.Hash == nil:
		if _, err := rand.Read(paymentPreimage[:]); err != nil {
			return nil, nil, err
		}
		paymentHash = paymentPreimage.Hash()

	// If just a hash is given, we create a hold invoice by setting the
	// preimage to unknown.
	case invoice.Preimage == nil && invoice.Hash != nil:
		paymentPreimage = channeldb.UnknownPreimage
		paymentHash = *invoice.Hash

	// A specific preimage was supplied. Use that for the invoice.
	case invoice.Preimage != nil && invoice.Hash == nil:
		paymentPreimage = *invoice.Preimage
		paymentHash = invoice.Preimage.Hash()
	}

	// The size of the memo, receipt and description hash attached must not
	// exceed the maximum values for either of the fields.
	if len(invoice.Memo) > channeldb.MaxMemoSize {
		return nil, nil, fmt.Errorf("memo too large: %v bytes "+
			"(maxsize=%v)", len(invoice.Memo), channeldb.MaxMemoSize)
	}
	if len(invoice.Receipt) > channeldb.MaxReceiptSize {
		return nil, nil, fmt.Errorf("receipt too large: %v bytes "+
			"(maxsize=%v)", len(invoice.Receipt), channeldb.MaxReceiptSize)
	}
	if len(invoice.DescriptionHash) > 0 && len(invoice.DescriptionHash) != 32 {
		return nil, nil, fmt.Errorf("description hash is %v bytes, must be 32",
			len(invoice.DescriptionHash))
	}

	// The value of the invoice must not be negative.
	if invoice.Value < 0 {
		return nil, nil, fmt.Errorf("payments of negative value "+
			"are not allowed, value is %v", invoice.Value)
	}

	amtMSat := lnwire.NewMSatFromSatoshis(invoice.Value)

	// The value of the invoice must also not exceed the current soft-limit
	// on the largest payment within the network.
	if amtMSat > cfg.MaxPaymentMSat {
		return nil, nil, fmt.Errorf("payment of %v is too large, max "+
			"payment allowed is %v", invoice.Value,
			cfg.MaxPaymentMSat.ToSatoshis(),
		)
	}

	// We also create an encoded payment request which allows the
	// caller to compactly send the invoice to the payer. We'll create a
	// list of options to be added to the encoded payment request. For now
	// we only support the required fields description/description_hash,
	// expiry, fallback address, and the amount field.
	var options []func(*zpay32.Invoice)

	// We only include the amount in the invoice if it is greater than 0.
	// By not including the amount, we enable the creation of invoices that
	// allow the payee to specify the amount of satoshis they wish to send.
	if amtMSat > 0 {
		options = append(options, zpay32.Amount(amtMSat))
	}

	// If specified, add a fallback address to the payment request.
	if len(invoice.FallbackAddr) > 0 {
		addr, err := btcutil.DecodeAddress(invoice.FallbackAddr,
			cfg.ChainParams)
		if err != nil {
			return nil, nil, fmt.Errorf("invalid fallback address: %v",
				err)
		}
		options = append(options, zpay32.FallbackAddr(addr))
	}

	// If expiry is set, specify it. If it is not provided, no expiry time
	// will be explicitly added to this payment request, which will imply
	// the default 3600 seconds.
	if invoice.Expiry > 0 {

		// We'll ensure that the specified expiry is restricted to sane
		// number of seconds. As a result, we'll reject an invoice with
		// an expiry greater than 1 year.
		maxExpiry := time.Hour * 24 * 365
		expSeconds := invoice.Expiry

		if float64(expSeconds) > maxExpiry.Seconds() {
			return nil, nil, fmt.Errorf("expiry of %v seconds "+
				"greater than max expiry of %v seconds",
				float64(expSeconds), maxExpiry.Seconds())
		}

		expiry := time.Duration(invoice.Expiry) * time.Second
		options = append(options, zpay32.Expiry(expiry))
	}

	// If the description hash is set, then we add it do the list of options.
	// If not, use the memo field as the payment request description.
	if len(invoice.DescriptionHash) > 0 {
		var descHash [32]byte
		copy(descHash[:], invoice.DescriptionHash[:])
		options = append(options, zpay32.DescriptionHash(descHash))
	} else {
		// Use the memo field as the description. If this is not set
		// this will just be an empty string.
		options = append(options, zpay32.Description(invoice.Memo))
	}

	// We'll use our current default CLTV value unless one was specified as
	// an option on the command line when creating an invoice.
	switch {
	case invoice.CltvExpiry > math.MaxUint16:
		return nil, nil, fmt.Errorf("CLTV delta of %v is too large, max "+
			"accepted is: %v", invoice.CltvExpiry, math.MaxUint16)
	case invoice.CltvExpiry != 0:
		options = append(options,
			zpay32.CLTVExpiry(invoice.CltvExpiry))
	default:
		// TODO(roasbeef): assumes set delta between versions
		defaultDelta := cfg.DefaultCLTVExpiry
		options = append(options, zpay32.CLTVExpiry(uint64(defaultDelta)))
	}

	// If we were requested to include routing hints in the invoice, then
	// we'll fetch all of our available private channels and create routing
	// hints for them.
	if invoice.Private {
		openChannels, err := cfg.ChanDB.FetchAllChannels()
		if err != nil {
			return nil, nil, fmt.Errorf("could not fetch all channels")
		}

		graph := cfg.ChanDB.ChannelGraph()

		numHints := 0
		for _, channel := range openChannels {
			// We'll restrict the number of individual route hints
			// to 20 to avoid creating overly large invoices.
			if numHints >= 20 {
				break
			}

			// Since we're only interested in our private channels,
			// we'll skip public ones.
			isPublic := channel.ChannelFlags&lnwire.FFAnnounceChannel != 0
			if isPublic {
				continue
			}

			// Make sure the counterparty has enough balance in the
			// channel for our amount. We do this in order to reduce
			// payment errors when attempting to use this channel
			// as a hint.
			chanPoint := lnwire.NewChanIDFromOutPoint(
				&channel.FundingOutpoint,
			)
			if amtMSat >= channel.LocalCommitment.RemoteBalance {
				log.Debugf("Skipping channel %v due to "+
					"not having enough remote balance",
					chanPoint)
				continue
			}

			// Make sure the channel is active.
			if !cfg.IsChannelActive(chanPoint) {
				log.Debugf("Skipping channel %v due to not "+
					"being eligible to forward payments",
					chanPoint)
				continue
			}

			// To ensure we don't leak unadvertised nodes, we'll
			// make sure our counterparty is publicly advertised
			// within the network. Otherwise, we'll end up leaking
			// information about nodes that intend to stay
			// unadvertised, like in the case of a node only having
			// private channels.
			var remotePub [33]byte
			copy(remotePub[:], channel.IdentityPub.SerializeCompressed())
			isRemoteNodePublic, err := graph.IsPublicNode(remotePub)
			if err != nil {
				log.Errorf("Unable to determine if node %x "+
					"is advertised: %v", remotePub, err)
				continue
			}

			if !isRemoteNodePublic {
				log.Debugf("Skipping channel %v due to "+
					"counterparty %x being unadvertised",
					chanPoint, remotePub)
				continue
			}

			// Fetch the policies for each end of the channel.
			chanID := channel.ShortChanID().ToUint64()
			info, p1, p2, err := graph.FetchChannelEdgesByID(chanID)
			if err != nil {
				log.Errorf("Unable to fetch the routing "+
					"policies for the edges of the channel "+
					"%v: %v", chanPoint, err)
				continue
			}

			// Now, we'll need to determine which is the correct
			// policy for HTLCs being sent from the remote node.
			var remotePolicy *channeldb.ChannelEdgePolicy
			if bytes.Equal(remotePub[:], info.NodeKey1Bytes[:]) {
				remotePolicy = p1
			} else {
				remotePolicy = p2
			}

			// If for some reason we don't yet have the edge for
			// the remote party, then we'll just skip adding this
			// channel as a routing hint.
			if remotePolicy == nil {
				continue
			}

			// Finally, create the routing hint for this channel and
			// add it to our list of route hints.
			hint := zpay32.HopHint{
				NodeID:      channel.IdentityPub,
				ChannelID:   chanID,
				FeeBaseMSat: uint32(remotePolicy.FeeBaseMSat),
				FeeProportionalMillionths: uint32(
					remotePolicy.FeeProportionalMillionths,
				),
				CLTVExpiryDelta: remotePolicy.TimeLockDelta,
			}

			// Include the route hint in our set of options that
			// will be used when creating the invoice.
			routeHint := []zpay32.HopHint{hint}
			options = append(options, zpay32.RouteHint(routeHint))

			numHints++
		}

	}

	// Create and encode the payment request as a bech32 (zpay32) string.
	creationDate := time.Now()
	payReq, err := zpay32.NewInvoice(
		cfg.ChainParams, paymentHash, creationDate, options...,
	)
	if err != nil {
		return nil, nil, err
	}

	payReqString, err := payReq.Encode(
		zpay32.MessageSigner{
			SignCompact: cfg.NodeSigner.SignDigestCompact,
		},
	)
	if err != nil {
		return nil, nil, err
	}

	newInvoice := &channeldb.Invoice{
		CreationDate:   creationDate,
		Memo:           []byte(invoice.Memo),
		Receipt:        invoice.Receipt,
		PaymentRequest: []byte(payReqString),
		FinalCltvDelta: int32(payReq.MinFinalCLTVExpiry()),
		Expiry:         payReq.Expiry(),
		Terms: channeldb.ContractTerm{
			Value:           amtMSat,
			PaymentPreimage: paymentPreimage,
		},
	}

	log.Tracef("[addinvoice] adding new invoice %v",
		newLogClosure(func() string {
			return spew.Sdump(newInvoice)
		}),
	)

	// With all sanity checks passed, write the invoice to the database.
	_, err = cfg.AddInvoice(newInvoice, paymentHash)
	if err != nil {
		return nil, nil, err
	}

	return &paymentHash, newInvoice, nil
}
<|endoftext|>


<commit_before>package serkis

import (
	"html/template"
	"path"
)

var (
	editTemplate    = genTemplate("edit", editTemplateHTML)
	showTemplate    = genTemplate("show", showTemplateHTML)
	rawShowTemplate = genTemplate("rawShow", rawShowTemplateHTML)
	newTemplate     = genTemplate("new", newTemplateHTML)
)

const editTemplateHTML = `
View this file

<form method="POST" action="/edit/{{ .Fpath }}">
	<textarea cols="80" rows="30" name="contents" accept-charset="UTF-8">{{ .Fcontents }}</textarea>

	<input name="message" type="text" value="Updating file..."/>

	<input type="submit" value="Commit" />
</form>
`

const showTemplateHTML = `
Edit this file
Create a new file
Back
Share

{{ .UnescapedFcontents }}
`

const rawShowTemplateHTML = `
{{ .UnescapedFcontents }}
`

const newTemplateHTML = `
<form method="POST" action="/new">
	<input name="path" type="text"/>

	<input type="submit" value="Create file" />
</form>
`

type TemplateContents struct {
	Fpath     string
	Fcontents string
}

func (tc TemplateContents) ShareURL() template.HTML {
	s := Share{Fpath: tc.Fpath}

	secret, _ := s.Secret(cryptoKey)

	return template.HTML("share/" + secret)
}

func (tc TemplateContents) UnescapedFcontents() template.HTML {
	return template.HTML(tc.Fcontents)
}

func (tc TemplateContents) BackURL() string {
	url := path.Dir(tc.Fpath)

	if path.Base(tc.Fpath) == "README.md" {
		url = path.Dir(url)
	}

	return url
}

func genTemplate(name, html string) *template.Template {
	return template.Must(template.New(name).Parse(html))
}
<commit_msg>Add basic styling<commit_after>package serkis

import (
	"html/template"
	"path"
)

var (
	editTemplate    = genTemplate("edit", editTemplateHTML)
	showTemplate    = genTemplate("show", showTemplateHTML)
	rawShowTemplate = genTemplate("rawShow", rawShowTemplateHTML)
	newTemplate     = genTemplate("new", newTemplateHTML)
)

const style = `
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,600,600italic,300,300italic,400italic,700,700italic,800,800italic' rel='stylesheet' type='text/css'>

`

const editTemplateHTML = style + `
View this file

<form method="POST" action="/edit/{{ .Fpath }}">
	<textarea cols="80" rows="30" name="contents" accept-charset="UTF-8">{{ .Fcontents }}</textarea>

	<input name="message" type="text" value="Updating file..."/>

	<input type="submit" value="Commit" />
</form>
`

const showTemplateHTML = style + `
Edit this file
Create a new file
Back
Share

{{ .UnescapedFcontents }}
`

const rawShowTemplateHTML = style + `
{{ .UnescapedFcontents }}
`

const newTemplateHTML = style + `
<form method="POST" action="/new">
	<input name="path" type="text"/>

	<input type="submit" value="Create file" />
</form>
`

type TemplateContents struct {
	Fpath     string
	Fcontents string
}

func (tc TemplateContents) ShareURL() template.HTML {
	s := Share{Fpath: tc.Fpath}

	secret, _ := s.Secret(cryptoKey)

	return template.HTML("share/" + secret)
}

func (tc TemplateContents) UnescapedFcontents() template.HTML {
	return template.HTML(tc.Fcontents)
}

func (tc TemplateContents) BackURL() string {
	url := path.Dir(tc.Fpath)

	if path.Base(tc.Fpath) == "README.md" {
		url = path.Dir(url)
	}

	return url
}

func genTemplate(name, html string) *template.Template {
	return template.Must(template.New(name).Parse(html))
}
<|endoftext|>


<commit_before>package ss13_se

import (
	"html/template"
)

func loadTemplates() (map[string]*template.Template, error) {
	tmpls := make(map[string]*template.Template)
	for name, src := range tmplList {
		t, err := parseTemplate(tmplBase, src)
		if err != nil {
			return nil, err
		}
		tmpls[name] = t
	}
	return tmpls, nil
}

func parseTemplate(src ...string) (*template.Template, error) {
	var err error
	t := template.New("*")
	for _, s := range src {
		t, err = t.Parse(s)
		if err != nil {
			return nil, err
		}
	}
	return t, nil
}

// Using the awesome style from http://bettermotherfuckingwebsite.com/

const tmplBase string = `<!DOCTYPE html>

                <meta charset="utf-8">
                <title>
                        {{block "title" .}}NO TITLE{{end}} | ss13.se
                </title>

			ss13.se
			Global stats
			Latest news

Last updated: {{.Hub.LastUpdated}}

                        {{block "body" .}}NO BODY{{end}}

					Source code at
					Github

				{{/* TODO: not sure about the copyright stuff when fetching ext. data */}}
				Copyright © 2017 A. Svensson

					Raw data from
					Byond

`

var tmplList = map[string]string{
	"index": `{{define "title"}}Index{{end}}
{{define "body"}}

Servers

		Players
		Server

	{{range .Servers}}

			{{.Players}}
			{{.Title}}

	{{else}}

0	Sorry, no servers yet!

	{{end}}

{{end}}
`,

	"news": `{{define "title"}}News{{end}}
{{define "body"}}

Latest mentions on reddit

{{range .Reddit}}

{{.Title}}

{{end}}

{{end}}
`,

	"server": `{{define "title"}}{{.Server.Title}}{{end}}
{{define "body"}}

{{.Server.Title}}

{{if .Server.SiteURL}}
	Website
{{end}}

{{if .Server.ByondURL}}
	Join game
{{end}}

Current players: {{.Server.Players}}

Daily History

Weekly History

Average per day

Average per hour

{{end}}
`,
}
<commit_msg>Removes the annoying underlining of links<commit_after>package ss13_se

import (
	"html/template"
)

func loadTemplates() (map[string]*template.Template, error) {
	tmpls := make(map[string]*template.Template)
	for name, src := range tmplList {
		t, err := parseTemplate(tmplBase, src)
		if err != nil {
			return nil, err
		}
		tmpls[name] = t
	}
	return tmpls, nil
}

func parseTemplate(src ...string) (*template.Template, error) {
	var err error
	t := template.New("*")
	for _, s := range src {
		t, err = t.Parse(s)
		if err != nil {
			return nil, err
		}
	}
	return t, nil
}

// Using the awesome style from http://bettermotherfuckingwebsite.com/

const tmplBase string = `<!DOCTYPE html>

                <meta charset="utf-8">
                <title>
                        {{block "title" .}}NO TITLE{{end}} | ss13.se
                </title>

			ss13.se
			Global stats
			Latest news

Last updated: {{.Hub.LastUpdated}}

                        {{block "body" .}}NO BODY{{end}}

					Source code at
					Github

				{{/* TODO: not sure about the copyright stuff when fetching ext. data */}}
				Copyright © 2017 A. Svensson

					Raw data from
					Byond

`

var tmplList = map[string]string{
	"index": `{{define "title"}}Index{{end}}
{{define "body"}}

Servers

		Players
		Server

	{{range .Servers}}

			{{.Players}}
			{{.Title}}

	{{else}}

0	Sorry, no servers yet!

	{{end}}

{{end}}
`,

	"news": `{{define "title"}}News{{end}}
{{define "body"}}

Latest mentions on reddit

{{range .Reddit}}

{{.Title}}

{{end}}

{{end}}
`,

	"server": `{{define "title"}}{{.Server.Title}}{{end}}
{{define "body"}}

{{.Server.Title}}

{{if .Server.SiteURL}}
	Website
{{end}}

{{if .Server.ByondURL}}
	Join game
{{end}}

Current players: {{.Server.Players}}

Daily History

Weekly History

Average per day

Average per hour

{{end}}
`,
}
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
//
// 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
//
//     https://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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package pbinfo provides convenience types for looking up protobuf elements.
package pbinfo

import (
	"fmt"
	"strings"
	"unicode"

	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/protoc-gen-go/descriptor"
	"github.com/googleapis/gapic-generator-go/internal/errors"
)

// ProtoType represents a type in protobuf descriptors.
// It is an interface implemented by DescriptorProto and EnumDescriptorProto.
type ProtoType interface {
	proto.Message
	GetName() string
}

// Info provides lookup tables for various protobuf properties.
// For example, we can look up a type by name without iterating the entire
// descriptor.
type Info struct {
	// Maps services and messages to the file containing them,
	// so we can figure out the import.
	ParentFile map[proto.Message]*descriptor.FileDescriptorProto

	// NOTE(pongad): ParentElement and sub-types are only used in samples.
	// They are added in the shared package because they share a lot of similarities
	// with things that are already here. Maybe revisit this in the future?

	// Maps a protobuf element to the enclosing scope.
	// If enum E is defined in message M which is in file F,
	// ParentElement[E]=M, ParentElement[M]=nil, and ParentFile[M]=F
	ParentElement map[ProtoType]ProtoType

	// Maps type names to their messages.
	Type map[string]ProtoType

	// Maps service names to their descriptors.
	Serv map[string]*descriptor.ServiceDescriptorProto
}

// Of creates Info from given protobuf files.
func Of(files []*descriptor.FileDescriptorProto) Info {
	info := Info{
		ParentFile:    map[proto.Message]*descriptor.FileDescriptorProto{},
		ParentElement: map[ProtoType]ProtoType{},
		Type:          map[string]ProtoType{},
		Serv:          map[string]*descriptor.ServiceDescriptorProto{},
	}

	for _, f := range files {
		// ParentFile
		for _, m := range f.MessageType {
			info.ParentFile[m] = f
		}
		for _, e := range f.EnumType {
			info.ParentFile[e] = f
		}
		for _, s := range f.Service {
			info.ParentFile[s] = f
			for _, m := range s.Method {
				info.ParentFile[m] = f
				info.ParentElement[m] = s
			}
		}

		// Type
		for _, m := range f.MessageType {
			// In descriptors, putting the dot in front means the name is fully-qualified.
			addMessage(info.Type, info.ParentElement, "."+f.GetPackage(), m, nil)
		}
		for _, e := range f.EnumType {
			info.Type["."+f.GetPackage()+"."+e.GetName()] = e
		}

		// Serv
		for _, s := range f.Service {
			fullyQualifiedName := fmt.Sprintf(".%s.%s", f.GetPackage(), s.GetName())
			info.Serv[fullyQualifiedName] = s
		}
	}

	return info
}

func addMessage(typMap map[string]ProtoType, parentMap map[ProtoType]ProtoType, prefix string, msg, parentMsg *descriptor.DescriptorProto) {
	fullName := prefix + "." + msg.GetName()
	typMap[fullName] = msg
	if parentMsg != nil {
		parentMap[msg] = parentMsg
	}

	for _, subMsg := range msg.NestedType {
		addMessage(typMap, parentMap, fullName, subMsg, msg)
	}

	for _, subEnum := range msg.EnumType {
		typMap[fullName+"."+subEnum.GetName()] = subEnum
		parentMap[subEnum] = msg
	}

	for _, field := range msg.GetField() {
		parentMap[field] = msg
	}
}

// ImportSpec represents a Go module import path and an optional alias.
type ImportSpec struct {
	Name, Path string
}

// NameSpec reports the name and ImportSpec of e.
//
// The reported name is the same with how protoc-gen-go refers to e.
// E.g. if type B is nested under A, then the name of type B is "A_B".
func (in *Info) NameSpec(e ProtoType) (string, ImportSpec, error) {
	appendpb := func(n string) string {
		if !strings.HasSuffix(n, "pb") {
			n += "pb"
		}
		return n
	}

	topLvl := e
	var nameParts []string
	for e2 := e; e2 != nil; e2 = in.ParentElement[e2] {
		topLvl = e2
		nameParts = append(nameParts, e2.GetName())
	}
	for i, l := 0, len(nameParts); i < l/2; i++ {
		nameParts[i], nameParts[l-i-1] = nameParts[l-i-1], nameParts[i]
	}
	name := strings.Join(nameParts, "_")

	var eTxt interface{} = e
	if et, ok := eTxt.(interface{ GetName() string }); ok {
		eTxt = et.GetName()
	}

	fdesc := in.ParentFile[topLvl]
	if fdesc == nil {
		return "", ImportSpec{}, errors.E(nil, "can't determine import path for %v; can't find parent file", eTxt)
	}

	pkg := fdesc.GetOptions().GetGoPackage()
	if pkg == "" {
		return "", ImportSpec{}, errors.E(nil, "can't determine import path for %v, file %q missing `option go_package`", eTxt, fdesc.GetName())
	}

	if p := strings.IndexByte(pkg, ';'); p >= 0 {
		return name, ImportSpec{Path: pkg[:p], Name: appendpb(pkg[p+1:])}, nil
	}

	for {
		p := strings.LastIndexByte(pkg, '/')
		if p < 0 {
			return name, ImportSpec{Path: pkg, Name: appendpb(pkg)}, nil
		}
		elem := pkg[p+1:]
		if len(elem) >= 2 && elem[0] == 'v' && elem[1] >= '0' && elem[1] <= '9' {
			// It's a version number; skip so we get a more meaningful name
			pkg = pkg[:p]
			continue
		}
		return name, ImportSpec{Path: pkg, Name: appendpb(elem)}, nil
	}
}

// ImportSpec reports the ImportSpec for package containing protobuf element e.
// Deprecated: Use NameSpec instead.
func (in *Info) ImportSpec(e ProtoType) (ImportSpec, error) {
	_, imp, err := in.NameSpec(e)
	return imp, err
}

// ReduceServName removes redundant components from the service name.
// For example, FooServiceV2 -> Foo.
// The returned name is used as part of longer names, like FooClient.
// If the package name and the service name is the same,
// ReduceServName returns empty string, so we get foo.Client instead of foo.FooClient.
func ReduceServName(svc, pkg string) string {
	// remove trailing version
	if p := strings.LastIndexByte(svc, 'V'); p >= 0 {
		isVer := true
		for _, r := range svc[p+1:] {
			if !unicode.IsDigit(r) {
				isVer = false
				break
			}
		}
		if isVer {
			svc = svc[:p]
		}
	}

	svc = strings.TrimSuffix(svc, "Service")
	if strings.EqualFold(svc, pkg) {
		svc = ""
	}

	// This is a special case for IAM and should not be
	// extended to support any new API name containing
	// an acronym.
	//
	// In order to avoid a breaking change for IAM
	// clients, we must keep consistent identifier casing.
	if strings.Contains(svc, "IAM") {
		svc = strings.ReplaceAll(svc, "IAM", "Iam")
	}

	return svc
}
<commit_msg>chore(pbinfo): switch to accessor methods (#864)<commit_after>// Copyright 2018 Google LLC
//
// 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
//
//     https://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,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package pbinfo provides convenience types for looking up protobuf elements.
package pbinfo

import (
	"fmt"
	"strings"
	"unicode"

	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/protoc-gen-go/descriptor"
	"github.com/googleapis/gapic-generator-go/internal/errors"
)

// ProtoType represents a type in protobuf descriptors.
// It is an interface implemented by DescriptorProto and EnumDescriptorProto.
type ProtoType interface {
	proto.Message
	GetName() string
}

// Info provides lookup tables for various protobuf properties.
// For example, we can look up a type by name without iterating the entire
// descriptor.
type Info struct {
	// Maps services and messages to the file containing them,
	// so we can figure out the import.
	ParentFile map[proto.Message]*descriptor.FileDescriptorProto

	// NOTE(pongad): ParentElement and sub-types are only used in samples.
	// They are added in the shared package because they share a lot of similarities
	// with things that are already here. Maybe revisit this in the future?

	// Maps a protobuf element to the enclosing scope.
	// If enum E is defined in message M which is in file F,
	// ParentElement[E]=M, ParentElement[M]=nil, and ParentFile[M]=F
	ParentElement map[ProtoType]ProtoType

	// Maps type names to their messages.
	Type map[string]ProtoType

	// Maps service names to their descriptors.
	Serv map[string]*descriptor.ServiceDescriptorProto
}

// Of creates Info from given protobuf files.
func Of(files []*descriptor.FileDescriptorProto) Info {
	info := Info{
		ParentFile:    map[proto.Message]*descriptor.FileDescriptorProto{},
		ParentElement: map[ProtoType]ProtoType{},
		Type:          map[string]ProtoType{},
		Serv:          map[string]*descriptor.ServiceDescriptorProto{},
	}

	for _, f := range files {
		// ParentFile
		for _, m := range f.GetMessageType() {
			info.ParentFile[m] = f
		}
		for _, e := range f.GetEnumType() {
			info.ParentFile[e] = f
		}
		for _, s := range f.GetService() {
			info.ParentFile[s] = f
			for _, m := range s.GetMethod() {
				info.ParentFile[m] = f
				info.ParentElement[m] = s
			}
		}

		// Type
		for _, m := range f.GetMessageType() {
			// In descriptors, putting the dot in front means the name is fully-qualified.
			addMessage(info.Type, info.ParentElement, "."+f.GetPackage(), m, nil)
		}
		for _, e := range f.GetEnumType() {
			info.Type["."+f.GetPackage()+"."+e.GetName()] = e
		}

		// Serv
		for _, s := range f.GetService() {
			fullyQualifiedName := fmt.Sprintf(".%s.%s", f.GetPackage(), s.GetName())
			info.Serv[fullyQualifiedName] = s
		}
	}

	return info
}

func addMessage(typMap map[string]ProtoType, parentMap map[ProtoType]ProtoType, prefix string, msg, parentMsg *descriptor.DescriptorProto) {
	fullName := prefix + "." + msg.GetName()
	typMap[fullName] = msg
	if parentMsg != nil {
		parentMap[msg] = parentMsg
	}

	for _, subMsg := range msg.GetNestedType() {
		addMessage(typMap, parentMap, fullName, subMsg, msg)
	}

	for _, subEnum := range msg.GetEnumType() {
		typMap[fullName+"."+subEnum.GetName()] = subEnum
		parentMap[subEnum] = msg
	}

	for _, field := range msg.GetField() {
		parentMap[field] = msg
	}
}

// ImportSpec represents a Go module import path and an optional alias.
type ImportSpec struct {
	Name, Path string
}

// NameSpec reports the name and ImportSpec of e.
//
// The reported name is the same with how protoc-gen-go refers to e.
// E.g. if type B is nested under A, then the name of type B is "A_B".
func (in *Info) NameSpec(e ProtoType) (string, ImportSpec, error) {
	appendpb := func(n string) string {
		if !strings.HasSuffix(n, "pb") {
			n += "pb"
		}
		return n
	}

	topLvl := e
	var nameParts []string
	for e2 := e; e2 != nil; e2 = in.ParentElement[e2] {
		topLvl = e2
		nameParts = append(nameParts, e2.GetName())
	}
	for i, l := 0, len(nameParts); i < l/2; i++ {
		nameParts[i], nameParts[l-i-1] = nameParts[l-i-1], nameParts[i]
	}
	name := strings.Join(nameParts, "_")

	var eTxt interface{} = e
	if et, ok := eTxt.(interface{ GetName() string }); ok {
		eTxt = et.GetName()
	}

	fdesc := in.ParentFile[topLvl]
	if fdesc == nil {
		return "", ImportSpec{}, errors.E(nil, "can't determine import path for %v; can't find parent file", eTxt)
	}

	pkg := fdesc.GetOptions().GetGoPackage()
	if pkg == "" {
		return "", ImportSpec{}, errors.E(nil, "can't determine import path for %v, file %q missing `option go_package`", eTxt, fdesc.GetName())
	}

	if p := strings.IndexByte(pkg, ';'); p >= 0 {
		return name, ImportSpec{Path: pkg[:p], Name: appendpb(pkg[p+1:])}, nil
	}

	for {
		p := strings.LastIndexByte(pkg, '/')
		if p < 0 {
			return name, ImportSpec{Path: pkg, Name: appendpb(pkg)}, nil
		}
		elem := pkg[p+1:]
		if len(elem) >= 2 && elem[0] == 'v' && elem[1] >= '0' && elem[1] <= '9' {
			// It's a version number; skip so we get a more meaningful name
			pkg = pkg[:p]
			continue
		}
		return name, ImportSpec{Path: pkg, Name: appendpb(elem)}, nil
	}
}

// ImportSpec reports the ImportSpec for package containing protobuf element e.
// Deprecated: Use NameSpec instead.
func (in *Info) ImportSpec(e ProtoType) (ImportSpec, error) {
	_, imp, err := in.NameSpec(e)
	return imp, err
}

// ReduceServName removes redundant components from the service name.
// For example, FooServiceV2 -> Foo.
// The returned name is used as part of longer names, like FooClient.
// If the package name and the service name is the same,
// ReduceServName returns empty string, so we get foo.Client instead of foo.FooClient.
func ReduceServName(svc, pkg string) string {
	// remove trailing version
	if p := strings.LastIndexByte(svc, 'V'); p >= 0 {
		isVer := true
		for _, r := range svc[p+1:] {
			if !unicode.IsDigit(r) {
				isVer = false
				break
			}
		}
		if isVer {
			svc = svc[:p]
		}
	}

	svc = strings.TrimSuffix(svc, "Service")
	if strings.EqualFold(svc, pkg) {
		svc = ""
	}

	// This is a special case for IAM and should not be
	// extended to support any new API name containing
	// an acronym.
	//
	// In order to avoid a breaking change for IAM
	// clients, we must keep consistent identifier casing.
	if strings.Contains(svc, "IAM") {
		svc = strings.ReplaceAll(svc, "IAM", "Iam")
	}

	return svc
}
<|endoftext|>


<commit_before>package contnet

import (
	"log"
	"math"
	"sort"
	"strings"
)

type keywordExtractor struct{}

var ContentKeywordExtractor = keywordExtractor{}

type ContentKeywordExtractionInput struct {
	Title       string
	Description string
	Comments    []string
}

func (keywordExtractor keywordExtractor) Extract(input *ContentKeywordExtractionInput, maxKeywords int) []string {
	joinedComments := strings.Join(input.Comments, " ")

	// get histograms for every element of the input
	titleHistogram, titleCache := __stringHistogram(&input.Title)
	descriptionHistogram, descriptionCache := __stringHistogram(&input.Description)
	commentHistogram, commentCache := __stringHistogram(&joinedComments)

	// now we have histograms (sorted in descending order by word occurrence count) for all elements of the input
	var descriptionAvg, commentAvg float64
	titleHistogram, titleCache, _ = __removeOutliers(titleHistogram, titleCache)
	descriptionHistogram, descriptionCache, descriptionAvg = __removeOutliers(descriptionHistogram, descriptionCache)
	commentHistogram, commentCache, commentAvg = __removeOutliers(commentHistogram, commentCache)

	// now all common or rare words have been removed. Remaining words are all candidates for keyword status.
	// if a word occurs in: title, description and comment it is the strongest candidate
	// combination of a two is still good
	// only in one is pretty bad but still can be keyword
	// join maps
	global := map[string]float64{}
	for tK, tV := range titleCache {
		global[tK] = float64(tV) + descriptionAvg + commentAvg
	}

	for dK, dV := range descriptionCache {
		global[dK] += float64(dV) + commentAvg
	}

	for cK, cV := range commentCache {
		global[cK] += float64(cV)
	}

	// extract array and sort
	globalArr := []*WordCount{}
	for k, v := range global {
		globalArr = append(globalArr, &WordCount{Word: k, Count: v})
	}

	WordCountBy(wordCountCriteria).Sort(globalArr)

	lb := maxKeywords
	if maxKeywords > len(globalArr) {
		lb = len(globalArr) - 1
	}

	out := []string{}

	for i := 0; i < lb; i++ {
		out = append(out, globalArr[i].Word)
	}

	return out
}

func __removeOutliers(histogram []*WordCount, cache map[string]float64) ([]*WordCount, map[string]float64, float64) {
	countSum := 0.0
	for i := 0; i < len(histogram); i++ {
		countSum += histogram[i].Count
	}

	meanValue := countSum / float64(len(histogram))

	sqDiffSum := 0.0
	for i := 0; i < len(histogram); i++ {
		sqDiffSum += math.Pow(histogram[i].Count-meanValue, 2.0)
	}

	variance := sqDiffSum / float64(len(histogram))

	std := math.Sqrt(variance)

	out := []*WordCount{}
	outCache := map[string]float64{}

	lb := meanValue - std
	ub := meanValue + std

	tmpSum := 0.0
	for i := 0; i < len(histogram); i++ {
		if histogram[i].Count >= lb && histogram[i].Count <= ub && !__simplewords.is(histogram[i].Word) {
			out = append(out, histogram[i])
			outCache[histogram[i].Word] = histogram[i].Count
			tmpSum += histogram[i].Count
		} else {
			log.Printf("Removing word %s occ = %f", histogram[i].Word, histogram[i].Count)
		}
	}

	return out, outCache, tmpSum / float64(len(out))
}

type runes map[rune]bool
type simplewords map[string]bool

func (r runes) contain(rune rune) bool {
	_, contains := r[rune]
	return contains
}

func (sw simplewords) is(word string) bool {
	_, contains := sw[word]
	return contains
}

var __stopRunes = runes{
	' ':  true,
	'.':  true,
	'\n': true,
	'(':  true,
	')':  true,
	'"':  true,
	',':  true,
	'*':  true,
}

var __simplewords = simplewords{
	"": true,
}

func __stringHistogram(str *string) ([]*WordCount, map[string]float64) {
	words := strings.FieldsFunc(*str, func(r rune) bool {
		return __stopRunes.contain(r)
	})

	cache := map[string]float64{}

	for i := 0; i < len(words); i++ {
		cache[words[i]]++
	}

	out := []*WordCount{}

	for k, v := range cache {
		out = append(out, &WordCount{Word: k, Count: v})
	}

	WordCountBy(wordCountCriteria).Sort(out)

	return out, cache
}

type WordCount struct {
	Word  string
	Count float64
}

var wordCountCriteria = func(c1, c2 *WordCount) bool {
	return c1.Count > c2.Count
}

// function that defines ordering between content objects
type WordCountBy func(c1, c2 *WordCount) bool

// method on the function type, sorts the argument slice according  to the function
func (wordCountBy WordCountBy) Sort(wordCounts []*WordCount) {
	ws := &wordCountSorter{
		wordCounts:  wordCounts,
		wordCountBy: wordCountBy,
	}
	sort.Sort(ws)
}

type wordCountSorter struct {
	wordCounts  []*WordCount
	wordCountBy func(c1, c2 *WordCount) bool
}

// Len is part of sort.Interface.
func (ws *wordCountSorter) Len() int {
	return len(ws.wordCounts)
}

// Swap is part of sort.Interface.
func (ws *wordCountSorter) Swap(i, j int) {
	ws.wordCounts[i], ws.wordCounts[j] = ws.wordCounts[j], ws.wordCounts[i]
}

// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
func (ws *wordCountSorter) Less(i, j int) bool {
	return ws.wordCountBy(ws.wordCounts[i], ws.wordCounts[j])
}
<commit_msg>Input doesn't have to be in lowercase<commit_after>package contnet

import (
	"log"
	"math"
	"sort"
	"strings"
)

type keywordExtractor struct{}

var ContentKeywordExtractor = keywordExtractor{}

type ContentKeywordExtractionInput struct {
	Title       string
	Description string
	Comments    []string
}

func (keywordExtractor keywordExtractor) Extract(input *ContentKeywordExtractionInput, maxKeywords int) []string {
	input.Title = strings.ToLower(input.Title)
	input.Description = strings.ToLower(input.Description)
	for i := 0; i < len(input.Comments); i++ {
		input.Comments[i] = strings.ToLower(input.Comments[i])
	}

	joinedComments := strings.Join(input.Comments, " ")

	// get histograms for every element of the input
	titleHistogram, titleCache := __stringHistogram(&input.Title)
	descriptionHistogram, descriptionCache := __stringHistogram(&input.Description)
	commentHistogram, commentCache := __stringHistogram(&joinedComments)

	// now we have histograms (sorted in descending order by word occurrence count) for all elements of the input
	var descriptionAvg, commentAvg float64
	titleHistogram, titleCache, _ = __removeOutliers(titleHistogram, titleCache)
	descriptionHistogram, descriptionCache, descriptionAvg = __removeOutliers(descriptionHistogram, descriptionCache)
	commentHistogram, commentCache, commentAvg = __removeOutliers(commentHistogram, commentCache)

	// now all common or rare words have been removed. Remaining words are all candidates for keyword status.
	// if a word occurs in: title, description and comment it is the strongest candidate
	// combination of a two is still good
	// only in one is pretty bad but still can be keyword
	// join maps
	global := map[string]float64{}
	for tK, tV := range titleCache {
		global[tK] = float64(tV) + descriptionAvg + commentAvg
	}

	for dK, dV := range descriptionCache {
		global[dK] += float64(dV) + commentAvg
	}

	for cK, cV := range commentCache {
		global[cK] += float64(cV)
	}

	// extract array and sort
	globalArr := []*WordCount{}
	for k, v := range global {
		globalArr = append(globalArr, &WordCount{Word: k, Count: v})
	}

	WordCountBy(wordCountCriteria).Sort(globalArr)

	lb := maxKeywords
	if maxKeywords > len(globalArr) {
		lb = len(globalArr) - 1
	}

	out := []string{}

	for i := 0; i < lb; i++ {
		out = append(out, globalArr[i].Word)
	}

	return out
}

func __removeOutliers(histogram []*WordCount, cache map[string]float64) ([]*WordCount, map[string]float64, float64) {
	countSum := 0.0
	for i := 0; i < len(histogram); i++ {
		countSum += histogram[i].Count
	}

	meanValue := countSum / float64(len(histogram))

	sqDiffSum := 0.0
	for i := 0; i < len(histogram); i++ {
		sqDiffSum += math.Pow(histogram[i].Count-meanValue, 2.0)
	}

	variance := sqDiffSum / float64(len(histogram))

	std := math.Sqrt(variance)

	out := []*WordCount{}
	outCache := map[string]float64{}

	lb := meanValue - std
	ub := meanValue + std

	tmpSum := 0.0
	for i := 0; i < len(histogram); i++ {
		if histogram[i].Count >= lb && histogram[i].Count <= ub && !__simplewords.is(histogram[i].Word) {
			out = append(out, histogram[i])
			outCache[histogram[i].Word] = histogram[i].Count
			tmpSum += histogram[i].Count
		} else {
			log.Printf("Removing word %s occ = %f", histogram[i].Word, histogram[i].Count)
		}
	}

	return out, outCache, tmpSum / float64(len(out))
}

type runes map[rune]bool
type simplewords map[string]bool

func (r runes) contain(rune rune) bool {
	_, contains := r[rune]
	return contains
}

func (sw simplewords) is(word string) bool {
	_, contains := sw[word]
	return contains
}

var __stopRunes = runes{
	' ':  true,
	'.':  true,
	'\n': true,
	'(':  true,
	')':  true,
	'"':  true,
	',':  true,
	'*':  true,
}

var __simplewords = simplewords{
	"": true,
}

func __stringHistogram(str *string) ([]*WordCount, map[string]float64) {
	words := strings.FieldsFunc(*str, func(r rune) bool {
		return __stopRunes.contain(r)
	})

	cache := map[string]float64{}

	for i := 0; i < len(words); i++ {
		cache[words[i]]++
	}

	out := []*WordCount{}

	for k, v := range cache {
		out = append(out, &WordCount{Word: k, Count: v})
	}

	WordCountBy(wordCountCriteria).Sort(out)

	return out, cache
}

type WordCount struct {
	Word  string
	Count float64
}

var wordCountCriteria = func(c1, c2 *WordCount) bool {
	return c1.Count > c2.Count
}

// function that defines ordering between content objects
type WordCountBy func(c1, c2 *WordCount) bool

// method on the function type, sorts the argument slice according  to the function
func (wordCountBy WordCountBy) Sort(wordCounts []*WordCount) {
	ws := &wordCountSorter{
		wordCounts:  wordCounts,
		wordCountBy: wordCountBy,
	}
	sort.Sort(ws)
}

type wordCountSorter struct {
	wordCounts  []*WordCount
	wordCountBy func(c1, c2 *WordCount) bool
}

// Len is part of sort.Interface.
func (ws *wordCountSorter) Len() int {
	return len(ws.wordCounts)
}

// Swap is part of sort.Interface.
func (ws *wordCountSorter) Swap(i, j int) {
	ws.wordCounts[i], ws.wordCounts[j] = ws.wordCounts[j], ws.wordCounts[i]
}

// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
func (ws *wordCountSorter) Less(i, j int) bool {
	return ws.wordCountBy(ws.wordCounts[i], ws.wordCounts[j])
}
<|endoftext|>


<commit_before>/*

LICENSE:  MIT
Author:   sine
Email:    sinerwr@gmail.com

*/

package controller

import (
	"encoding/json"
	"github.com/getsentry/raven-go"
	"golang.org/x/net/context"
	"io/ioutil"
	"net/http"

	"github.com/SiCo-Ops/Pb"
	"github.com/SiCo-Ops/dao/grpc"
	// "github.com/SiCo-Ops/dao/mongo"
)

var (
	cloudTokenID  string
	cloudTokenKey string
	cloudRegion   string
	cloudService  string
)

type ThirdToken struct {
	PrivateToken AuthenticationToken `json:"token"`
	Cloud        string              `json:"cloud"`
	Name         string              `json:"name"`
	ID           string              `json:"id"`
	Key          string              `json:"key"`
}

type CloudAPIRequest struct {
	PrivateToken   AuthenticationToken `json:"token"`
	CloudTokenName string              `json:"name"`
	Region         string              `json:"region"`
	Action         string              `json:"action"`
	Param          map[string]string   `json:"params"`
}

type CloudAPIRawRequest struct {
	Token         string            `json:"token"`
	CloudTokenID  string            `json:"cloudid"`
	CloudTokenKey string            `json:"cloudkey"`
	Region        string            `json:"region"`
	Action        string            `json:"action"`
	Param         map[string]string `json:"params"`
}

type CloudAPIResponse struct {
	Code int64  `json:"code"`
	Msg  string `json:"msg"`
	Data string `json:"data"`
}

func CloudTokenRegistry(rw http.ResponseWriter, req *http.Request) {
	defer func() {
		recover()
		if rcv := recover(); rcv != nil {
			raven.CaptureMessage("controller.CloudTokenRegistry", nil)
		}
	}()
	data, ok := ValidatePostData(rw, req)
	v := &ThirdToken{}
	if ok {
		json.Unmarshal(data, v)
	} else {
		return
	}
	if v.Name == "" || v.Cloud == "" || v.ID == "" {
		rsp, _ := json.Marshal(ResponseErrmsg(2))
		httprsp(rw, rsp)
		return
	}
	if config.AAAEnable && !AAAValidateToken(v.PrivateToken.ID, v.PrivateToken.Signature) {
		rsp, _ := json.Marshal(ResponseErrmsg(1))
		httprsp(rw, rsp)
		return
	}
	cc := rpc.RPCConn(RPCAddr["Li"])
	defer cc.Close()
	c := pb.NewCloudTokenServiceClient(cc)
	in := &pb.CloudTokenCall{}
	in.Cloud = v.Cloud
	in.Name = v.Name
	in.Id = v.ID
	in.Key = v.Key
	in.AAATokenID = v.PrivateToken.ID
	r, err := c.TokenSet(context.Background(), in)
	if err != nil {
		raven.CaptureError(err, nil)
	}
	if r.Id != "" {
		rsp, _ := json.Marshal(&ResponseData{0, "Success"})
		httprsp(rw, rsp)
		return
	}
	rsp, _ := json.Marshal(ResponseErrmsg(2))
	httprsp(rw, rsp)
}

func CloudTokenGet(id string, cloud string, name string) (string, string) {
	in := &pb.CloudTokenCall{}
	in.AAATokenID = id
	in.Cloud = cloud
	in.Name = name
	cc := rpc.RPCConn(RPCAddr["Li"])
	defer cc.Close()
	c := pb.NewCloudTokenServiceClient(cc)
	res, err := c.TokenGet(context.Background(), in)
	if err != nil {
		raven.CaptureError(err, nil)
	}
	if res.Id != "" {
		return res.Id, res.Key
	}
	return "", ""
}

func CloudServiceIsSupport(cloud string, service string) bool {
	d, err := ioutil.ReadFile("cloud.json")
	if err != nil {
		raven.CaptureError(err, nil)
		return false
	}
	var v map[string][]string
	json.Unmarshal(d, &v)
	if value, ok := v[cloud]; ok {
		for _, v := range value {
			if v == service {
				return true
			}
		}
		return false
	}
	return false
}

func CloudAPICall(rw http.ResponseWriter, req *http.Request) {
	data, ok := ValidatePostData(rw, req)
	if !ok {
		return
	}
	v := &CloudAPIRequest{}
	json.Unmarshal(data, v)

	if !AAAValidateToken(v.PrivateToken.ID, v.PrivateToken.Signature) {
		rsp, _ := json.Marshal(ResponseErrmsg(2))
		httprsp(rw, rsp)
		return
	}

	cloud := getRouteName(req, "cloud")
	service := getRouteName(req, "service")
	action, ok := actionMap(cloud, service, v.Action)
	if !ok {
		rsp, _ := json.Marshal(ResponseErrmsg(4))
		httprsp(rw, rsp)
		return
	}

	cloudTokenID, cloudTokenKey = CloudTokenGet(v.PrivateToken.ID, cloud, v.CloudTokenName)

	in := &pb.CloudAPICall{Service: service, Action: action, Region: v.Region, CloudId: cloudTokenID, CloudKey: cloudTokenKey}
	in.Params = v.Param
	cc := rpc.RPCConn(RPCAddr["Li"])
	defer cc.Close()
	c := pb.NewCloudAPIServiceClient(cc)
	var res *pb.CloudAPIBack
	switch cloud {
	case "qcloud":
		res, _ = c.QcloudRPC(context.Background(), in)
	default:
		res = &pb.CloudAPIBack{Code: 1, Msg: "Not ready support yet."}
	}
	if res.Code == 0 {
		rsp := res.Data
		httprsp(rw, rsp)
		return
	}
	rsp, _ := json.Marshal(res)
	httprsp(rw, rsp)
}

func CloudAPICallRaw(rw http.ResponseWriter, req *http.Request) {
	data, ok := ValidatePostData(rw, req)
	if !ok {
		return
	}
	v := &CloudAPIRawRequest{}
	json.Unmarshal(data, v)
	if !ValidateOpenToken(v.Token) {
		rsp, _ := json.Marshal(ResponseErrmsg(5))
		httprsp(rw, rsp)
		return
	}

	cloud := getRouteName(req, "cloud")
	service := getRouteName(req, "service")

	in := &pb.CloudAPICall{Service: service, Action: v.Action, Region: v.Region, CloudId: v.CloudTokenID, CloudKey: v.CloudTokenKey}
	in.Params = v.Param
	cc := rpc.RPCConn(RPCAddr["Li"])
	defer cc.Close()
	c := pb.NewCloudAPIServiceClient(cc)
	var res *pb.CloudAPIBack
	switch cloud {
	case "qcloud":
		res, _ = c.QcloudRPC(context.Background(), in)
	default:
		res = &pb.CloudAPIBack{Code: 1, Msg: "Not ready support yet."}
	}
	if res.Code == 0 {
		rsp := res.Data
		httprsp(rw, rsp)
		return
	}
	rsp, _ := json.Marshal(res)
	httprsp(rw, rsp)

}
<commit_msg>fix_reqeust_cloudapi_with_same_procedure<commit_after>/*

LICENSE:  MIT
Author:   sine
Email:    sinerwr@gmail.com

*/

package controller

import (
	"encoding/json"
	"github.com/getsentry/raven-go"
	"golang.org/x/net/context"
	"io/ioutil"
	"net/http"

	"github.com/SiCo-Ops/Pb"
	"github.com/SiCo-Ops/dao/grpc"
	// "github.com/SiCo-Ops/dao/mongo"
)

var (
	cloudTokenID  string
	cloudTokenKey string
	cloudRegion   string
	cloudService  string
)

type ThirdToken struct {
	PrivateToken AuthenticationToken `json:"token"`
	Cloud        string              `json:"cloud"`
	Name         string              `json:"name"`
	ID           string              `json:"id"`
	Key          string              `json:"key"`
}

type CloudAPIRequest struct {
	PrivateToken   AuthenticationToken `json:"token"`
	CloudTokenName string              `json:"name"`
	Region         string              `json:"region"`
	Action         string              `json:"action"`
	Param          map[string]string   `json:"params"`
}

type CloudAPIRawRequest struct {
	Token         string            `json:"token"`
	CloudTokenID  string            `json:"cloudid"`
	CloudTokenKey string            `json:"cloudkey"`
	Region        string            `json:"region"`
	Action        string            `json:"action"`
	Param         map[string]string `json:"params"`
}

type CloudAPIResponse struct {
	Code int64  `json:"code"`
	Msg  string `json:"msg"`
	Data string `json:"data"`
}

func CloudTokenRegistry(rw http.ResponseWriter, req *http.Request) {
	defer func() {
		recover()
		if rcv := recover(); rcv != nil {
			raven.CaptureMessage("controller.CloudTokenRegistry", nil)
		}
	}()
	data, ok := ValidatePostData(rw, req)
	v := &ThirdToken{}
	if ok {
		json.Unmarshal(data, v)
	} else {
		return
	}
	if v.Name == "" || v.Cloud == "" || v.ID == "" {
		rsp, _ := json.Marshal(ResponseErrmsg(2))
		httprsp(rw, rsp)
		return
	}
	if config.AAAEnable && !AAAValidateToken(v.PrivateToken.ID, v.PrivateToken.Signature) {
		rsp, _ := json.Marshal(ResponseErrmsg(1))
		httprsp(rw, rsp)
		return
	}
	cc := rpc.RPCConn(RPCAddr["Li"])
	defer cc.Close()
	c := pb.NewCloudTokenServiceClient(cc)
	in := &pb.CloudTokenCall{}
	in.Cloud = v.Cloud
	in.Name = v.Name
	in.Id = v.ID
	in.Key = v.Key
	in.AAATokenID = v.PrivateToken.ID
	r, err := c.TokenSet(context.Background(), in)
	if err != nil {
		raven.CaptureError(err, nil)
	}
	if r.Id != "" {
		rsp, _ := json.Marshal(&ResponseData{0, "Success"})
		httprsp(rw, rsp)
		return
	}
	rsp, _ := json.Marshal(ResponseErrmsg(2))
	httprsp(rw, rsp)
}

func CloudTokenGet(id string, cloud string, name string) (string, string) {
	in := &pb.CloudTokenCall{}
	in.AAATokenID = id
	in.Cloud = cloud
	in.Name = name
	cc := rpc.RPCConn(RPCAddr["Li"])
	defer cc.Close()
	c := pb.NewCloudTokenServiceClient(cc)
	res, err := c.TokenGet(context.Background(), in)
	if err != nil {
		raven.CaptureError(err, nil)
	}
	if res.Id != "" {
		return res.Id, res.Key
	}
	return "", ""
}

func CloudServiceIsSupport(cloud string, service string) bool {
	d, err := ioutil.ReadFile("cloud.json")
	if err != nil {
		raven.CaptureError(err, nil)
		return false
	}
	var v map[string][]string
	json.Unmarshal(d, &v)
	if value, ok := v[cloud]; ok {
		for _, v := range value {
			if v == service {
				return true
			}
		}
		return false
	}
	return false
}

func CloudAPICall(rw http.ResponseWriter, req *http.Request) {
	data, ok := ValidatePostData(rw, req)
	if !ok {
		return
	}
	v := &CloudAPIRequest{}
	json.Unmarshal(data, v)

	if !AAAValidateToken(v.PrivateToken.ID, v.PrivateToken.Signature) {
		rsp, _ := json.Marshal(ResponseErrmsg(2))
		httprsp(rw, rsp)
		return
	}

	cloud := getRouteName(req, "cloud")
	service := getRouteName(req, "service")
	action, ok := actionMap(cloud, service, v.Action)
	if !ok {
		rsp, _ := json.Marshal(ResponseErrmsg(4))
		httprsp(rw, rsp)
		return
	}

	cloudTokenID, cloudTokenKey = CloudTokenGet(v.PrivateToken.ID, cloud, v.CloudTokenName)

	in := &pb.CloudAPICall{Cloud: cloud, Service: service, Action: action, Region: v.Region, CloudId: cloudTokenID, CloudKey: cloudTokenKey}
	in.Params = v.Param
	cc := rpc.RPCConn(RPCAddr["Li"])
	defer cc.Close()
	c := pb.NewCloudAPIServiceClient(cc)
	var res *pb.CloudAPIBack
	res, _ = c.RequestRPC(context.Background(), in)
	if res.Code == 0 {
		rsp := res.Data
		httprsp(rw, rsp)
		return
	}
	rsp, _ := json.Marshal(res)
	httprsp(rw, rsp)
}

func CloudAPICallRaw(rw http.ResponseWriter, req *http.Request) {
	data, ok := ValidatePostData(rw, req)
	if !ok {
		return
	}
	v := &CloudAPIRawRequest{}
	json.Unmarshal(data, v)
	if !ValidateOpenToken(v.Token) {
		rsp, _ := json.Marshal(ResponseErrmsg(5))
		httprsp(rw, rsp)
		return
	}

	cloud := getRouteName(req, "cloud")
	service := getRouteName(req, "service")

	in := &pb.CloudAPICall{Cloud: cloud, Service: service, Action: v.Action, Region: v.Region, CloudId: v.CloudTokenID, CloudKey: v.CloudTokenKey}
	in.Params = v.Param
	cc := rpc.RPCConn(RPCAddr["Li"])
	defer cc.Close()
	c := pb.NewCloudAPIServiceClient(cc)
	var res *pb.CloudAPIBack
	res, _ = c.RequestRPC(context.Background(), in)
	if res.Code == 0 {
		rsp := res.Data
		httprsp(rw, rsp)
		return
	}
	rsp, _ := json.Marshal(res)
	httprsp(rw, rsp)

}
<|endoftext|>


<commit_before>package controller

import (
	"fmt"

	"github.com/goadesign/goa"
	"github.com/jinzhu/gorm"
	"github.com/tikasan/eventory/app"
	"github.com/tikasan/eventory/models"
	"github.com/tikasan/eventory/utility"
)

// PrefsController implements the prefs resource.
type PrefsController struct {
	*goa.Controller
	db *gorm.DB
}

// NewPrefsController creates a prefs controller.
func NewPrefsController(service *goa.Service, db *gorm.DB) *PrefsController {
	return &PrefsController{
		Controller: service.NewController("PrefsController"),
		db:         db,
	}
}

// Follow runs the follow action.
func (c *PrefsController) Follow(ctx *app.FollowPrefsContext) error {
	// PrefsController_Follow: start_implement

	// Put your logic here
	ufg := &models.UserFollowPref{}
	ufg.PrefID = ctx.PrefID
	userID, err := utility.GetUserID(ctx.Context)
	if err != nil {
		return fmt.Errorf("%v", err)
	}
	ufg.UserID = userID
	ufgDB := models.NewUserFollowPrefDB(c.db)
	if "PUT" == ctx.Request.Method {
		ufgDB.UserFollowPref(ctx.Context, ufg)
	}
	if "DELETE" == ctx.Request.Method {
		ufgDB.UserUnfollowPref(ctx.Context, ufg)
	}
	// PrefsController_Follow: end_implement
	return nil
}
<commit_msg>add : 都道府県コントローラーのコメント<commit_after>package controller

import (
	"fmt"

	"github.com/goadesign/goa"
	"github.com/jinzhu/gorm"
	"github.com/tikasan/eventory/app"
	"github.com/tikasan/eventory/models"
	"github.com/tikasan/eventory/utility"
)

// PrefsController implements the prefs resource.
type PrefsController struct {
	*goa.Controller
	db *gorm.DB
}

// NewPrefsController creates a prefs controller.
func NewPrefsController(service *goa.Service, db *gorm.DB) *PrefsController {
	return &PrefsController{
		Controller: service.NewController("PrefsController"),
		db:         db,
	}
}

// フォロー、アンフォロー操作
func (c *PrefsController) Follow(ctx *app.FollowPrefsContext) error {
	// PrefsController_Follow: start_implement

	// Put your logic here
	ufg := &models.UserFollowPref{}
	ufg.PrefID = ctx.PrefID
	userID, err := utility.GetUserID(ctx.Context)
	if err != nil {
		return fmt.Errorf("%v", err)
	}
	ufg.UserID = userID
	ufgDB := models.NewUserFollowPrefDB(c.db)
	// HTTPメソッドで判定する
	if "PUT" == ctx.Request.Method {
		ufgDB.UserFollowPref(ctx.Context, ufg)
	}
	if "DELETE" == ctx.Request.Method {
		ufgDB.UserUnfollowPref(ctx.Context, ufg)
	}
	// PrefsController_Follow: end_implement
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"text/template"

	"github.com/mitchellh/cli"
)

//
// Implement the "hosts" command

type HostsCommand struct {
	Ui cli.Ui
}

func (c *HostsCommand) Run(_ []string) int {
	state, err := fetchState(".")
	if err != nil {
		return 1
	}

	instances, err := parseState(*state)
	if err != nil {
		return 1
	}

	tString, err := FSString(false, "/templates/etcHostsTemplate")
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Unable to read etcHostsTemplate: %s", err))
		return 1
	}

	t, err := template.New("etcHostsTemplate").Parse(tString)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Unable to parse etcHostsTemplate: %s", err))
		return 1
	}

	output := bytes.NewBuffer([]byte{})
	err = t.Execute(output, instances)
	if err != nil {
		return 1
	}

	c.Ui.Output(output.String())
	return 0
}

func (c *HostsCommand) Help() string {
	return "Generate an /etc/hosts fragment for the Terraform instances"
}

func (c *HostsCommand) Synopsis() string {
	return "Generate an /etc/hosts fragment for the Terraform instances"
}
<commit_msg>Improved error reporting in hosts command.<commit_after>package main

import (
	"bytes"
	"fmt"
	"text/template"

	"github.com/mitchellh/cli"
)

//
// Implement the "hosts" command

type HostsCommand struct {
	Ui cli.Ui
}

func (c *HostsCommand) Run(_ []string) int {
	state, err := fetchState(".")
	if err != nil {
		return 1
	}

	instances, err := parseState(*state)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Unable to parse state file: %s", err))
		return 1
	}

	tString, err := FSString(false, "/templates/etcHostsTemplate")
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Unable to read etcHostsTemplate: %s", err))
		return 1
	}

	t, err := template.New("etcHostsTemplate").Parse(tString)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Unable to parse etcHostsTemplate: %s", err))
		return 1
	}

	output := bytes.NewBuffer([]byte{})
	err = t.Execute(output, instances)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Unable to execute hosts template: %s", err))
		return 1
	}

	c.Ui.Output(output.String())
	return 0
}

func (c *HostsCommand) Help() string {
	return "Generate an /etc/hosts fragment for the Terraform instances"
}

func (c *HostsCommand) Synopsis() string {
	return "Generate an /etc/hosts fragment for the Terraform instances"
}
<|endoftext|>


<commit_before>/*
Copyright 2021 The logr Authors.

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,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package funcr implements github.com/go-logr/logr.Logger in terms of
// an arbitrary "write" function.
package funcr

import (
	"bytes"
	"fmt"
	"path/filepath"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"sync/atomic"

	"github.com/go-logr/logr"
)

// New returns a logr.Logger which is implemented by an arbitrary function.
func New(fn func(prefix, args string), opts Options) logr.Logger {
	fnl := &fnlogger{
		prefix:    "",
		values:    nil,
		depth:     0,
		write:     fn,
		logCaller: opts.LogCaller,
		verbosity: opts.Verbosity,
	}
	return logr.New(fnl)
}

// Options carries parameters which influence the way logs are generated.
type Options struct {
	// LogCaller tells funcr to add a "caller" key to some or all log lines.
	// This has some overhead, so some users might not want it.
	LogCaller MessageClass

	// Verbosity tells funcr which V logs to be write.  Higher values enable
	// more logs.
	Verbosity int
}

// MessageClass indicates which category or categories of messages to consider.
type MessageClass int

const (
	None MessageClass = iota
	All
	Info
	Error
)

type fnlogger struct {
	prefix    string
	values    []interface{}
	depth     int
	write     func(prefix, args string)
	logCaller MessageClass
	verbosity int
}

// Assert conformance to the interfaces.
var _ logr.LogSink = &fnlogger{}
var _ logr.CallDepthLogSink = &fnlogger{}

// Magic string for intermediate frames that we should ignore.
const autogeneratedFrameName = "<autogenerated>"

// Cached depth of this interface's log functions.
var framesAtomic int32 // atomic

// Discover how many frames we need to climb to find the caller. This approach
// was suggested by Ian Lance Taylor of the Go team, so it *should* be safe
// enough (famous last words) and should survive changes in Go's optimizer.
//
// This assumes that all logging paths are the same depth from the caller,
// which should be a reasonable assumption since they are part of the same
// interface.
func framesToCaller() int {
	// Figuring out the current depth is somewhat expensive. Saving the value
	// amortizes most of that runtime cost.
	if atomic.LoadInt32(&framesAtomic) != 0 {
		return int(framesAtomic)
	}
	// 1 is the immediate caller.  3 should be too many.
	for i := 1; i < 3; i++ {
		_, file, _, _ := runtime.Caller(i + 1) // +1 for this function's frame
		if file != autogeneratedFrameName {
			atomic.StoreInt32(&framesAtomic, int32(i))
			return i
		}
	}
	return 1 // something went wrong, this is safe
}

func flatten(kvList ...interface{}) string {
	if len(kvList)%2 != 0 {
		kvList = append(kvList, "<no-value>")
	}
	// Empirically bytes.Buffer is faster than strings.Builder for this.
	buf := bytes.NewBuffer(make([]byte, 0, 1024))
	for i := 0; i < len(kvList); i += 2 {
		k, ok := kvList[i].(string)
		if !ok {
			k = fmt.Sprintf("<non-string-key-%d>", i/2)
		}
		v := kvList[i+1]

		if i > 0 {
			buf.WriteRune(' ')
		}
		buf.WriteRune('"')
		buf.WriteString(k)
		buf.WriteRune('"')
		buf.WriteRune('=')
		buf.WriteString(pretty(v))
	}
	return buf.String()
}

func pretty(value interface{}) string {
	return prettyWithFlags(value, 0)
}

const (
	flagRawString = 0x1
)

// TODO: This is not fast. Most of the overhead goes here.
func prettyWithFlags(value interface{}, flags uint32) string {
	// Handling the most common types without reflect is a small perf win.
	switch v := value.(type) {
	case bool:
		return strconv.FormatBool(v)
	case string:
		if flags&flagRawString > 0 {
			return v
		}
		// This is empirically faster than strings.Builder.
		return `"` + v + `"`
	case int:
		return strconv.FormatInt(int64(v), 10)
	case int8:
		return strconv.FormatInt(int64(v), 10)
	case int16:
		return strconv.FormatInt(int64(v), 10)
	case int32:
		return strconv.FormatInt(int64(v), 10)
	case int64:
		return strconv.FormatInt(int64(v), 10)
	case uint:
		return strconv.FormatUint(uint64(v), 10)
	case uint8:
		return strconv.FormatUint(uint64(v), 10)
	case uint16:
		return strconv.FormatUint(uint64(v), 10)
	case uint32:
		return strconv.FormatUint(uint64(v), 10)
	case uint64:
		return strconv.FormatUint(v, 10)
	case uintptr:
		return strconv.FormatUint(uint64(v), 10)
	case float32:
		return strconv.FormatFloat(float64(v), 'f', -1, 32)
	case float64:
		return strconv.FormatFloat(v, 'f', -1, 64)
	}

	buf := bytes.NewBuffer(make([]byte, 0, 256))
	t := reflect.TypeOf(value)
	if t == nil {
		return "null"
	}
	v := reflect.ValueOf(value)
	switch t.Kind() {
	case reflect.Bool:
		return strconv.FormatBool(v.Bool())
	case reflect.String:
		if flags&flagRawString > 0 {
			return v.String()
		}
		// This is empirically faster than strings.Builder.
		return `"` + v.String() + `"`
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return strconv.FormatInt(int64(v.Int()), 10)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return strconv.FormatUint(uint64(v.Uint()), 10)
	case reflect.Float32:
		return strconv.FormatFloat(float64(v.Float()), 'f', -1, 32)
	case reflect.Float64:
		return strconv.FormatFloat(v.Float(), 'f', -1, 64)
	case reflect.Struct:
		buf.WriteRune('{')
		for i := 0; i < t.NumField(); i++ {
			f := t.Field(i)
			if f.PkgPath != "" {
				// reflect says this field is only defined for non-exported fields.
				continue
			}
			if i > 0 {
				buf.WriteRune(',')
			}
			buf.WriteRune('"')
			name := f.Name
			if tag, found := f.Tag.Lookup("json"); found {
				if comma := strings.Index(tag, ","); comma != -1 {
					name = tag[:comma]
				} else {
					name = tag
				}
			}
			buf.WriteString(name)
			buf.WriteRune('"')
			buf.WriteRune(':')
			buf.WriteString(pretty(v.Field(i).Interface()))
		}
		buf.WriteRune('}')
		return buf.String()
	case reflect.Slice, reflect.Array:
		buf.WriteRune('[')
		for i := 0; i < v.Len(); i++ {
			if i > 0 {
				buf.WriteRune(',')
			}
			e := v.Index(i)
			buf.WriteString(pretty(e.Interface()))
		}
		buf.WriteRune(']')
		return buf.String()
	case reflect.Map:
		buf.WriteRune('{')
		// This does not sort the map keys, for best perf.
		it := v.MapRange()
		i := 0
		for it.Next() {
			if i > 0 {
				buf.WriteRune(',')
			}
			// JSON only does string keys.
			buf.WriteRune('"')
			buf.WriteString(prettyWithFlags(it.Key().Interface(), flagRawString))
			buf.WriteRune('"')
			buf.WriteRune(':')
			buf.WriteString(pretty(it.Value().Interface()))
			i++
		}
		buf.WriteRune('}')
		return buf.String()
	case reflect.Ptr, reflect.Interface:
		return pretty(v.Elem().Interface())
	}
	return fmt.Sprintf(`"<unhandled-%s>"`, t.Kind().String())
}

type callerID struct {
	File string `json:"file"`
	Line int    `json:"line"`
}

func (l fnlogger) caller() callerID {
	// +1 for this frame.
	_, file, line, ok := runtime.Caller(framesToCaller() + l.depth + 1)
	if !ok {
		return callerID{"<unknown>", 0}
	}
	return callerID{filepath.Base(file), line}
}

func (l *fnlogger) Init(info logr.RuntimeInfo) {
	l.depth += info.CallDepth
}

func (l fnlogger) Enabled(level int) bool {
	return level <= l.verbosity
}

func (l fnlogger) Info(level int, msg string, kvList ...interface{}) {
	args := make([]interface{}, 0, 64) // using a constant here impacts perf
	if l.logCaller == All || l.logCaller == Info {
		args = append(args, "caller", l.caller())
	}
	args = append(args, "level", level, "msg", msg)
	args = append(args, l.values...)
	args = append(args, kvList...)
	argsStr := flatten(args...)
	l.write(l.prefix, argsStr)
}

func (l fnlogger) Error(err error, msg string, kvList ...interface{}) {
	args := make([]interface{}, 0, 64) // using a constant here impacts perf
	if l.logCaller == All || l.logCaller == Error {
		args = append(args, "caller", l.caller())
	}
	args = append(args, "msg", msg)
	var loggableErr interface{}
	if err != nil {
		loggableErr = err.Error()
	}
	args = append(args, "error", loggableErr)
	args = append(args, l.values...)
	args = append(args, kvList...)
	argsStr := flatten(args...)
	l.write(l.prefix, argsStr)
}

// WithName returns a new Logger with the specified name appended.  funcr
// uses '/' characters to separate name elements.  Callers should not pass '/'
// in the provided name string, but this library does not actually enforce that.
func (l *fnlogger) WithName(name string) logr.LogSink {
	l2 := &fnlogger{}
	*l2 = *l
	if len(l2.prefix) > 0 {
		l.prefix = l2.prefix + "/"
	}
	l2.prefix += name
	return l2
}

func (l *fnlogger) WithValues(kvList ...interface{}) logr.LogSink {
	l2 := &fnlogger{}
	*l2 = *l
	// Three slice args forces a copy.
	n := len(l.values)
	l2.values = append(l2.values[:n:n], kvList...)
	return l2
}

func (l *fnlogger) WithCallDepth(depth int) logr.LogSink {
	l2 := &fnlogger{}
	*l2 = *l
	l2.depth += depth
	return l2
}
<commit_msg>Simplify funcr With* methods<commit_after>/*
Copyright 2021 The logr Authors.

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,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package funcr implements github.com/go-logr/logr.Logger in terms of
// an arbitrary "write" function.
package funcr

import (
	"bytes"
	"fmt"
	"path/filepath"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"sync/atomic"

	"github.com/go-logr/logr"
)

// New returns a logr.Logger which is implemented by an arbitrary function.
func New(fn func(prefix, args string), opts Options) logr.Logger {
	fnl := &fnlogger{
		prefix:    "",
		values:    nil,
		depth:     0,
		write:     fn,
		logCaller: opts.LogCaller,
		verbosity: opts.Verbosity,
	}
	return logr.New(fnl)
}

// Options carries parameters which influence the way logs are generated.
type Options struct {
	// LogCaller tells funcr to add a "caller" key to some or all log lines.
	// This has some overhead, so some users might not want it.
	LogCaller MessageClass

	// Verbosity tells funcr which V logs to be write.  Higher values enable
	// more logs.
	Verbosity int
}

// MessageClass indicates which category or categories of messages to consider.
type MessageClass int

const (
	None MessageClass = iota
	All
	Info
	Error
)

type fnlogger struct {
	prefix    string
	values    []interface{}
	depth     int
	write     func(prefix, args string)
	logCaller MessageClass
	verbosity int
}

// Assert conformance to the interfaces.
var _ logr.LogSink = &fnlogger{}
var _ logr.CallDepthLogSink = &fnlogger{}

// Magic string for intermediate frames that we should ignore.
const autogeneratedFrameName = "<autogenerated>"

// Cached depth of this interface's log functions.
var framesAtomic int32 // atomic

// Discover how many frames we need to climb to find the caller. This approach
// was suggested by Ian Lance Taylor of the Go team, so it *should* be safe
// enough (famous last words) and should survive changes in Go's optimizer.
//
// This assumes that all logging paths are the same depth from the caller,
// which should be a reasonable assumption since they are part of the same
// interface.
func framesToCaller() int {
	// Figuring out the current depth is somewhat expensive. Saving the value
	// amortizes most of that runtime cost.
	if atomic.LoadInt32(&framesAtomic) != 0 {
		return int(framesAtomic)
	}
	// 1 is the immediate caller.  3 should be too many.
	for i := 1; i < 3; i++ {
		_, file, _, _ := runtime.Caller(i + 1) // +1 for this function's frame
		if file != autogeneratedFrameName {
			atomic.StoreInt32(&framesAtomic, int32(i))
			return i
		}
	}
	return 1 // something went wrong, this is safe
}

func flatten(kvList ...interface{}) string {
	if len(kvList)%2 != 0 {
		kvList = append(kvList, "<no-value>")
	}
	// Empirically bytes.Buffer is faster than strings.Builder for this.
	buf := bytes.NewBuffer(make([]byte, 0, 1024))
	for i := 0; i < len(kvList); i += 2 {
		k, ok := kvList[i].(string)
		if !ok {
			k = fmt.Sprintf("<non-string-key-%d>", i/2)
		}
		v := kvList[i+1]

		if i > 0 {
			buf.WriteRune(' ')
		}
		buf.WriteRune('"')
		buf.WriteString(k)
		buf.WriteRune('"')
		buf.WriteRune('=')
		buf.WriteString(pretty(v))
	}
	return buf.String()
}

func pretty(value interface{}) string {
	return prettyWithFlags(value, 0)
}

const (
	flagRawString = 0x1
)

// TODO: This is not fast. Most of the overhead goes here.
func prettyWithFlags(value interface{}, flags uint32) string {
	// Handling the most common types without reflect is a small perf win.
	switch v := value.(type) {
	case bool:
		return strconv.FormatBool(v)
	case string:
		if flags&flagRawString > 0 {
			return v
		}
		// This is empirically faster than strings.Builder.
		return `"` + v + `"`
	case int:
		return strconv.FormatInt(int64(v), 10)
	case int8:
		return strconv.FormatInt(int64(v), 10)
	case int16:
		return strconv.FormatInt(int64(v), 10)
	case int32:
		return strconv.FormatInt(int64(v), 10)
	case int64:
		return strconv.FormatInt(int64(v), 10)
	case uint:
		return strconv.FormatUint(uint64(v), 10)
	case uint8:
		return strconv.FormatUint(uint64(v), 10)
	case uint16:
		return strconv.FormatUint(uint64(v), 10)
	case uint32:
		return strconv.FormatUint(uint64(v), 10)
	case uint64:
		return strconv.FormatUint(v, 10)
	case uintptr:
		return strconv.FormatUint(uint64(v), 10)
	case float32:
		return strconv.FormatFloat(float64(v), 'f', -1, 32)
	case float64:
		return strconv.FormatFloat(v, 'f', -1, 64)
	}

	buf := bytes.NewBuffer(make([]byte, 0, 256))
	t := reflect.TypeOf(value)
	if t == nil {
		return "null"
	}
	v := reflect.ValueOf(value)
	switch t.Kind() {
	case reflect.Bool:
		return strconv.FormatBool(v.Bool())
	case reflect.String:
		if flags&flagRawString > 0 {
			return v.String()
		}
		// This is empirically faster than strings.Builder.
		return `"` + v.String() + `"`
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return strconv.FormatInt(int64(v.Int()), 10)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return strconv.FormatUint(uint64(v.Uint()), 10)
	case reflect.Float32:
		return strconv.FormatFloat(float64(v.Float()), 'f', -1, 32)
	case reflect.Float64:
		return strconv.FormatFloat(v.Float(), 'f', -1, 64)
	case reflect.Struct:
		buf.WriteRune('{')
		for i := 0; i < t.NumField(); i++ {
			f := t.Field(i)
			if f.PkgPath != "" {
				// reflect says this field is only defined for non-exported fields.
				continue
			}
			if i > 0 {
				buf.WriteRune(',')
			}
			buf.WriteRune('"')
			name := f.Name
			if tag, found := f.Tag.Lookup("json"); found {
				if comma := strings.Index(tag, ","); comma != -1 {
					name = tag[:comma]
				} else {
					name = tag
				}
			}
			buf.WriteString(name)
			buf.WriteRune('"')
			buf.WriteRune(':')
			buf.WriteString(pretty(v.Field(i).Interface()))
		}
		buf.WriteRune('}')
		return buf.String()
	case reflect.Slice, reflect.Array:
		buf.WriteRune('[')
		for i := 0; i < v.Len(); i++ {
			if i > 0 {
				buf.WriteRune(',')
			}
			e := v.Index(i)
			buf.WriteString(pretty(e.Interface()))
		}
		buf.WriteRune(']')
		return buf.String()
	case reflect.Map:
		buf.WriteRune('{')
		// This does not sort the map keys, for best perf.
		it := v.MapRange()
		i := 0
		for it.Next() {
			if i > 0 {
				buf.WriteRune(',')
			}
			// JSON only does string keys.
			buf.WriteRune('"')
			buf.WriteString(prettyWithFlags(it.Key().Interface(), flagRawString))
			buf.WriteRune('"')
			buf.WriteRune(':')
			buf.WriteString(pretty(it.Value().Interface()))
			i++
		}
		buf.WriteRune('}')
		return buf.String()
	case reflect.Ptr, reflect.Interface:
		return pretty(v.Elem().Interface())
	}
	return fmt.Sprintf(`"<unhandled-%s>"`, t.Kind().String())
}

type callerID struct {
	File string `json:"file"`
	Line int    `json:"line"`
}

func (l fnlogger) caller() callerID {
	// +1 for this frame.
	_, file, line, ok := runtime.Caller(framesToCaller() + l.depth + 1)
	if !ok {
		return callerID{"<unknown>", 0}
	}
	return callerID{filepath.Base(file), line}
}

// Note that this receiver is a pointer, so depth can be saved.
func (l *fnlogger) Init(info logr.RuntimeInfo) {
	l.depth += info.CallDepth
}

func (l fnlogger) Enabled(level int) bool {
	return level <= l.verbosity
}

func (l fnlogger) Info(level int, msg string, kvList ...interface{}) {
	args := make([]interface{}, 0, 64) // using a constant here impacts perf
	if l.logCaller == All || l.logCaller == Info {
		args = append(args, "caller", l.caller())
	}
	args = append(args, "level", level, "msg", msg)
	args = append(args, l.values...)
	args = append(args, kvList...)
	argsStr := flatten(args...)
	l.write(l.prefix, argsStr)
}

func (l fnlogger) Error(err error, msg string, kvList ...interface{}) {
	args := make([]interface{}, 0, 64) // using a constant here impacts perf
	if l.logCaller == All || l.logCaller == Error {
		args = append(args, "caller", l.caller())
	}
	args = append(args, "msg", msg)
	var loggableErr interface{}
	if err != nil {
		loggableErr = err.Error()
	}
	args = append(args, "error", loggableErr)
	args = append(args, l.values...)
	args = append(args, kvList...)
	argsStr := flatten(args...)
	l.write(l.prefix, argsStr)
}

// WithName returns a new Logger with the specified name appended.  funcr
// uses '/' characters to separate name elements.  Callers should not pass '/'
// in the provided name string, but this library does not actually enforce that.
func (l fnlogger) WithName(name string) logr.LogSink {
	if len(l.prefix) > 0 {
		l.prefix = l.prefix + "/"
	}
	l.prefix += name
	return &l
}

func (l fnlogger) WithValues(kvList ...interface{}) logr.LogSink {
	// Three slice args forces a copy.
	n := len(l.values)
	l.values = append(l.values[:n:n], kvList...)
	return &l
}

func (l fnlogger) WithCallDepth(depth int) logr.LogSink {
	l.depth += depth
	return &l
}
<|endoftext|>