<commit_before>package pqx

import (
	"testing"
)

func TestConnect(t *testing.T) {
	conn, err := Connect(map[string]string{"socket": "/private/tmp/.s.PGSQL.5432"})
	if err != nil {
		t.Fatal("Unable to establish connection")
	}

	if _, present := conn.runtimeParams["server_version"]; !present {
		t.Error("Runtime parameters not stored")
	}

	if conn.pid == 0 {
		t.Error("Backend PID not stored")
	}

	if conn.secretKey == 0 {
		t.Error("Backend secret key not stored")
	}

	err = conn.Close()
	if err != nil {
		t.Fatal("Unable to close connection")
	}
}

func TestQuery(t *testing.T) {
	conn, err := Connect(map[string]string{"socket": "/private/tmp/.s.PGSQL.5432"})
	if err != nil {
		t.Fatal("Unable to establish connection")
	}

	// var rows []map[string]string
	_, err = conn.Query("SELECT * FROM people")
	if err != nil {
		t.Fatal("Query failed")
	}

	err = conn.Close()
	if err != nil {
		t.Fatal("Unable to close connection")
	}
}<commit_msg>Add failing test for Conn.Query<commit_after>package pqx

import (
	"testing"
)

func TestConnect(t *testing.T) {
	conn, err := Connect(map[string]string{"socket": "/private/tmp/.s.PGSQL.5432"})
	if err != nil {
		t.Fatal("Unable to establish connection")
	}

	if _, present := conn.runtimeParams["server_version"]; !present {
		t.Error("Runtime parameters not stored")
	}

	if conn.pid == 0 {
		t.Error("Backend PID not stored")
	}

	if conn.secretKey == 0 {
		t.Error("Backend secret key not stored")
	}

	err = conn.Close()
	if err != nil {
		t.Fatal("Unable to close connection")
	}
}

func TestQuery(t *testing.T) {
	conn, err := Connect(map[string]string{"socket": "/private/tmp/.s.PGSQL.5432"})
	if err != nil {
		t.Fatal("Unable to establish connection")
	}

	var rows []map[string]string
	rows, err = conn.Query("select 'Jack' as name")
	if err != nil {
		t.Fatal("Query failed")
	}

	if len(rows) != 1 {
		t.Fatal("Received wrong number of rows")
	}

	if rows[0]["name"] != "Jack" {
		t.Fatal("Received incorrect name")
	}

	err = conn.Close()
	if err != nil {
		t.Fatal("Unable to close connection")
	}
}<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"restic/errors"
	"sort"

	"github.com/spf13/cobra"

	"encoding/json"
	"restic"
)

var cmdSnapshots = &cobra.Command{
	Use:   "snapshots",
	Short: "list all snapshots",
	Long: `
The "snapshots" command lists all snapshots stored in the repository.
`,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runSnapshots(snapshotOptions, globalOptions, args)
	},
}

// SnapshotOptions bundle all options for the snapshots command.
type SnapshotOptions struct {
	Host  string
	Paths []string
}

var snapshotOptions SnapshotOptions

func init() {
	cmdRoot.AddCommand(cmdSnapshots)

	f := cmdSnapshots.Flags()
	f.StringVar(&snapshotOptions.Host, "host", "", "only print snapshots for this host")
	f.StringSliceVar(&snapshotOptions.Paths, "path", []string{}, "only print snapshots for this `path` (can be specified multiple times)")
}

func runSnapshots(opts SnapshotOptions, gopts GlobalOptions, args []string) error {
	if len(args) != 0 {
		return errors.Fatal("wrong number of arguments")
	}

	repo, err := OpenRepository(gopts)
	if err != nil {
		return err
	}

	if !gopts.NoLock {
		lock, err := lockRepo(repo)
		defer unlockRepo(lock)
		if err != nil {
			return err
		}
	}

	done := make(chan struct{})
	defer close(done)

	list := []*restic.Snapshot{}
	for id := range repo.List(restic.SnapshotFile, done) {
		sn, err := restic.LoadSnapshot(repo, id)
		if err != nil {
			Warnf("error loading snapshot %s: %v\n", id, err)
			continue
		}

		if restic.SamePaths(sn.Paths, opts.Paths) && (opts.Host == "" || opts.Host == sn.Hostname) {
			pos := sort.Search(len(list), func(i int) bool {
				return list[i].Time.After(sn.Time)
			})

			if pos < len(list) {
				list = append(list, nil)
				copy(list[pos+1:], list[pos:])
				list[pos] = sn
			} else {
				list = append(list, sn)
			}
		}

	}

	if gopts.JSON {
		err := printSnapshotsJSON(gopts.stdout, list)
		if err != nil {
			Warnf("error printing snapshot: %v\n", err)
		}
		return nil
	}
	printSnapshotsReadable(gopts.stdout, list)

	return nil
}

// printSnapshotsReadable prints a text table of the snapshots in list to stdout.
func printSnapshotsReadable(stdout io.Writer, list []*restic.Snapshot) {

	tab := NewTable()
	tab.Header = fmt.Sprintf("%-8s  %-19s  %-10s  %-10s  %-3s %s", "ID", "Date", "Host", "Tags", "", "Directory")
	tab.RowFormat = "%-8s  %-19s  %-10s  %-10s  %-3s %s"

	for _, sn := range list {
		if len(sn.Paths) == 0 {
			continue
		}

		firstTag := ""
		if len(sn.Tags) > 0 {
			firstTag = sn.Tags[0]
		}

		rows := len(sn.Paths)

		treeElement := "   "
		if rows != 1 {
			treeElement = "┌──"
		}

		tab.Rows = append(tab.Rows, []interface{}{sn.ID().Str(), sn.Time.Format(TimeFormat), sn.Hostname, firstTag, treeElement, sn.Paths[0]})

		if len(sn.Tags) > rows {
			rows = len(sn.Tags)
		}

		for i := 1; i < rows; i++ {
			path := ""
			if len(sn.Paths) > i {
				path = sn.Paths[i]
			}

			tag := ""
			if len(sn.Tags) > i {
				tag = sn.Tags[i]
			}

			treeElement := "│"
			if i == (rows - 1) {
				treeElement = "└──"
			}

			tab.Rows = append(tab.Rows, []interface{}{"", "", "", tag, treeElement, path})
		}
	}

	tab.Write(stdout)
}

// Snapshot helps to print Snaphots as JSON
type Snapshot struct {
	*restic.Snapshot

	ID string `json:"id"`
}

// printSnapshotsJSON writes the JSON representation of list to stdout.
func printSnapshotsJSON(stdout io.Writer, list []*restic.Snapshot) error {

	var snapshots []Snapshot

	for _, sn := range list {

		k := Snapshot{
			Snapshot: sn,
			ID:       sn.ID().String(),
		}
		snapshots = append(snapshots, k)
	}

	return json.NewEncoder(stdout).Encode(snapshots)

}
<commit_msg>Make columns for host and tags size width dynamicly on their content.<commit_after>package main

import (
	"fmt"
	"io"
	"restic/errors"
	"sort"

	"github.com/spf13/cobra"

	"encoding/json"
	"restic"
)

var cmdSnapshots = &cobra.Command{
	Use:   "snapshots",
	Short: "list all snapshots",
	Long: `
The "snapshots" command lists all snapshots stored in the repository.
`,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runSnapshots(snapshotOptions, globalOptions, args)
	},
}

// SnapshotOptions bundle all options for the snapshots command.
type SnapshotOptions struct {
	Host  string
	Paths []string
}

var snapshotOptions SnapshotOptions

func init() {
	cmdRoot.AddCommand(cmdSnapshots)

	f := cmdSnapshots.Flags()
	f.StringVar(&snapshotOptions.Host, "host", "", "only print snapshots for this host")
	f.StringSliceVar(&snapshotOptions.Paths, "path", []string{}, "only print snapshots for this `path` (can be specified multiple times)")
}

func runSnapshots(opts SnapshotOptions, gopts GlobalOptions, args []string) error {
	if len(args) != 0 {
		return errors.Fatal("wrong number of arguments")
	}

	repo, err := OpenRepository(gopts)
	if err != nil {
		return err
	}

	if !gopts.NoLock {
		lock, err := lockRepo(repo)
		defer unlockRepo(lock)
		if err != nil {
			return err
		}
	}

	done := make(chan struct{})
	defer close(done)

	list := []*restic.Snapshot{}
	for id := range repo.List(restic.SnapshotFile, done) {
		sn, err := restic.LoadSnapshot(repo, id)
		if err != nil {
			Warnf("error loading snapshot %s: %v\n", id, err)
			continue
		}

		if restic.SamePaths(sn.Paths, opts.Paths) && (opts.Host == "" || opts.Host == sn.Hostname) {
			pos := sort.Search(len(list), func(i int) bool {
				return list[i].Time.After(sn.Time)
			})

			if pos < len(list) {
				list = append(list, nil)
				copy(list[pos+1:], list[pos:])
				list[pos] = sn
			} else {
				list = append(list, sn)
			}
		}

	}

	if gopts.JSON {
		err := printSnapshotsJSON(gopts.stdout, list)
		if err != nil {
			Warnf("error printing snapshot: %v\n", err)
		}
		return nil
	}
	printSnapshotsReadable(gopts.stdout, list)

	return nil
}

// printSnapshotsReadable prints a text table of the snapshots in list to stdout.
func printSnapshotsReadable(stdout io.Writer, list []*restic.Snapshot) {

	// Determine the max widths for host and tag.
	maxHost, maxTag := 10, 6
	for _, sn := range list {
		if len(sn.Hostname) > maxHost {
			maxHost = len(sn.Hostname)
		}
		for _, tag := range sn.Tags {
			if len(tag) > maxTag {
				maxTag = len(tag)
			}
		}
	}

	tab := NewTable()
	tab.Header = fmt.Sprintf("%-8s  %-19s  %-*s  %-*s  %-3s %s", "ID", "Date", -maxHost, "Host", -maxTag, "Tags", "", "Directory")
	tab.RowFormat = fmt.Sprintf("%%-8s  %%-19s  %%%ds  %%%ds  %%-3s %%s", -maxHost, -maxTag)

	for _, sn := range list {
		if len(sn.Paths) == 0 {
			continue
		}

		firstTag := ""
		if len(sn.Tags) > 0 {
			firstTag = sn.Tags[0]
		}

		rows := len(sn.Paths)

		treeElement := "   "
		if rows != 1 {
			treeElement = "┌──"
		}

		tab.Rows = append(tab.Rows, []interface{}{sn.ID().Str(), sn.Time.Format(TimeFormat), sn.Hostname, firstTag, treeElement, sn.Paths[0]})

		if len(sn.Tags) > rows {
			rows = len(sn.Tags)
		}

		for i := 1; i < rows; i++ {
			path := ""
			if len(sn.Paths) > i {
				path = sn.Paths[i]
			}

			tag := ""
			if len(sn.Tags) > i {
				tag = sn.Tags[i]
			}

			treeElement := "│"
			if i == (rows - 1) {
				treeElement = "└──"
			}

			tab.Rows = append(tab.Rows, []interface{}{"", "", "", tag, treeElement, path})
		}
	}

	tab.Write(stdout)
}

// Snapshot helps to print Snaphots as JSON
type Snapshot struct {
	*restic.Snapshot

	ID string `json:"id"`
}

// printSnapshotsJSON writes the JSON representation of list to stdout.
func printSnapshotsJSON(stdout io.Writer, list []*restic.Snapshot) error {

	var snapshots []Snapshot

	for _, sn := range list {

		k := Snapshot{
			Snapshot: sn,
			ID:       sn.ID().String(),
		}
		snapshots = append(snapshots, k)
	}

	return json.NewEncoder(stdout).Encode(snapshots)

}
<|endoftext|>


<commit_before>package htmlrefs

import (
	"strings"
	"testing"
)

