package main

import (
	"errors"
	"fmt"
	"log"
)

var errInvalidKey = errors.New("invalid key")

type errNotFound struct {
	key string
}

func (e errNotFound) Error() string {
	return fmt.Sprintf("unable to find value for %q", e.key)
}

func lookup(key string) (string, error) {
	data := map[string]string{"foo": "bar", "mascot": "gopher", "go": "awesome"}

	if key == "" {
		return "", errInvalidKey
	}
	if v, ok := data[key]; ok {
		return v, nil
	}
	return "", errNotFound{key: key}
}

// TODO: Convert the following error check to use errors.Is
func noKey() {
	var key string
	v, err := lookup(key)
	if err != nil {
		if err != errInvalidKey {
			log.Fatal(err)
		}
		fmt.Println("please specify a valid key")
		return
	}
	fmt.Printf("value for %q is %q", key, v)
}

// TODO: Convert the following error check to use errors.As
func notFound() {
	key := "baz"
	v, err := lookup(key)
	if err != nil {
		if _, ok := err.(errNotFound); !ok {
			log.Fatal(err)
		}
		fmt.Printf("error: %s\n", err)
		return
	}
	fmt.Printf("value for %q is %q", key, v)
}

func found() {
	key := "go"
	v, err := lookup(key)
	if err != nil {
		if _, ok := err.(errNotFound); !ok {
			log.Fatal(err)
		}
		fmt.Printf("error: %s\n", err)
		return
	}
	fmt.Printf("value for %q is %q", key, v)
}

// TODO: Update the following function to print a stack trace when i == 10
func nested(i int) {
	if i == 10 {
		return
	}
	fmt.Println("nested call", i)
	nested(i + 1)
}

func main() {
	noKey()
	notFound()
	found()
	nested(1)
}