func TestAll(t *testing.T) {

	// the required resources to be found in the example
	reqs := [...]Reference{
		{URI: "/favicon.ico", Token: "link"},
		{URI: "/foo.css", Token: "link"},
		{URI: "/async.js", Token: "script"},
		{URI: "/foo.js", Token: "script"},
		{URI: "/relative", Token: "a"},
		{URI: "http://example.com/absolute", Token: "a"},
		{URI: "//google.com/protocol-relative", Token: "a"},
		{URI: "#yep", Token: "a"},
		{URI: "/sample.jpg", Token: "a"},
		{URI: "/logo.svg", Token: "source"},
		{URI: "/logo.webp", Token: "source"},
		{URI: "/logo.png", Token: "source"},
		{URI: "/logo.gif", Token: "img"},
		{URI: "/vid.mp4", Token: "video"},
	}

	// example HTML reader
	reader := strings.NewReader(`
		<!DOCTYPE html>

			<title>example</title>
			<link rel="icon" href="/favicon.ico">
			<link rel="stylesheet" type="text/css" href="/foo.css">

			<picture>
			   <source type="image/svg+xml" srcset="/logo.svg" />
			   <source type="image/webp" srcset="/logo.webp" />
			   <source type="image/png" srcset="/logo.png" />

			</picture>

nope

			<video src="/vid.mp4"></video>

	`)

	// get the refs from the implementation
	refs := All(reader)

	if len(refs) != len(reqs) {
		t.Error("Wrong number of refs returned")
	}

	for i := 0; i < len(reqs); i++ {
		uri := reqs[i].URI
		ref := refs[i]

		if uri != ref {
			t.Errorf("Mismatch URI detected. need: %s , have: %s", uri, ref)
		}
	}
}
<commit_msg>format<commit_after>package htmlrefs

import (
	"strings"
	"testing"
)

func TestAll(t *testing.T) {

	// the required resources to be found in the example
	reqs := [...]Reference{
		{URI: "/favicon.ico", Token: "link"},
		{URI: "/foo.css", Token: "link"},
		{URI: "/async.js", Token: "script"},
		{URI: "/foo.js", Token: "script"},
		{URI: "/relative", Token: "a"},
		{URI: "http://example.com/absolute", Token: "a"},
		{URI: "//google.com/protocol-relative", Token: "a"},
		{URI: "#yep", Token: "a"},
		{URI: "/sample.jpg", Token: "a"},
		{URI: "/logo.svg", Token: "source"},
		{URI: "/logo.webp", Token: "source"},
		{URI: "/logo.png", Token: "source"},
		{URI: "/logo.gif", Token: "img"},
		{URI: "/vid.mp4", Token: "video"},
	}

	// example HTML reader
	reader := strings.NewReader(`
		<!DOCTYPE html>

				<title>example</title>
				<link rel="icon" href="/favicon.ico">
				<link rel="stylesheet" type="text/css" href="/foo.css">

				<picture>
				   <source type="image/svg+xml" srcset="/logo.svg" />
				   <source type="image/webp" srcset="/logo.webp" />
				   <source type="image/png" srcset="/logo.png" />

				</picture>

nope

				<video src="/vid.mp4"></video>

	`)

	// get the refs from the implementation
	refs := All(reader)

	if len(refs) != len(reqs) {
		t.Error("Wrong number of refs returned")
	}

	for i := 0; i < len(reqs); i++ {
		uri := reqs[i].URI
		ref := refs[i]

		if uri != ref {
			t.Errorf("Mismatch URI detected. need: %s , have: %s", uri, ref)
		}
	}
}
<|endoftext|>


<commit_before>package http

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"html/template"
	"net/http"
)

type Response struct {
	http.ResponseWriter
}

func NewResponse(rw http.ResponseWriter) *Response {
	return &Response{
		ResponseWriter: rw,
	}
}

func (r *Response) WriteJSON(v interface{}) (content []byte, err error) {
	content, err = json.Marshal(v)
	if err != nil {
		return nil, err
	}
	r.Header().Set("Content-Type", "application/json;charset=utf-8")
	_, err = r.Write(content)
	return
}

func (r *Response) WriteXML(v interface{}) (content []byte, err error) {
	content, err = xml.Marshal(v)
	if err != nil {
		return nil, err
	}
	r.Header().Set("Content-Type", "application/xml;charset=utf-8")
	_, err = r.Write([]byte(xml.Header))
	_, err = r.Write(content)
	return
}

func (r *Response) WriteJSONP(v interface{}, callback string) (content []byte, err error) {
	if callback == "" {
		err = errors.New("jsonp callback not found")
		return nil, err
	}
	r.Header().Set("Content-Type", "application/javascript;charset=utf-8")
	content, err = json.Marshal(v)
	if err != nil {
		return nil, err
	}

	content = append([]byte{'('}, content...)
	content = append(content, []byte{')'}...)
	content = append([]byte(template.JSEscapeString(callback)), content...)

	_, err = r.Write(content)
	return
}
<commit_msg>JSON media type doesn't support charset option.<commit_after>package http

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"html/template"
	"net/http"
)

type Response struct {
	http.ResponseWriter
}

func NewResponse(rw http.ResponseWriter) *Response {
	return &Response{
		ResponseWriter: rw,
	}
}

func (r *Response) WriteJSON(v interface{}) (content []byte, err error) {
	content, err = json.Marshal(v)
	if err != nil {
		return nil, err
	}
	r.Header().Set("Content-Type", "application/json")
	_, err = r.Write(content)
	return
}

func (r *Response) WriteXML(v interface{}) (content []byte, err error) {
	content, err = xml.Marshal(v)
	if err != nil {
		return nil, err
	}
	r.Header().Set("Content-Type", "application/xml;charset=utf-8")
	_, err = r.Write([]byte(xml.Header))
	_, err = r.Write(content)
	return
}

func (r *Response) WriteJSONP(v interface{}, callback string) (content []byte, err error) {
	if callback == "" {
		err = errors.New("jsonp callback not found")
		return nil, err
	}
	r.Header().Set("Content-Type", "application/javascript;charset=utf-8")
	content, err = json.Marshal(v)
	if err != nil {
		return nil, err
	}

	content = append([]byte{'('}, content...)
	content = append(content, []byte{')'}...)
	content = append([]byte(template.JSEscapeString(callback)), content...)

	_, err = r.Write(content)
	return
}
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"

	"github.com/hashicorp/hcl2/hcl"
	"github.com/hashicorp/terraform/tfdiags"
	"github.com/zclconf/go-cty/cty"
)

// evaluateResourceForEachExpression interprets a "for_each" argument on a resource.
//
// Returns a cty.Value map, and diagnostics if necessary. It will return nil if
// the expression is nil, and is used to distinguish between an unset for_each and an
// empty map
func evaluateResourceForEachExpression(expr hcl.Expression, ctx EvalContext) (forEach map[string]cty.Value, diags tfdiags.Diagnostics) {
	forEachMap, known, diags := evaluateResourceForEachExpressionKnown(expr, ctx)
	if !known {
		// Attach a diag as we do with count, with the same downsides
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid forEach argument",
			Detail:   `The "for_each" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the for_each depends on.`,
		})
	}
	return forEachMap, diags
}

// evaluateResourceForEachExpressionKnown is like evaluateResourceForEachExpression
// except that it handles an unknown result by returning an empty map and
// a known = false, rather than by reporting the unknown value as an error
// diagnostic.
func evaluateResourceForEachExpressionKnown(expr hcl.Expression, ctx EvalContext) (forEach map[string]cty.Value, known bool, diags tfdiags.Diagnostics) {
	if expr == nil {
		return nil, true, nil
	}

	forEachVal, forEachDiags := ctx.EvaluateExpr(expr, cty.DynamicPseudoType, nil)
	diags = diags.Append(forEachDiags)
	if diags.HasErrors() {
		return nil, true, diags
	}

	switch {
	case forEachVal.IsNull():
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid for_each argument",
			Detail:   `The given "for_each" argument value is unsuitable: the given "for_each" argument value is null. A map, or set of strings is allowed.`,
			Subject:  expr.Range().Ptr(),
		})
		return nil, true, diags
	case !forEachVal.IsKnown():
		return map[string]cty.Value{}, false, diags
	}

	if !forEachVal.CanIterateElements() || forEachVal.Type().IsListType() {
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid for_each argument",
			Detail:   fmt.Sprintf(`The given "for_each" argument value is unsuitable: the "for_each" argument must be a map, or set of strings, and you have provided a value of type %s.`, forEachVal.Type().FriendlyName()),
			Subject:  expr.Range().Ptr(),
		})
		return nil, true, diags
	}

	if forEachVal.Type().IsSetType() {
		if forEachVal.Type().ElementType() != cty.String {
			diags = diags.Append(&hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Invalid for_each set argument",
				Detail:   fmt.Sprintf(`The given "for_each" argument value is unsuitable: "for_each" supports maps and sets of strings, but you have provided a set containing type %s.`, forEachVal.Type().ElementType().FriendlyName()),
				Subject:  expr.Range().Ptr(),
			})
			return nil, true, diags
		}
	}

	// If the map is empty ({}), return an empty map, because cty will return nil when representing {} AsValueMap
	if forEachVal.LengthInt() == 0 {
		return map[string]cty.Value{}, true, diags
	}

	return forEachVal.AsValueMap(), true, nil
}
<commit_msg>make validation on for_each argument more precise<commit_after>package terraform

import (
	"fmt"

	"github.com/hashicorp/hcl2/hcl"
	"github.com/hashicorp/terraform/tfdiags"
	"github.com/zclconf/go-cty/cty"
)

// evaluateResourceForEachExpression interprets a "for_each" argument on a resource.
//
// Returns a cty.Value map, and diagnostics if necessary. It will return nil if
// the expression is nil, and is used to distinguish between an unset for_each and an
// empty map
func evaluateResourceForEachExpression(expr hcl.Expression, ctx EvalContext) (forEach map[string]cty.Value, diags tfdiags.Diagnostics) {
	forEachMap, known, diags := evaluateResourceForEachExpressionKnown(expr, ctx)
	if !known {
		// Attach a diag as we do with count, with the same downsides
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid forEach argument",
			Detail:   `The "for_each" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the for_each depends on.`,
		})
	}
	return forEachMap, diags
}

// evaluateResourceForEachExpressionKnown is like evaluateResourceForEachExpression
// except that it handles an unknown result by returning an empty map and
// a known = false, rather than by reporting the unknown value as an error
// diagnostic.
func evaluateResourceForEachExpressionKnown(expr hcl.Expression, ctx EvalContext) (forEach map[string]cty.Value, known bool, diags tfdiags.Diagnostics) {
	if expr == nil {
		return nil, true, nil
	}

	forEachVal, forEachDiags := ctx.EvaluateExpr(expr, cty.DynamicPseudoType, nil)
	diags = diags.Append(forEachDiags)
	if diags.HasErrors() {
		return nil, true, diags
	}

	switch {
	case forEachVal.IsNull():
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid for_each argument",
			Detail:   `The given "for_each" argument value is unsuitable: the given "for_each" argument value is null. A map, or set of strings is allowed.`,
			Subject:  expr.Range().Ptr(),
		})
		return nil, true, diags
	case !forEachVal.IsKnown():
		return map[string]cty.Value{}, false, diags
	}

	if !forEachVal.Type().IsMapType() && !forEachVal.Type().IsSetType() {
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid for_each argument",
			Detail:   fmt.Sprintf(`The given "for_each" argument value is unsuitable: the "for_each" argument must be a map, or set of strings, and you have provided a value of type %s.`, forEachVal.Type().FriendlyName()),
			Subject:  expr.Range().Ptr(),
		})
		return nil, true, diags
	}

	if forEachVal.Type().IsSetType() {
		if forEachVal.Type().ElementType() != cty.String {
			diags = diags.Append(&hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Invalid for_each set argument",
				Detail:   fmt.Sprintf(`The given "for_each" argument value is unsuitable: "for_each" supports maps and sets of strings, but you have provided a set containing type %s.`, forEachVal.Type().ElementType().FriendlyName()),
				Subject:  expr.Range().Ptr(),
			})
			return nil, true, diags
		}
	}

	// If the map is empty ({}), return an empty map, because cty will return nil when representing {} AsValueMap
	if forEachVal.LengthInt() == 0 {
		return map[string]cty.Value{}, true, diags
	}

	return forEachVal.AsValueMap(), true, nil
}
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"

	"github.com/hashicorp/hcl2/hcl"
	"github.com/hashicorp/terraform/tfdiags"
	"github.com/zclconf/go-cty/cty"
)

// evaluateResourceForEachExpression interprets a "for_each" argument on a resource.
//
// Returns a cty.Value map, and diagnostics if necessary. It will return nil if
// the expression is nil, and is used to distinguish between an unset for_each and an
// empty map
func evaluateResourceForEachExpression(expr hcl.Expression, ctx EvalContext) (forEach map[string]cty.Value, diags tfdiags.Diagnostics) {
	forEachMap, known, diags := evaluateResourceForEachExpressionKnown(expr, ctx)
	if !known {
		// Attach a diag as we do with count, with the same downsides
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid for_each argument",
			Detail:   `The "for_each" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the for_each depends on.`,
		})
	}
	return forEachMap, diags
}

// evaluateResourceForEachExpressionKnown is like evaluateResourceForEachExpression
// except that it handles an unknown result by returning an empty map and
// a known = false, rather than by reporting the unknown value as an error
// diagnostic.
func evaluateResourceForEachExpressionKnown(expr hcl.Expression, ctx EvalContext) (forEach map[string]cty.Value, known bool, diags tfdiags.Diagnostics) {
	if expr == nil {
		return nil, true, nil
	}

	forEachVal, forEachDiags := ctx.EvaluateExpr(expr, cty.DynamicPseudoType, nil)
	diags = diags.Append(forEachDiags)
	if diags.HasErrors() {
		return nil, true, diags
	}

	switch {
	case forEachVal.IsNull():
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid for_each argument",
			Detail:   `The given "for_each" argument value is unsuitable: the given "for_each" argument value is null. A map, or set of strings is allowed.`,
			Subject:  expr.Range().Ptr(),
		})
		return nil, true, diags
	case !forEachVal.IsKnown():
		return map[string]cty.Value{}, false, diags
	}

	if !forEachVal.CanIterateElements() || forEachVal.Type().IsListType() || forEachVal.Type().IsTupleType() {
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid for_each argument",
			Detail:   fmt.Sprintf(`The given "for_each" argument value is unsuitable: the "for_each" argument must be a map, or set of strings, and you have provided a value of type %s.`, forEachVal.Type().FriendlyName()),
			Subject:  expr.Range().Ptr(),
		})
		return nil, true, diags
	}

	if forEachVal.Type().IsSetType() {
		if forEachVal.Type().ElementType() != cty.String {
			diags = diags.Append(&hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Invalid for_each set argument",
				Detail:   fmt.Sprintf(`The given "for_each" argument value is unsuitable: "for_each" supports maps and sets of strings, but you have provided a set containing type %s.`, forEachVal.Type().ElementType().FriendlyName()),
				Subject:  expr.Range().Ptr(),
			})
			return nil, true, diags
		}

		// A set may contain unknown values that must be
		// discovered by checking with IsWhollyKnown (which iterates through the
		// structure), while for maps in cty, keys can never be unknown or null,
		// thus the earlier IsKnown check suffices for maps
		if !forEachVal.IsWhollyKnown() {
			return map[string]cty.Value{}, false, diags
		}
	}

	// If the map is empty ({}), return an empty map, because cty will return nil when representing {} AsValueMap
	if forEachVal.LengthInt() == 0 {
		return map[string]cty.Value{}, true, diags
	}

	return forEachVal.AsValueMap(), true, nil
}
<commit_msg>Add source addressing to make error more useful<commit_after>package terraform

import (
	"fmt"

	"github.com/hashicorp/hcl2/hcl"
	"github.com/hashicorp/terraform/tfdiags"
	"github.com/zclconf/go-cty/cty"
)

// evaluateResourceForEachExpression interprets a "for_each" argument on a resource.
//
// Returns a cty.Value map, and diagnostics if necessary. It will return nil if
// the expression is nil, and is used to distinguish between an unset for_each and an
// empty map
func evaluateResourceForEachExpression(expr hcl.Expression, ctx EvalContext) (forEach map[string]cty.Value, diags tfdiags.Diagnostics) {
	forEachMap, known, diags := evaluateResourceForEachExpressionKnown(expr, ctx)
	if !known {
		// Attach a diag as we do with count, with the same downsides
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid for_each argument",
			Detail:   `The "for_each" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the for_each depends on.`,
			Subject:  expr.Range().Ptr(),
		})
	}
	return forEachMap, diags
}

// evaluateResourceForEachExpressionKnown is like evaluateResourceForEachExpression
// except that it handles an unknown result by returning an empty map and
// a known = false, rather than by reporting the unknown value as an error
// diagnostic.
func evaluateResourceForEachExpressionKnown(expr hcl.Expression, ctx EvalContext) (forEach map[string]cty.Value, known bool, diags tfdiags.Diagnostics) {
	if expr == nil {
		return nil, true, nil
	}

	forEachVal, forEachDiags := ctx.EvaluateExpr(expr, cty.DynamicPseudoType, nil)
	diags = diags.Append(forEachDiags)
	if diags.HasErrors() {
		return nil, true, diags
	}

	switch {
	case forEachVal.IsNull():
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid for_each argument",
			Detail:   `The given "for_each" argument value is unsuitable: the given "for_each" argument value is null. A map, or set of strings is allowed.`,
			Subject:  expr.Range().Ptr(),
		})
		return nil, true, diags
	case !forEachVal.IsKnown():
		return map[string]cty.Value{}, false, diags
	}

	if !forEachVal.CanIterateElements() || forEachVal.Type().IsListType() || forEachVal.Type().IsTupleType() {
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid for_each argument",
			Detail:   fmt.Sprintf(`The given "for_each" argument value is unsuitable: the "for_each" argument must be a map, or set of strings, and you have provided a value of type %s.`, forEachVal.Type().FriendlyName()),
			Subject:  expr.Range().Ptr(),
		})
		return nil, true, diags
	}

	if forEachVal.Type().IsSetType() {
		if forEachVal.Type().ElementType() != cty.String {
			diags = diags.Append(&hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Invalid for_each set argument",
				Detail:   fmt.Sprintf(`The given "for_each" argument value is unsuitable: "for_each" supports maps and sets of strings, but you have provided a set containing type %s.`, forEachVal.Type().ElementType().FriendlyName()),
				Subject:  expr.Range().Ptr(),
			})
			return nil, true, diags
		}

		// A set may contain unknown values that must be
		// discovered by checking with IsWhollyKnown (which iterates through the
		// structure), while for maps in cty, keys can never be unknown or null,
		// thus the earlier IsKnown check suffices for maps
		if !forEachVal.IsWhollyKnown() {
			return map[string]cty.Value{}, false, diags
		}
	}

	// If the map is empty ({}), return an empty map, because cty will return nil when representing {} AsValueMap
	if forEachVal.LengthInt() == 0 {
		return map[string]cty.Value{}, true, diags
	}

	return forEachVal.AsValueMap(), true, nil
}
<|endoftext|>


<commit_before>package terraform

import (
	"encoding/json"
	"fmt"
	"sync"

	"github.com/zclconf/go-cty/cty"

	"github.com/hashicorp/terraform/config/hcl2shim"
	"github.com/hashicorp/terraform/providers"
	"github.com/hashicorp/terraform/tfdiags"
)

var _ providers.Interface = (*MockProvider)(nil)

// MockProvider implements providers.Interface but mocks out all the
// calls for testing purposes.
type MockProvider struct {
	sync.Mutex

	// Anything you want, in case you need to store extra data with the mock.
	Meta interface{}

	GetSchemaCalled bool
	GetSchemaReturn *ProviderSchema // This is using ProviderSchema directly rather than providers.GetSchemaResponse for compatibility with old tests

	ValidateProviderConfigCalled   bool
	ValidateProviderConfigResponse providers.ValidateProviderConfigResponse
	ValidateProviderConfigRequest  providers.ValidateProviderConfigRequest
	ValidateProviderConfigFn       func(providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse

	ValidateResourceTypeConfigCalled   bool
	ValidateResourceTypeConfigTypeName string
	ValidateResourceTypeConfigResponse providers.ValidateResourceTypeConfigResponse
	ValidateResourceTypeConfigRequest  providers.ValidateResourceTypeConfigRequest
	ValidateResourceTypeConfigFn       func(providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse

	ValidateDataSourceConfigCalled   bool
	ValidateDataSourceConfigTypeName string
	ValidateDataSourceConfigResponse providers.ValidateDataSourceConfigResponse
	ValidateDataSourceConfigRequest  providers.ValidateDataSourceConfigRequest
	ValidateDataSourceConfigFn       func(providers.ValidateDataSourceConfigRequest) providers.ValidateDataSourceConfigResponse

	UpgradeResourceStateCalled   bool
	UpgradeResourceStateTypeName string
	UpgradeResourceStateResponse providers.UpgradeResourceStateResponse
	UpgradeResourceStateRequest  providers.UpgradeResourceStateRequest
	UpgradeResourceStateFn       func(providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse

	ConfigureCalled   bool
	ConfigureResponse providers.ConfigureResponse
	ConfigureRequest  providers.ConfigureRequest
	ConfigureNewFn    func(providers.ConfigureRequest) providers.ConfigureResponse // Named ConfigureNewFn so we can still have the legacy ConfigureFn declared below

	StopCalled   bool
	StopFn       func() error
	StopResponse error

	ReadResourceCalled   bool
	ReadResourceResponse providers.ReadResourceResponse
	ReadResourceRequest  providers.ReadResourceRequest
	ReadResourceFn       func(providers.ReadResourceRequest) providers.ReadResourceResponse

	PlanResourceChangeCalled   bool
	PlanResourceChangeResponse providers.PlanResourceChangeResponse
	PlanResourceChangeRequest  providers.PlanResourceChangeRequest
	PlanResourceChangeFn       func(providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse

	ApplyResourceChangeCalled   bool
	ApplyResourceChangeResponse providers.ApplyResourceChangeResponse
	ApplyResourceChangeRequest  providers.ApplyResourceChangeRequest
	ApplyResourceChangeFn       func(providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse

	ImportResourceStateCalled   bool
	ImportResourceStateResponse providers.ImportResourceStateResponse
	ImportResourceStateRequest  providers.ImportResourceStateRequest
	ImportResourceStateFn       func(providers.ImportResourceStateRequest) providers.ImportResourceStateResponse
	// Legacy return type for existing tests, which will be shimmed into an
	// ImportResourceStateResponse if set
	ImportStateReturn []*InstanceState

	ReadDataSourceCalled   bool
	ReadDataSourceResponse providers.ReadDataSourceResponse
	ReadDataSourceRequest  providers.ReadDataSourceRequest
	ReadDataSourceFn       func(providers.ReadDataSourceRequest) providers.ReadDataSourceResponse

	CloseCalled bool
	CloseError  error

	// Legacy callbacks: if these are set, we will shim incoming calls for
	// new-style methods to these old-fashioned terraform.ResourceProvider
	// mock callbacks, for the benefit of older tests that were written against
	// the old mock API.
	ValidateFn  func(c *ResourceConfig) (ws []string, es []error)
	ConfigureFn func(c *ResourceConfig) error
	DiffFn      func(info *InstanceInfo, s *InstanceState, c *ResourceConfig) (*InstanceDiff, error)
	ApplyFn     func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error)
}

func (p *MockProvider) GetSchema() providers.GetSchemaResponse {
	p.Lock()
	defer p.Unlock()

	p.GetSchemaCalled = true
	ret := providers.GetSchemaResponse{
		Provider: providers.Schema{
			Block: p.GetSchemaReturn.Provider,
		},
		DataSources:   map[string]providers.Schema{},
		ResourceTypes: map[string]providers.Schema{},
	}
	for n, s := range p.GetSchemaReturn.DataSources {
		ret.DataSources[n] = providers.Schema{
			Block: s,
		}
	}
	for n, s := range p.GetSchemaReturn.ResourceTypes {
		ret.ResourceTypes[n] = providers.Schema{
			Block: s,
		}
	}
	return ret
}

func (p *MockProvider) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse {
	p.Lock()
	defer p.Unlock()

	p.ValidateProviderConfigCalled = true
	p.ValidateProviderConfigRequest = r
	if p.ValidateProviderConfigFn != nil {
		return p.ValidateProviderConfigFn(r)
	}
	return p.ValidateProviderConfigResponse
}

func (p *MockProvider) ValidateResourceTypeConfig(r providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse {
	p.Lock()
	defer p.Unlock()

	p.ValidateResourceTypeConfigCalled = true
	p.ValidateResourceTypeConfigRequest = r

	if p.ValidateFn != nil {
		return providers.ValidateResourceTypeConfigResponse{
			Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("legacy ValidateFn handling in MockProvider not actually implemented yet")),
		}
	}
	if p.ValidateResourceTypeConfigFn != nil {
		return p.ValidateResourceTypeConfigFn(r)
	}

	return p.ValidateResourceTypeConfigResponse
}

func (p *MockProvider) ValidateDataSourceConfig(r providers.ValidateDataSourceConfigRequest) providers.ValidateDataSourceConfigResponse {
	p.Lock()
	defer p.Unlock()

	p.ValidateDataSourceConfigCalled = true
	p.ValidateDataSourceConfigRequest = r

	if p.ValidateDataSourceConfigFn != nil {
		return p.ValidateDataSourceConfigFn(r)
	}

	return p.ValidateDataSourceConfigResponse
}

func (p *MockProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse {
	p.Lock()
	defer p.Unlock()

	p.UpgradeResourceStateCalled = true
	p.UpgradeResourceStateRequest = r

	if p.UpgradeResourceStateFn != nil {
		return p.UpgradeResourceStateFn(r)
	}

	return p.UpgradeResourceStateResponse
}

func (p *MockProvider) Configure(r providers.ConfigureRequest) providers.ConfigureResponse {
	p.Lock()
	defer p.Unlock()

	p.ConfigureCalled = true
	p.ConfigureRequest = r

	if p.ConfigureFn != nil {
		return providers.ConfigureResponse{
			Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("legacy ConfigureFn handling in MockProvider not actually implemented yet")),
		}
	}
	if p.ConfigureNewFn != nil {
		return p.ConfigureNewFn(r)
	}

	return p.ConfigureResponse
}

func (p *MockProvider) Stop() error {
	p.Lock()
	defer p.Unlock()

	p.StopCalled = true
	if p.StopFn != nil {
		return p.StopFn()
	}

	return p.StopResponse
}

func (p *MockProvider) ReadResource(r providers.ReadResourceRequest) providers.ReadResourceResponse {
	p.Lock()
	defer p.Unlock()

	p.ReadResourceCalled = true
	p.ReadResourceRequest = r

	if p.ReadResourceFn != nil {
		return p.ReadResourceFn(r)
	}

	return p.ReadResourceResponse
}

func (p *MockProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
	p.PlanResourceChangeCalled = true
	p.PlanResourceChangeRequest = r

	if p.DiffFn != nil {
		ps := p.GetSchema()
		if ps.ResourceTypes == nil || ps.ResourceTypes[r.TypeName].Block == nil {
			return providers.PlanResourceChangeResponse{
				Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Printf("mock provider has no schema for resource type %s", r.TypeName)),
			}
		}
		schema := ps.ResourceTypes[r.TypeName].Block
		info := &InstanceInfo{
			Type: r.TypeName,
		}
		priorState := NewInstanceStateShimmedFromValue(r.PriorState, 0)
		cfg := NewResourceConfigShimmed(r.Config, schema)
		legacyDiff, err := p.DiffFn(info, priorState, cfg)

		var res providers.PlanResourceChangeResponse
		res.PlannedState = cty.NullVal(schema.ImpliedType()) // mimic how an absent value would arrive over the GRPC channel
		if err != nil {
			res.Diagnostics = res.Diagnostics.Append(err)
		}
		if legacyDiff != nil {
			newVal, err := legacyDiff.ApplyToValue(r.PriorState, schema)
			if err != nil {
				res.Diagnostics = res.Diagnostics.Append(err)
			}
			res.PlannedState = newVal

			var requiresNew []string
			for attr, d := range legacyDiff.Attributes {
				if d.RequiresNew {
					requiresNew = append(requiresNew, attr)
				}
			}
			requiresReplace, err := hcl2shim.RequiresReplace(requiresNew, schema.ImpliedType())
			if err != nil {
				res.Diagnostics = res.Diagnostics.Append(err)
			}
			res.RequiresReplace = requiresReplace
		}
		return res

	}
	if p.PlanResourceChangeFn != nil {
		return p.PlanResourceChangeFn(r)
	}

	return p.PlanResourceChangeResponse
}

func (p *MockProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
	p.Lock()
	p.ApplyResourceChangeCalled = true
	p.ApplyResourceChangeRequest = r
	p.Unlock()

	if p.DiffFn != nil {
		return providers.ApplyResourceChangeResponse{
			Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("legacy ApplyFn handling in MockProvider not actually implemented yet")),
		}
	}
	if p.ApplyResourceChangeFn != nil {
		return p.ApplyResourceChangeFn(r)
	}

	return p.ApplyResourceChangeResponse
}

func (p *MockProvider) ImportResourceState(r providers.ImportResourceStateRequest) providers.ImportResourceStateResponse {
	p.Lock()
	defer p.Unlock()

	if p.ImportStateReturn != nil {
		for _, is := range p.ImportStateReturn {
			if is.Attributes == nil {
				is.Attributes = make(map[string]string)
			}
			is.Attributes["id"] = is.ID
			schema := p.GetSchemaReturn.ResourceTypes[r.TypeName]
			private, err := json.Marshal(is.Meta)
			if err != nil {
				panic(err)
			}

			state, err := hcl2shim.HCL2ValueFromFlatmap(is.Attributes, schema.ImpliedType())
			if err != nil {
				panic(err)
			}
			p.ImportResourceStateResponse.ImportedResources = append(
				p.ImportResourceStateResponse.ImportedResources,
				providers.ImportedResource{
					TypeName: r.TypeName,
					State:    state,
					Private:  private,
				})
		}
	}

	p.ImportResourceStateCalled = true
	p.ImportResourceStateRequest = r
	if p.ImportResourceStateFn != nil {
		return p.ImportResourceStateFn(r)
	}

	return p.ImportResourceStateResponse
}

func (p *MockProvider) ReadDataSource(r providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
	p.Lock()
	defer p.Unlock()

	p.ReadDataSourceCalled = true
	p.ReadDataSourceRequest = r

	if p.ReadDataSourceFn != nil {
		return p.ReadDataSourceFn(r)
	}

	return p.ReadDataSourceResponse
}

func (p *MockProvider) Close() error {
	p.CloseCalled = true
	return p.CloseError
}
<commit_msg>core: Shim to old p.ApplyFn interface in MockProvider<commit_after>package terraform

import (
	"encoding/json"
	"fmt"
	"sync"

	"github.com/zclconf/go-cty/cty"

	"github.com/hashicorp/terraform/config"
	"github.com/hashicorp/terraform/config/hcl2shim"
	"github.com/hashicorp/terraform/providers"
	"github.com/hashicorp/terraform/tfdiags"
)

var _ providers.Interface = (*MockProvider)(nil)

// MockProvider implements providers.Interface but mocks out all the
// calls for testing purposes.
type MockProvider struct {
	sync.Mutex

	// Anything you want, in case you need to store extra data with the mock.
	Meta interface{}

	GetSchemaCalled bool
	GetSchemaReturn *ProviderSchema // This is using ProviderSchema directly rather than providers.GetSchemaResponse for compatibility with old tests

	ValidateProviderConfigCalled   bool
	ValidateProviderConfigResponse providers.ValidateProviderConfigResponse
	ValidateProviderConfigRequest  providers.ValidateProviderConfigRequest
	ValidateProviderConfigFn       func(providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse

	ValidateResourceTypeConfigCalled   bool
	ValidateResourceTypeConfigTypeName string
	ValidateResourceTypeConfigResponse providers.ValidateResourceTypeConfigResponse
	ValidateResourceTypeConfigRequest  providers.ValidateResourceTypeConfigRequest
	ValidateResourceTypeConfigFn       func(providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse

	ValidateDataSourceConfigCalled   bool
	ValidateDataSourceConfigTypeName string
	ValidateDataSourceConfigResponse providers.ValidateDataSourceConfigResponse
	ValidateDataSourceConfigRequest  providers.ValidateDataSourceConfigRequest
	ValidateDataSourceConfigFn       func(providers.ValidateDataSourceConfigRequest) providers.ValidateDataSourceConfigResponse

	UpgradeResourceStateCalled   bool
	UpgradeResourceStateTypeName string
	UpgradeResourceStateResponse providers.UpgradeResourceStateResponse
	UpgradeResourceStateRequest  providers.UpgradeResourceStateRequest
	UpgradeResourceStateFn       func(providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse

	ConfigureCalled   bool
	ConfigureResponse providers.ConfigureResponse
	ConfigureRequest  providers.ConfigureRequest
	ConfigureNewFn    func(providers.ConfigureRequest) providers.ConfigureResponse // Named ConfigureNewFn so we can still have the legacy ConfigureFn declared below

	StopCalled   bool
	StopFn       func() error
	StopResponse error

	ReadResourceCalled   bool
	ReadResourceResponse providers.ReadResourceResponse
	ReadResourceRequest  providers.ReadResourceRequest
	ReadResourceFn       func(providers.ReadResourceRequest) providers.ReadResourceResponse

	PlanResourceChangeCalled   bool
	PlanResourceChangeResponse providers.PlanResourceChangeResponse
	PlanResourceChangeRequest  providers.PlanResourceChangeRequest
	PlanResourceChangeFn       func(providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse

	ApplyResourceChangeCalled   bool
	ApplyResourceChangeResponse providers.ApplyResourceChangeResponse
	ApplyResourceChangeRequest  providers.ApplyResourceChangeRequest
	ApplyResourceChangeFn       func(providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse

	ImportResourceStateCalled   bool
	ImportResourceStateResponse providers.ImportResourceStateResponse
	ImportResourceStateRequest  providers.ImportResourceStateRequest
	ImportResourceStateFn       func(providers.ImportResourceStateRequest) providers.ImportResourceStateResponse
	// Legacy return type for existing tests, which will be shimmed into an
	// ImportResourceStateResponse if set
	ImportStateReturn []*InstanceState

	ReadDataSourceCalled   bool
	ReadDataSourceResponse providers.ReadDataSourceResponse
	ReadDataSourceRequest  providers.ReadDataSourceRequest
	ReadDataSourceFn       func(providers.ReadDataSourceRequest) providers.ReadDataSourceResponse

	CloseCalled bool
	CloseError  error

	// Legacy callbacks: if these are set, we will shim incoming calls for
	// new-style methods to these old-fashioned terraform.ResourceProvider
	// mock callbacks, for the benefit of older tests that were written against
	// the old mock API.
	ValidateFn  func(c *ResourceConfig) (ws []string, es []error)
	ConfigureFn func(c *ResourceConfig) error
	DiffFn      func(info *InstanceInfo, s *InstanceState, c *ResourceConfig) (*InstanceDiff, error)
	ApplyFn     func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error)
}

func (p *MockProvider) GetSchema() providers.GetSchemaResponse {
	p.Lock()
	defer p.Unlock()

	p.GetSchemaCalled = true
	ret := providers.GetSchemaResponse{
		Provider: providers.Schema{
			Block: p.GetSchemaReturn.Provider,
		},
		DataSources:   map[string]providers.Schema{},
		ResourceTypes: map[string]providers.Schema{},
	}
	for n, s := range p.GetSchemaReturn.DataSources {
		ret.DataSources[n] = providers.Schema{
			Block: s,
		}
	}
	for n, s := range p.GetSchemaReturn.ResourceTypes {
		ret.ResourceTypes[n] = providers.Schema{
			Block: s,
		}
	}
	return ret
}

func (p *MockProvider) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse {
	p.Lock()
	defer p.Unlock()

	p.ValidateProviderConfigCalled = true
	p.ValidateProviderConfigRequest = r
	if p.ValidateProviderConfigFn != nil {
		return p.ValidateProviderConfigFn(r)
	}
	return p.ValidateProviderConfigResponse
}

func (p *MockProvider) ValidateResourceTypeConfig(r providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse {
	p.Lock()
	defer p.Unlock()

	p.ValidateResourceTypeConfigCalled = true
	p.ValidateResourceTypeConfigRequest = r

	if p.ValidateFn != nil {
		return providers.ValidateResourceTypeConfigResponse{
			Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("legacy ValidateFn handling in MockProvider not actually implemented yet")),
		}
	}
	if p.ValidateResourceTypeConfigFn != nil {
		return p.ValidateResourceTypeConfigFn(r)
	}

	return p.ValidateResourceTypeConfigResponse
}

func (p *MockProvider) ValidateDataSourceConfig(r providers.ValidateDataSourceConfigRequest) providers.ValidateDataSourceConfigResponse {
	p.Lock()
	defer p.Unlock()

	p.ValidateDataSourceConfigCalled = true
	p.ValidateDataSourceConfigRequest = r

	if p.ValidateDataSourceConfigFn != nil {
		return p.ValidateDataSourceConfigFn(r)
	}

	return p.ValidateDataSourceConfigResponse
}

func (p *MockProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse {
	p.Lock()
	defer p.Unlock()

	p.UpgradeResourceStateCalled = true
	p.UpgradeResourceStateRequest = r

	if p.UpgradeResourceStateFn != nil {
		return p.UpgradeResourceStateFn(r)
	}

	return p.UpgradeResourceStateResponse
}

func (p *MockProvider) Configure(r providers.ConfigureRequest) providers.ConfigureResponse {
	p.Lock()
	defer p.Unlock()

	p.ConfigureCalled = true
	p.ConfigureRequest = r

	if p.ConfigureFn != nil {
		return providers.ConfigureResponse{
			Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("legacy ConfigureFn handling in MockProvider not actually implemented yet")),
		}
	}
	if p.ConfigureNewFn != nil {
		return p.ConfigureNewFn(r)
	}

	return p.ConfigureResponse
}

func (p *MockProvider) Stop() error {
	p.Lock()
	defer p.Unlock()

	p.StopCalled = true
	if p.StopFn != nil {
		return p.StopFn()
	}

	return p.StopResponse
}

func (p *MockProvider) ReadResource(r providers.ReadResourceRequest) providers.ReadResourceResponse {
	p.Lock()
	defer p.Unlock()

	p.ReadResourceCalled = true
	p.ReadResourceRequest = r

	if p.ReadResourceFn != nil {
		return p.ReadResourceFn(r)
	}

	return p.ReadResourceResponse
}

func (p *MockProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
	p.PlanResourceChangeCalled = true
	p.PlanResourceChangeRequest = r

	if p.DiffFn != nil {
		ps := p.GetSchema()
		if ps.ResourceTypes == nil || ps.ResourceTypes[r.TypeName].Block == nil {
			return providers.PlanResourceChangeResponse{
				Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Printf("mock provider has no schema for resource type %s", r.TypeName)),
			}
		}
		schema := ps.ResourceTypes[r.TypeName].Block
		info := &InstanceInfo{
			Type: r.TypeName,
		}
		priorState := NewInstanceStateShimmedFromValue(r.PriorState, 0)
		cfg := NewResourceConfigShimmed(r.Config, schema)
		legacyDiff, err := p.DiffFn(info, priorState, cfg)

		var res providers.PlanResourceChangeResponse
		res.PlannedState = cty.NullVal(schema.ImpliedType()) // mimic how an absent value would arrive over the GRPC channel
		if err != nil {
			res.Diagnostics = res.Diagnostics.Append(err)
		}
		if legacyDiff != nil {
			newVal, err := legacyDiff.ApplyToValue(r.PriorState, schema)
			if err != nil {
				res.Diagnostics = res.Diagnostics.Append(err)
			}
			res.PlannedState = newVal

			var requiresNew []string
			for attr, d := range legacyDiff.Attributes {
				if d.RequiresNew {
					requiresNew = append(requiresNew, attr)
				}
			}
			requiresReplace, err := hcl2shim.RequiresReplace(requiresNew, schema.ImpliedType())
			if err != nil {
				res.Diagnostics = res.Diagnostics.Append(err)
			}
			res.RequiresReplace = requiresReplace
		}
		return res

	}
	if p.PlanResourceChangeFn != nil {
		return p.PlanResourceChangeFn(r)
	}

	return p.PlanResourceChangeResponse
}

func (p *MockProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
	p.Lock()
	p.ApplyResourceChangeCalled = true
	p.ApplyResourceChangeRequest = r
	p.Unlock()

	if p.ApplyFn != nil {
		// ApplyFn is a special callback fashioned after our old provider
		// interface, which expected to be given an actual diff rather than
		// separate old/new values to apply. Therefore we need to approximate
		// a diff here well enough that _most_ of our legacy ApplyFns in old
		// tests still see the behavior they are expecting. New tests should
		// not use this, and should instead use ApplyResourceChangeFn directly.
		providerSchema := p.GetSchema()
		schema, ok := providerSchema.ResourceTypes[r.TypeName]
		if !ok {
			return providers.ApplyResourceChangeResponse{
				Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("no mocked schema available for resource type %s", r.TypeName)),
			}
		}

		info := &InstanceInfo{
			Type: r.TypeName,
		}

		priorVal := r.PriorState
		plannedVal := r.PlannedState
		priorMap := hcl2shim.FlatmapValueFromHCL2(priorVal)
		plannedMap := hcl2shim.FlatmapValueFromHCL2(plannedVal)
		s := NewInstanceStateShimmedFromValue(priorVal, 0)
		d := &InstanceDiff{
			Attributes: make(map[string]*ResourceAttrDiff),
		}
		if plannedMap == nil { // destroying, then
			d.Destroy = true
			// Destroy diffs don't have any attribute diffs
		} else {
			if priorMap == nil { // creating, then
				// We'll just make an empty prior map to make things easier below.
				priorMap = make(map[string]string)
			}

			for k, new := range plannedMap {
				old := priorMap[k]
				newComputed := false
				if new == config.UnknownVariableValue {
					new = ""
					newComputed = true
				}
				d.Attributes[k] = &ResourceAttrDiff{
					Old:         old,
					New:         new,
					NewComputed: newComputed,
					Type:        DiffAttrInput, // not generally used in tests, so just hard-coded
				}
			}
			// Also need any attributes that were removed in "planned"
			for k, old := range priorMap {
				if _, ok := plannedMap[k]; ok {
					continue
				}
				d.Attributes[k] = &ResourceAttrDiff{
					Old:        old,
					NewRemoved: true,
					Type:       DiffAttrInput,
				}
			}
		}
		newState, err := p.ApplyFn(info, s, d)
		resp := providers.ApplyResourceChangeResponse{}
		if err != nil {
			resp.Diagnostics = resp.Diagnostics.Append(err)
		}
		newVal, err := newState.AttrsAsObjectValue(schema.Block.ImpliedType())
		if err != nil {
			resp.Diagnostics = resp.Diagnostics.Append(err)
		}
		resp.NewState = newVal

		return resp
	}
	if p.ApplyResourceChangeFn != nil {
		return p.ApplyResourceChangeFn(r)
	}

	return p.ApplyResourceChangeResponse
}

func (p *MockProvider) ImportResourceState(r providers.ImportResourceStateRequest) providers.ImportResourceStateResponse {
	p.Lock()
	defer p.Unlock()

	if p.ImportStateReturn != nil {
		for _, is := range p.ImportStateReturn {
			if is.Attributes == nil {
				is.Attributes = make(map[string]string)
			}
			is.Attributes["id"] = is.ID
			schema := p.GetSchemaReturn.ResourceTypes[r.TypeName]
			private, err := json.Marshal(is.Meta)
			if err != nil {
				panic(err)
			}

			state, err := hcl2shim.HCL2ValueFromFlatmap(is.Attributes, schema.ImpliedType())
			if err != nil {
				panic(err)
			}
			p.ImportResourceStateResponse.ImportedResources = append(
				p.ImportResourceStateResponse.ImportedResources,
				providers.ImportedResource{
					TypeName: r.TypeName,
					State:    state,
					Private:  private,
				})
		}
	}

	p.ImportResourceStateCalled = true
	p.ImportResourceStateRequest = r
	if p.ImportResourceStateFn != nil {
		return p.ImportResourceStateFn(r)
	}

	return p.ImportResourceStateResponse
}

func (p *MockProvider) ReadDataSource(r providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
	p.Lock()
	defer p.Unlock()

	p.ReadDataSourceCalled = true
	p.ReadDataSourceRequest = r

	if p.ReadDataSourceFn != nil {
		return p.ReadDataSourceFn(r)
	}

	return p.ReadDataSourceResponse
}

func (p *MockProvider) Close() error {
	p.CloseCalled = true
	return p.CloseError
}
<|endoftext|>


<commit_before>package terraform

import (
	"encoding/json"
	"fmt"
	"sync"

	"github.com/hashicorp/terraform/config/hcl2shim"
	"github.com/hashicorp/terraform/tfdiags"

	"github.com/hashicorp/terraform/providers"
)

var _ providers.Interface = (*MockProvider)(nil)

// MockProvider implements providers.Interface but mocks out all the
// calls for testing purposes.
type MockProvider struct {
	sync.Mutex

	// Anything you want, in case you need to store extra data with the mock.
	Meta interface{}

	GetSchemaCalled bool
	GetSchemaReturn *ProviderSchema // This is using ProviderSchema directly rather than providers.GetSchemaResponse for compatibility with old tests

	ValidateProviderConfigCalled   bool
	ValidateProviderConfigResponse providers.ValidateProviderConfigResponse
	ValidateProviderConfigRequest  providers.ValidateProviderConfigRequest
	ValidateProviderConfigFn       func(providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse

	ValidateResourceTypeConfigCalled   bool
	ValidateResourceTypeConfigTypeName string
	ValidateResourceTypeConfigResponse providers.ValidateResourceTypeConfigResponse
	ValidateResourceTypeConfigRequest  providers.ValidateResourceTypeConfigRequest
	ValidateResourceTypeConfigFn       func(providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse

	ValidateDataSourceConfigCalled   bool
	ValidateDataSourceConfigTypeName string
	ValidateDataSourceConfigResponse providers.ValidateDataSourceConfigResponse
	ValidateDataSourceConfigRequest  providers.ValidateDataSourceConfigRequest
	ValidateDataSourceConfigFn       func(providers.ValidateDataSourceConfigRequest) providers.ValidateDataSourceConfigResponse

	UpgradeResourceStateCalled   bool
	UpgradeResourceStateTypeName string
	UpgradeResourceStateResponse providers.UpgradeResourceStateResponse
	UpgradeResourceStateRequest  providers.UpgradeResourceStateRequest
	UpgradeResourceStateFn       func(providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse

	ConfigureCalled   bool
	ConfigureResponse providers.ConfigureResponse
	ConfigureRequest  providers.ConfigureRequest
	ConfigureNewFn    func(providers.ConfigureRequest) providers.ConfigureResponse // Named ConfigureNewFn so we can still have the legacy ConfigureFn declared below

	StopCalled   bool
	StopFn       func() error
	StopResponse error

	ReadResourceCalled   bool
	ReadResourceResponse providers.ReadResourceResponse
	ReadResourceRequest  providers.ReadResourceRequest
	ReadResourceFn       func(providers.ReadResourceRequest) providers.ReadResourceResponse

	PlanResourceChangeCalled   bool
	PlanResourceChangeResponse providers.PlanResourceChangeResponse
	PlanResourceChangeRequest  providers.PlanResourceChangeRequest
	PlanResourceChangeFn       func(providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse

	ApplyResourceChangeCalled   bool
	ApplyResourceChangeResponse providers.ApplyResourceChangeResponse
	ApplyResourceChangeRequest  providers.ApplyResourceChangeRequest
	ApplyResourceChangeFn       func(providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse

	ImportResourceStateCalled   bool
	ImportResourceStateResponse providers.ImportResourceStateResponse
	ImportResourceStateRequest  providers.ImportResourceStateRequest
	ImportResourceStateFn       func(providers.ImportResourceStateRequest) providers.ImportResourceStateResponse
	// Legacy return type for existing tests, which will be shimmed into an
	// ImportResourceStateResponse if set
	ImportStateReturn []*InstanceState

	ReadDataSourceCalled   bool
	ReadDataSourceResponse providers.ReadDataSourceResponse
	ReadDataSourceRequest  providers.ReadDataSourceRequest
	ReadDataSourceFn       func(providers.ReadDataSourceRequest) providers.ReadDataSourceResponse

	CloseCalled bool
	CloseError  error

	// Legacy callbacks: if these are set, we will shim incoming calls for
	// new-style methods to these old-fashioned terraform.ResourceProvider
	// mock callbacks, for the benefit of older tests that were written against
	// the old mock API.
	ValidateFn  func(c *ResourceConfig) (ws []string, es []error)
	ConfigureFn func(c *ResourceConfig) error
	DiffFn      func(info *InstanceInfo, s *InstanceState, c *ResourceConfig) (*InstanceDiff, error)
	ApplyFn     func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error)
}

func (p *MockProvider) GetSchema() providers.GetSchemaResponse {
	p.Lock()
	defer p.Unlock()

	p.GetSchemaCalled = true
	ret := providers.GetSchemaResponse{
		Provider: providers.Schema{
			Block: p.GetSchemaReturn.Provider,
		},
		DataSources:   map[string]providers.Schema{},
		ResourceTypes: map[string]providers.Schema{},
	}
	for n, s := range p.GetSchemaReturn.DataSources {
		ret.DataSources[n] = providers.Schema{
			Block: s,
		}
	}
	for n, s := range p.GetSchemaReturn.ResourceTypes {
		ret.ResourceTypes[n] = providers.Schema{
			Block: s,
		}
	}
	return ret
}

func (p *MockProvider) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse {
	p.Lock()
	defer p.Unlock()

	p.ValidateProviderConfigCalled = true
	p.ValidateProviderConfigRequest = r
	if p.ValidateProviderConfigFn != nil {
		return p.ValidateProviderConfigFn(r)
	}
	return p.ValidateProviderConfigResponse
}

func (p *MockProvider) ValidateResourceTypeConfig(r providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse {
	p.Lock()
	defer p.Unlock()

	p.ValidateResourceTypeConfigCalled = true
	p.ValidateResourceTypeConfigRequest = r

	if p.ValidateFn != nil {
		return providers.ValidateResourceTypeConfigResponse{
			Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("legacy ValidateFn handling in MockProvider not actually implemented yet")),
		}
	}
	if p.ValidateResourceTypeConfigFn != nil {
		return p.ValidateResourceTypeConfigFn(r)
	}

	return p.ValidateResourceTypeConfigResponse
}

func (p *MockProvider) ValidateDataSourceConfig(r providers.ValidateDataSourceConfigRequest) providers.ValidateDataSourceConfigResponse {
	p.Lock()
	defer p.Unlock()

	p.ValidateDataSourceConfigCalled = true
	p.ValidateDataSourceConfigRequest = r

	if p.ValidateDataSourceConfigFn != nil {
		return p.ValidateDataSourceConfigFn(r)
	}

	return p.ValidateDataSourceConfigResponse
}

func (p *MockProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse {
	p.Lock()
	defer p.Unlock()

	p.UpgradeResourceStateCalled = true
	p.UpgradeResourceStateRequest = r

	if p.UpgradeResourceStateFn != nil {
		return p.UpgradeResourceStateFn(r)
	}

	return p.UpgradeResourceStateResponse
}

func (p *MockProvider) Configure(r providers.ConfigureRequest) providers.ConfigureResponse {
	p.Lock()
	defer p.Unlock()

	p.ConfigureCalled = true
	p.ConfigureRequest = r

	if p.ConfigureFn != nil {
		return providers.ConfigureResponse{
			Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("legacy ConfigureFn handling in MockProvider not actually implemented yet")),
		}
	}
	if p.ConfigureNewFn != nil {
		return p.ConfigureNewFn(r)
	}

	return p.ConfigureResponse
}

func (p *MockProvider) Stop() error {
	p.Lock()
	defer p.Unlock()

	p.StopCalled = true
	if p.StopFn != nil {
		return p.StopFn()
	}

	return p.StopResponse
}

func (p *MockProvider) ReadResource(r providers.ReadResourceRequest) providers.ReadResourceResponse {
	p.Lock()
	defer p.Unlock()

	p.ReadResourceCalled = true
	p.ReadResourceRequest = r

	if p.ReadResourceFn != nil {
		return p.ReadResourceFn(r)
	}

	return p.ReadResourceResponse
}

func (p *MockProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
	p.PlanResourceChangeCalled = true
	p.PlanResourceChangeRequest = r

	if p.DiffFn != nil {
		return providers.PlanResourceChangeResponse{
			Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("legacy DiffFn handling in MockProvider not actually implemented yet")),
		}
	}
	if p.PlanResourceChangeFn != nil {
		return p.PlanResourceChangeFn(r)
	}

	return p.PlanResourceChangeResponse
}

func (p *MockProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
	p.Lock()
	p.ApplyResourceChangeCalled = true
	p.ApplyResourceChangeRequest = r
	p.Unlock()

	if p.DiffFn != nil {
		return providers.ApplyResourceChangeResponse{
			Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("legacy ApplyFn handling in MockProvider not actually implemented yet")),
		}
	}
	if p.ApplyResourceChangeFn != nil {
		return p.ApplyResourceChangeFn(r)
	}

	return p.ApplyResourceChangeResponse
}

func (p *MockProvider) ImportResourceState(r providers.ImportResourceStateRequest) providers.ImportResourceStateResponse {
	p.Lock()
	defer p.Unlock()

	if p.ImportStateReturn != nil {
		for _, is := range p.ImportStateReturn {
			if is.Attributes == nil {
				is.Attributes = make(map[string]string)
			}
			is.Attributes["id"] = is.ID
			schema := p.GetSchemaReturn.ResourceTypes[r.TypeName]
			private, err := json.Marshal(is.Meta)
			if err != nil {
				panic(err)
			}

			state, err := hcl2shim.HCL2ValueFromFlatmap(is.Attributes, schema.ImpliedType())
			if err != nil {
				panic(err)
			}
			p.ImportResourceStateResponse.ImportedResources = append(
				p.ImportResourceStateResponse.ImportedResources,
				providers.ImportedResource{
					TypeName: r.TypeName,
					State:    state,
					Private:  private,
				})
		}
	}

	p.ImportResourceStateCalled = true
	p.ImportResourceStateRequest = r
	if p.ImportResourceStateFn != nil {
		return p.ImportResourceStateFn(r)
	}

	return p.ImportResourceStateResponse
}

func (p *MockProvider) ReadDataSource(r providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
	p.Lock()
	defer p.Unlock()

	p.ReadDataSourceCalled = true
	p.ReadDataSourceRequest = r

	if p.ReadDataSourceFn != nil {
		return p.ReadDataSourceFn(r)
	}

	return p.ReadDataSourceResponse
}

func (p *MockProvider) Close() error {
	p.CloseCalled = true
	return p.CloseError
}
<commit_msg>core: MockProvider shim handling of DiffFn<commit_after>package terraform

import (
	"encoding/json"
	"fmt"
	"sync"

	"github.com/zclconf/go-cty/cty"

	"github.com/hashicorp/terraform/config/hcl2shim"
	"github.com/hashicorp/terraform/providers"
	"github.com/hashicorp/terraform/tfdiags"
)

var _ providers.Interface = (*MockProvider)(nil)

// MockProvider implements providers.Interface but mocks out all the
// calls for testing purposes.
type MockProvider struct {
	sync.Mutex

	// Anything you want, in case you need to store extra data with the mock.
	Meta interface{}

	GetSchemaCalled bool
	GetSchemaReturn *ProviderSchema // This is using ProviderSchema directly rather than providers.GetSchemaResponse for compatibility with old tests

	ValidateProviderConfigCalled   bool
	ValidateProviderConfigResponse providers.ValidateProviderConfigResponse
	ValidateProviderConfigRequest  providers.ValidateProviderConfigRequest
	ValidateProviderConfigFn       func(providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse

	ValidateResourceTypeConfigCalled   bool
	ValidateResourceTypeConfigTypeName string
	ValidateResourceTypeConfigResponse providers.ValidateResourceTypeConfigResponse
	ValidateResourceTypeConfigRequest  providers.ValidateResourceTypeConfigRequest
	ValidateResourceTypeConfigFn       func(providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse

	ValidateDataSourceConfigCalled   bool
	ValidateDataSourceConfigTypeName string
	ValidateDataSourceConfigResponse providers.ValidateDataSourceConfigResponse
	ValidateDataSourceConfigRequest  providers.ValidateDataSourceConfigRequest
	ValidateDataSourceConfigFn       func(providers.ValidateDataSourceConfigRequest) providers.ValidateDataSourceConfigResponse

	UpgradeResourceStateCalled   bool
	UpgradeResourceStateTypeName string
	UpgradeResourceStateResponse providers.UpgradeResourceStateResponse
	UpgradeResourceStateRequest  providers.UpgradeResourceStateRequest
	UpgradeResourceStateFn       func(providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse

	ConfigureCalled   bool
	ConfigureResponse providers.ConfigureResponse
	ConfigureRequest  providers.ConfigureRequest
	ConfigureNewFn    func(providers.ConfigureRequest) providers.ConfigureResponse // Named ConfigureNewFn so we can still have the legacy ConfigureFn declared below

	StopCalled   bool
	StopFn       func() error
	StopResponse error

	ReadResourceCalled   bool
	ReadResourceResponse providers.ReadResourceResponse
	ReadResourceRequest  providers.ReadResourceRequest
	ReadResourceFn       func(providers.ReadResourceRequest) providers.ReadResourceResponse

	PlanResourceChangeCalled   bool
	PlanResourceChangeResponse providers.PlanResourceChangeResponse
	PlanResourceChangeRequest  providers.PlanResourceChangeRequest
	PlanResourceChangeFn       func(providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse

	ApplyResourceChangeCalled   bool
	ApplyResourceChangeResponse providers.ApplyResourceChangeResponse
	ApplyResourceChangeRequest  providers.ApplyResourceChangeRequest
	ApplyResourceChangeFn       func(providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse

	ImportResourceStateCalled   bool
	ImportResourceStateResponse providers.ImportResourceStateResponse
	ImportResourceStateRequest  providers.ImportResourceStateRequest
	ImportResourceStateFn       func(providers.ImportResourceStateRequest) providers.ImportResourceStateResponse
	// Legacy return type for existing tests, which will be shimmed into an
	// ImportResourceStateResponse if set
	ImportStateReturn []*InstanceState

	ReadDataSourceCalled   bool
	ReadDataSourceResponse providers.ReadDataSourceResponse
	ReadDataSourceRequest  providers.ReadDataSourceRequest
	ReadDataSourceFn       func(providers.ReadDataSourceRequest) providers.ReadDataSourceResponse

	CloseCalled bool
	CloseError  error

	// Legacy callbacks: if these are set, we will shim incoming calls for
	// new-style methods to these old-fashioned terraform.ResourceProvider
	// mock callbacks, for the benefit of older tests that were written against
	// the old mock API.
	ValidateFn  func(c *ResourceConfig) (ws []string, es []error)
	ConfigureFn func(c *ResourceConfig) error
	DiffFn      func(info *InstanceInfo, s *InstanceState, c *ResourceConfig) (*InstanceDiff, error)
	ApplyFn     func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error)
}

func (p *MockProvider) GetSchema() providers.GetSchemaResponse {
	p.Lock()
	defer p.Unlock()

	p.GetSchemaCalled = true
	ret := providers.GetSchemaResponse{
		Provider: providers.Schema{
			Block: p.GetSchemaReturn.Provider,
		},
		DataSources:   map[string]providers.Schema{},
		ResourceTypes: map[string]providers.Schema{},
	}
	for n, s := range p.GetSchemaReturn.DataSources {
		ret.DataSources[n] = providers.Schema{
			Block: s,
		}
	}
	for n, s := range p.GetSchemaReturn.ResourceTypes {
		ret.ResourceTypes[n] = providers.Schema{
			Block: s,
		}
	}
	return ret
}

func (p *MockProvider) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse {
	p.Lock()
	defer p.Unlock()

	p.ValidateProviderConfigCalled = true
	p.ValidateProviderConfigRequest = r
	if p.ValidateProviderConfigFn != nil {
		return p.ValidateProviderConfigFn(r)
	}
	return p.ValidateProviderConfigResponse
}

func (p *MockProvider) ValidateResourceTypeConfig(r providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse {
	p.Lock()
	defer p.Unlock()

	p.ValidateResourceTypeConfigCalled = true
	p.ValidateResourceTypeConfigRequest = r

	if p.ValidateFn != nil {
		return providers.ValidateResourceTypeConfigResponse{
			Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("legacy ValidateFn handling in MockProvider not actually implemented yet")),
		}
	}
	if p.ValidateResourceTypeConfigFn != nil {
		return p.ValidateResourceTypeConfigFn(r)
	}

	return p.ValidateResourceTypeConfigResponse
}

func (p *MockProvider) ValidateDataSourceConfig(r providers.ValidateDataSourceConfigRequest) providers.ValidateDataSourceConfigResponse {
	p.Lock()
	defer p.Unlock()

	p.ValidateDataSourceConfigCalled = true
	p.ValidateDataSourceConfigRequest = r

	if p.ValidateDataSourceConfigFn != nil {
		return p.ValidateDataSourceConfigFn(r)
	}

	return p.ValidateDataSourceConfigResponse
}

func (p *MockProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse {
	p.Lock()
	defer p.Unlock()

	p.UpgradeResourceStateCalled = true
	p.UpgradeResourceStateRequest = r

	if p.UpgradeResourceStateFn != nil {
		return p.UpgradeResourceStateFn(r)
	}

	return p.UpgradeResourceStateResponse
}

func (p *MockProvider) Configure(r providers.ConfigureRequest) providers.ConfigureResponse {
	p.Lock()
	defer p.Unlock()

	p.ConfigureCalled = true
	p.ConfigureRequest = r

	if p.ConfigureFn != nil {
		return providers.ConfigureResponse{
			Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("legacy ConfigureFn handling in MockProvider not actually implemented yet")),
		}
	}
	if p.ConfigureNewFn != nil {
		return p.ConfigureNewFn(r)
	}

	return p.ConfigureResponse
}

func (p *MockProvider) Stop() error {
	p.Lock()
	defer p.Unlock()

	p.StopCalled = true
	if p.StopFn != nil {
		return p.StopFn()
	}

	return p.StopResponse
}

func (p *MockProvider) ReadResource(r providers.ReadResourceRequest) providers.ReadResourceResponse {
	p.Lock()
	defer p.Unlock()

	p.ReadResourceCalled = true
	p.ReadResourceRequest = r

	if p.ReadResourceFn != nil {
		return p.ReadResourceFn(r)
	}

	return p.ReadResourceResponse
}

func (p *MockProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
	p.PlanResourceChangeCalled = true
	p.PlanResourceChangeRequest = r

	if p.DiffFn != nil {
		ps := p.GetSchema()
		if ps.ResourceTypes == nil || ps.ResourceTypes[r.TypeName].Block == nil {
			return providers.PlanResourceChangeResponse{
				Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Printf("mock provider has no schema for resource type %s", r.TypeName)),
			}
		}
		schema := ps.ResourceTypes[r.TypeName].Block
		info := &InstanceInfo{
			Type: r.TypeName,
		}
		priorState := NewInstanceStateShimmedFromValue(r.PriorState, 0)
		cfg := NewResourceConfigShimmed(r.Config, schema)
		legacyDiff, err := p.DiffFn(info, priorState, cfg)

		var res providers.PlanResourceChangeResponse
		res.PlannedState = cty.NullVal(schema.ImpliedType()) // mimic how an absent value would arrive over the GRPC channel
		if err != nil {
			res.Diagnostics = res.Diagnostics.Append(err)
		}
		if legacyDiff != nil {
			newVal, err := legacyDiff.ApplyToValue(r.PriorState, schema)
			if err != nil {
				res.Diagnostics = res.Diagnostics.Append(err)
			}
			res.PlannedState = newVal

			var requiresNew []string
			for attr, d := range legacyDiff.Attributes {
				if d.RequiresNew {
					requiresNew = append(requiresNew, attr)
				}
			}
			requiresReplace, err := hcl2shim.RequiresReplace(requiresNew, schema.ImpliedType())
			if err != nil {
				res.Diagnostics = res.Diagnostics.Append(err)
			}
			res.RequiresReplace = requiresReplace
		}
		return res

	}
	if p.PlanResourceChangeFn != nil {
		return p.PlanResourceChangeFn(r)
	}

	return p.PlanResourceChangeResponse
}

func (p *MockProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
	p.Lock()
	p.ApplyResourceChangeCalled = true
	p.ApplyResourceChangeRequest = r
	p.Unlock()

	if p.DiffFn != nil {
		return providers.ApplyResourceChangeResponse{
			Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("legacy ApplyFn handling in MockProvider not actually implemented yet")),
		}
	}
	if p.ApplyResourceChangeFn != nil {
		return p.ApplyResourceChangeFn(r)
	}

	return p.ApplyResourceChangeResponse
}

func (p *MockProvider) ImportResourceState(r providers.ImportResourceStateRequest) providers.ImportResourceStateResponse {
	p.Lock()
	defer p.Unlock()

	if p.ImportStateReturn != nil {
		for _, is := range p.ImportStateReturn {
			if is.Attributes == nil {
				is.Attributes = make(map[string]string)
			}
			is.Attributes["id"] = is.ID
			schema := p.GetSchemaReturn.ResourceTypes[r.TypeName]
			private, err := json.Marshal(is.Meta)
			if err != nil {
				panic(err)
			}

			state, err := hcl2shim.HCL2ValueFromFlatmap(is.Attributes, schema.ImpliedType())
			if err != nil {
				panic(err)
			}
			p.ImportResourceStateResponse.ImportedResources = append(
				p.ImportResourceStateResponse.ImportedResources,
				providers.ImportedResource{
					TypeName: r.TypeName,
					State:    state,
					Private:  private,
				})
		}
	}

	p.ImportResourceStateCalled = true
	p.ImportResourceStateRequest = r
	if p.ImportResourceStateFn != nil {
		return p.ImportResourceStateFn(r)
	}

	return p.ImportResourceStateResponse
}

func (p *MockProvider) ReadDataSource(r providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
	p.Lock()
	defer p.Unlock()

	p.ReadDataSourceCalled = true
	p.ReadDataSourceRequest = r

	if p.ReadDataSourceFn != nil {
		return p.ReadDataSourceFn(r)
	}

	return p.ReadDataSourceResponse
}

func (p *MockProvider) Close() error {
	p.CloseCalled = true
	return p.CloseError
}
<|endoftext|>


<commit_before>package disc

import (
	"fmt"
	"go/parser"
	"path/filepath"

	"os"

	"go/token"

	"strings"
)

type disc struct {
	scope, root string
	fset        *token.FileSet
	gopath      string
}

func New(scope, root string) *disc {
	return &disc{
		scope:  scope,
		root:   root,
		fset:   token.NewFileSet(),
		gopath: os.Getenv("GOPATH"),
	}
}

func (d *disc) Discover() {
	d.traverse(d.root, make(map[string]bool, 1))
}

func (d *disc) traverse(p string, visited map[string]bool) {
	if visited[p] {
		return
	}
	visited[p] = true

	imports := d.imports(p)
	shortNames := d.shortNames(imports)
	fmt.Printf("Package %s imports: %v.\n", d.shortName(p), shortNames)
	for _, imp := range imports {
		if subpackage(imp, d.scope) {
			d.traverse(imp, visited)
		}
	}
}

// returns true if and only if p is a subpackage of q
func subpackage(p, q string) bool {
	return strings.HasPrefix(p, q)
}

func (d *disc) shortName(p string) string {
	if !subpackage(p, d.scope) {
		panic("failed")
	}
	return p[len(d.scope)+1:]
}

func (d *disc) shortNames(p []string) []string {
	result := []string{}
	for _, q := range p {
		s := d.shortName(q)
		result = append(result, s)
	}
	return result
}

func (d *disc) imports(pkg string) []string {
	i := make(map[string]bool, 1)
	pkgDir := filepath.Join(d.gopath, "src", pkg)
	pAsts, err := parser.ParseDir(d.fset, pkgDir, nil, parser.ImportsOnly)
	if err != nil {
		panic(err)
	}
	for _, ast := range pAsts {
		for _, f := range ast.Files {
			for _, is := range f.Imports {
				q := strings.Trim(is.Path.Value, `"`)
				if q != pkg && subpackage(q, d.scope) {
					i[q] = true
				}
			}
		}
	}
	result := []string{}
	for p, _ := range i {
		result = append(result, p)
	}
	return result
}
<commit_msg>Introduce visit function<commit_after>package disc

import (
	"fmt"
	"go/parser"
	"path/filepath"

	"os"

	"go/token"

	"strings"
)

type disc struct {
	scope, root string
	fset        *token.FileSet
	gopath      string
}

func New(scope, root string) *disc {
	return &disc{
		scope:  scope,
		root:   root,
		fset:   token.NewFileSet(),
		gopath: os.Getenv("GOPATH"),
	}
}

func (d *disc) Discover() {
	d.walk(d.root, func(p string, imp []string) {
		fmt.Printf("Package %s imports: %v.\n", p, imp)
	})
}

func (d *disc) walk(p string, visit func(string, []string)) {
	d.traverse(p, make(map[string]bool, 1), visit)
}

func (d *disc) traverse(p string, visited map[string]bool, visit func(string, []string)) {
	if visited[p] {
		return
	}
	visited[p] = true

	imports := d.imports(p)
	shortNames := d.shortNames(imports)
	visit(d.shortName(p), shortNames)
	for _, imp := range imports {
		if subpackage(imp, d.scope) {
			d.traverse(imp, visited, visit)
		}
	}
}

// returns true if and only if p is a subpackage of q
func subpackage(p, q string) bool {
	return strings.HasPrefix(p, q)
}

func (d *disc) shortName(p string) string {
	if !subpackage(p, d.scope) {
		panic("failed")
	}
	return p[len(d.scope)+1:]
}

func (d *disc) shortNames(p []string) []string {
	result := []string{}
	for _, q := range p {
		s := d.shortName(q)
		result = append(result, s)
	}
	return result
}

func (d *disc) imports(pkg string) []string {
	i := make(map[string]bool, 1)
	pkgDir := filepath.Join(d.gopath, "src", pkg)
	pAsts, err := parser.ParseDir(d.fset, pkgDir, nil, parser.ImportsOnly)
	if err != nil {
		panic(err)
	}
	for _, ast := range pAsts {
		for _, f := range ast.Files {
			for _, is := range f.Imports {
				q := strings.Trim(is.Path.Value, `"`)
				if q != pkg && subpackage(q, d.scope) {
					i[q] = true
				}
			}
		}
	}
	result := []string{}
	for p, _ := range i {
		result = append(result, p)
	}
	return result
}
<|endoftext|>


<commit_before>package gps

import (
	"strings"
	"sync"

	"github.com/armon/go-radix"
)

// Typed implementations of radix trees. These are just simple wrappers that let
// us avoid having to type assert anywhere else, cleaning up other code a bit.
//
// Some of the more annoying things to implement (like walks) aren't
// implemented. They can be added if/when we actually need them.
//
// Oh generics, where art thou...

type deducerTrie struct {
	sync.RWMutex
	t *radix.Tree
}

func newDeducerTrie() *deducerTrie {
	return &deducerTrie{
		t: radix.New(),
	}
}

// Delete is used to delete a key, returning the previous value and if it was deleted
func (t *deducerTrie) Delete(s string) (pathDeducer, bool) {
	t.Lock()
	if d, had := t.t.Delete(s); had {
		t.Unlock()
		return d.(pathDeducer), had
	}
	t.Unlock()
	return nil, false
}

// Get is used to lookup a specific key, returning the value and if it was found
func (t *deducerTrie) Get(s string) (pathDeducer, bool) {
	t.RLock()
	if d, has := t.t.Get(s); has {
		t.RUnlock()
		return d.(pathDeducer), has
	}
	t.RUnlock()
	return nil, false
}

// Insert is used to add a newentry or update an existing entry. Returns if updated.
func (t *deducerTrie) Insert(s string, d pathDeducer) (pathDeducer, bool) {
	t.Lock()
	if d2, had := t.t.Insert(s, d); had {
		t.Unlock()
		return d2.(pathDeducer), had
	}
	t.Unlock()
	return nil, false
}

// Len is used to return the number of elements in the tree
func (t *deducerTrie) Len() int {
	t.RLock()
	l := t.t.Len()
	t.RUnlock()
	return l
}

// LongestPrefix is like Get, but instead of an exact match, it will return the
// longest prefix match.
func (t *deducerTrie) LongestPrefix(s string) (string, pathDeducer, bool) {
	t.RLock()
	if p, d, has := t.t.LongestPrefix(s); has {
		t.RUnlock()
		return p, d.(pathDeducer), has
	}
	t.RUnlock()
	return "", nil, false
}

// ToMap is used to walk the tree and convert it to a map.
func (t *deducerTrie) ToMap() map[string]pathDeducer {
	m := make(map[string]pathDeducer)
	t.RLock()
	t.t.Walk(func(s string, d interface{}) bool {
		m[s] = d.(pathDeducer)
		return false
	})

	t.RUnlock()
	return m
}

type prTrie struct {
	sync.RWMutex
	t *radix.Tree
}

func newProjectRootTrie() *prTrie {
	return &prTrie{
		t: radix.New(),
	}
}

// Delete is used to delete a key, returning the previous value and if it was deleted
func (t *prTrie) Delete(s string) (ProjectRoot, bool) {
	t.Lock()
	if pr, had := t.t.Delete(s); had {
		t.Unlock()
		return pr.(ProjectRoot), had
	}
	t.Unlock()
	return "", false
}

// Get is used to lookup a specific key, returning the value and if it was found
func (t *prTrie) Get(s string) (ProjectRoot, bool) {
	t.RLock()
	if pr, has := t.t.Get(s); has {
		t.RUnlock()
		return pr.(ProjectRoot), has
	}
	t.RUnlock()
	return "", false
}

// Insert is used to add a newentry or update an existing entry. Returns if updated.
func (t *prTrie) Insert(s string, pr ProjectRoot) (ProjectRoot, bool) {
	t.Lock()
	if pr2, had := t.t.Insert(s, pr); had {
		t.Unlock()
		return pr2.(ProjectRoot), had
	}
	t.Unlock()
	return "", false
}

// Len is used to return the number of elements in the tree
func (t *prTrie) Len() int {
	t.RLock()
	l := t.t.Len()
	t.RUnlock()
	return l
}

// LongestPrefix is like Get, but instead of an exact match, it will return the
// longest prefix match.
func (t *prTrie) LongestPrefix(s string) (string, ProjectRoot, bool) {
	t.RLock()
	if p, pr, has := t.t.LongestPrefix(s); has && isPathPrefixOrEqual(p, s) {
		t.RUnlock()
		return p, pr.(ProjectRoot), has
	}
	t.RUnlock()
	return "", "", false
}

// ToMap is used to walk the tree and convert it to a map.
func (t *prTrie) ToMap() map[string]ProjectRoot {
	t.RLock()
	m := make(map[string]ProjectRoot)
	t.t.Walk(func(s string, pr interface{}) bool {
		m[s] = pr.(ProjectRoot)
		return false
	})

	t.RUnlock()
	return m
}

// isPathPrefixOrEqual is an additional helper check to ensure that the literal
// string prefix returned from a radix tree prefix match is also a path tree
// match.
//
// The radix tree gets it mostly right, but we have to guard against
// possibilities like this:
//
// github.com/sdboyer/foo
// github.com/sdboyer/foobar/baz
//
// The latter would incorrectly be conflated with the former. As we know we're
// operating on strings that describe import paths, guard against this case by
// verifying that either the input is the same length as the match (in which
// case we know they're equal), or that the next character is a "/". (Import
// paths are defined to always use "/", not the OS-specific path separator.)
func isPathPrefixOrEqual(pre, path string) bool {
	prflen, pathlen := len(pre), len(path)
	if pathlen == prflen+1 {
		// this can never be the case
		return false
	}

	// we assume something else (a trie) has done equality check up to the point
	// of the prefix, so we just check len
	return prflen == pathlen || strings.Index(path[prflen:], "/") == 0
}
<commit_msg>Just defer<commit_after>package gps

import (
	"strings"
	"sync"

	"github.com/armon/go-radix"
)

// Typed implementations of radix trees. These are just simple wrappers that let
// us avoid having to type assert anywhere else, cleaning up other code a bit.
//
// Some of the more annoying things to implement (like walks) aren't
// implemented. They can be added if/when we actually need them.
//
// Oh generics, where art thou...

type deducerTrie struct {
	sync.RWMutex
	t *radix.Tree
}

func newDeducerTrie() *deducerTrie {
	return &deducerTrie{
		t: radix.New(),
	}
}

// Delete is used to delete a key, returning the previous value and if it was deleted
func (t *deducerTrie) Delete(s string) (pathDeducer, bool) {
	t.Lock()
	defer t.Unlock()
	if d, had := t.t.Delete(s); had {
		return d.(pathDeducer), had
	}
	return nil, false
}

// Get is used to lookup a specific key, returning the value and if it was found
func (t *deducerTrie) Get(s string) (pathDeducer, bool) {
	t.RLock()
	defer t.RUnlock()
	if d, has := t.t.Get(s); has {
		return d.(pathDeducer), has
	}
	return nil, false
}

// Insert is used to add a newentry or update an existing entry. Returns if updated.
func (t *deducerTrie) Insert(s string, d pathDeducer) (pathDeducer, bool) {
	t.Lock()
	defer t.Unlock()
	if d2, had := t.t.Insert(s, d); had {
		return d2.(pathDeducer), had
	}
	return nil, false
}

// Len is used to return the number of elements in the tree
func (t *deducerTrie) Len() int {
	t.RLock()
	defer t.RUnlock()
	return t.t.Len()
}

// LongestPrefix is like Get, but instead of an exact match, it will return the
// longest prefix match.
func (t *deducerTrie) LongestPrefix(s string) (string, pathDeducer, bool) {
	t.RLock()
	defer t.RUnlock()
	if p, d, has := t.t.LongestPrefix(s); has {
		return p, d.(pathDeducer), has
	}
	return "", nil, false
}

// ToMap is used to walk the tree and convert it to a map.
func (t *deducerTrie) ToMap() map[string]pathDeducer {
	m := make(map[string]pathDeducer)
	t.RLock()
	t.t.Walk(func(s string, d interface{}) bool {
		m[s] = d.(pathDeducer)
		return false
	})

	t.RUnlock()
	return m
}

type prTrie struct {
	sync.RWMutex
	t *radix.Tree
}

func newProjectRootTrie() *prTrie {
	return &prTrie{
		t: radix.New(),
	}
}

// Delete is used to delete a key, returning the previous value and if it was deleted
func (t *prTrie) Delete(s string) (ProjectRoot, bool) {
	t.Lock()
	defer t.Unlock()
	if pr, had := t.t.Delete(s); had {
		return pr.(ProjectRoot), had
	}
	return "", false
}

// Get is used to lookup a specific key, returning the value and if it was found
func (t *prTrie) Get(s string) (ProjectRoot, bool) {
	t.RLock()
	defer t.RUnlock()
	if pr, has := t.t.Get(s); has {
		return pr.(ProjectRoot), has
	}
	return "", false
}

// Insert is used to add a newentry or update an existing entry. Returns if updated.
func (t *prTrie) Insert(s string, pr ProjectRoot) (ProjectRoot, bool) {
	t.Lock()
	defer t.Unlock()
	if pr2, had := t.t.Insert(s, pr); had {
		return pr2.(ProjectRoot), had
	}
	return "", false
}

// Len is used to return the number of elements in the tree
func (t *prTrie) Len() int {
	t.RLock()
	defer t.RUnlock()
	return t.t.Len()
}

// LongestPrefix is like Get, but instead of an exact match, it will return the
// longest prefix match.
func (t *prTrie) LongestPrefix(s string) (string, ProjectRoot, bool) {
	t.RLock()
	defer t.RUnlock()
	if p, pr, has := t.t.LongestPrefix(s); has && isPathPrefixOrEqual(p, s) {
		return p, pr.(ProjectRoot), has
	}
	return "", "", false
}

// ToMap is used to walk the tree and convert it to a map.
func (t *prTrie) ToMap() map[string]ProjectRoot {
	t.RLock()
	m := make(map[string]ProjectRoot)
	t.t.Walk(func(s string, pr interface{}) bool {
		m[s] = pr.(ProjectRoot)
		return false
	})

	t.RUnlock()
	return m
}

// isPathPrefixOrEqual is an additional helper check to ensure that the literal
// string prefix returned from a radix tree prefix match is also a path tree
// match.
//
// The radix tree gets it mostly right, but we have to guard against
// possibilities like this:
//
// github.com/sdboyer/foo
// github.com/sdboyer/foobar/baz
//
// The latter would incorrectly be conflated with the former. As we know we're
// operating on strings that describe import paths, guard against this case by
// verifying that either the input is the same length as the match (in which
// case we know they're equal), or that the next character is a "/". (Import
// paths are defined to always use "/", not the OS-specific path separator.)
func isPathPrefixOrEqual(pre, path string) bool {
	prflen, pathlen := len(pre), len(path)
	if pathlen == prflen+1 {
		// this can never be the case
		return false
	}

	// we assume something else (a trie) has done equality check up to the point
	// of the prefix, so we just check len
	return prflen == pathlen || strings.Index(path[prflen:], "/") == 0
}
<|endoftext|>