Update state and provider JSON with identity fields (#155)

diff --git a/schemas.go b/schemas.go
index 13d0d38..3073d9d 100644
--- a/schemas.go
+++ b/schemas.go
@@ -92,6 +92,9 @@
 
 	// The definitions for any functions in this provider.
 	Functions map[string]*FunctionSignature `json:"functions,omitempty"`
+
+	// The schemas for resources identities in this provider.
+	ResourceIdentitySchemas map[string]*IdentitySchema `json:"resource_identity_schemas,omitempty"`
 }
 
 // Schema is the JSON representation of a particular schema
@@ -294,3 +297,31 @@
 	// of this attribute type (not applicable to single nesting mode).
 	MaxItems uint64 `json:"max_items,omitempty"`
 }
+
+// IdentitySchema is the JSON representation of a particular
+// resource identity schema
+type IdentitySchema struct {
+	// The version of the particular resource identity schema.
+	Version uint64 `json:"version"`
+
+	// Map of identity attributes
+	Attributes map[string]*IdentityAttribute `json:"attributes,omitempty"`
+}
+
+// IdentityAttribute describes an identity attribute
+type IdentityAttribute struct {
+	// The identity attribute type
+	IdentityType cty.Type `json:"type,omitempty"`
+
+	// The description of the identity attribute
+	Description string `json:"description,omitempty"`
+
+	// RequiredForImport when enabled signifies that this attribute must be
+	// specified in the configuration during import
+	RequiredForImport bool `json:"required_for_import,omitempty"`
+
+	// OptionalForImport when enabled signifies that this attribute is not
+	// required to be specified during import, because it can be supplied by the
+	// provider
+	OptionalForImport bool `json:"optional_for_import,omitempty"`
+}
diff --git a/schemas_test.go b/schemas_test.go
index d659e79..56d45d3 100644
--- a/schemas_test.go
+++ b/schemas_test.go
@@ -25,6 +25,9 @@
 		"a provider schema including a resource with write-only attribute(s) is validated": {
 			testDataPath: "testdata/write_only_attribute_on_resource/schemas.json",
 		},
+		"a provider schema including resource identity schemas is validated": {
+			testDataPath: "testdata/identity/schemas.json",
+		},
 	}
 
 	for tn, tc := range cases {
diff --git a/state.go b/state.go
index e533632..ff17aef 100644
--- a/state.go
+++ b/state.go
@@ -173,6 +173,14 @@
 	// DeposedKey is set if the resource instance has been marked Deposed and
 	// will be destroyed on the next apply.
 	DeposedKey string `json:"deposed_key,omitempty"`
+
+	// The version of the resource identity schema the "identity" property
+	// conforms to.
+	IdentitySchemaVersion *uint64 `json:"identity_schema_version,omitempty"`
+
+	// The JSON representation of the resource identity, whose structure
+	// depends on the resource identity schema.
+	IdentityValues map[string]interface{} `json:"identity,omitempty"`
 }
 
 // StateOutput represents an output value in a common state
diff --git a/state_test.go b/state_test.go
index dee9021..a817a21 100644
--- a/state_test.go
+++ b/state_test.go
@@ -5,25 +5,40 @@
 
 import (
 	"encoding/json"
-	"io/ioutil"
+	"io"
 	"os"
 	"testing"
 )
 
 func TestStateValidate_raw(t *testing.T) {
-	f, err := os.Open("testdata/no_changes/state.json")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer f.Close()
-
-	var state State
-	if err := json.NewDecoder(f).Decode(&state); err != nil {
-		t.Fatal(err)
+	cases := map[string]struct {
+		statePath string
+	}{
+		"basic state": {
+			statePath: "testdata/no_changes/state.json",
+		},
+		"state with identity": {
+			statePath: "testdata/identity/state.json",
+		},
 	}
 
-	if err := state.Validate(); err != nil {
-		t.Fatal(err)
+	for tn, tc := range cases {
+		t.Run(tn, func(t *testing.T) {
+			f, err := os.Open(tc.statePath)
+			if err != nil {
+				t.Fatal(err)
+			}
+			defer f.Close()
+
+			var state State
+			if err := json.NewDecoder(f).Decode(&state); err != nil {
+				t.Fatal(err)
+			}
+
+			if err := state.Validate(); err != nil {
+				t.Fatal(err)
+			}
+		})
 	}
 }
 
@@ -34,7 +49,7 @@
 	}
 	defer f.Close()
 
-	b, err := ioutil.ReadAll(f)
+	b, err := io.ReadAll(f)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -53,7 +68,7 @@
 	}
 	defer f.Close()
 
-	b, err := ioutil.ReadAll(f)
+	b, err := io.ReadAll(f)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/testdata/identity/schemas.json b/testdata/identity/schemas.json
new file mode 100644
index 0000000..97e0d64
--- /dev/null
+++ b/testdata/identity/schemas.json
@@ -0,0 +1 @@
+{"format_version":"1.0","provider_schemas":{"example":{"provider":{"version":0,"block":{"attributes":{"example":{"type":"string","description_kind":"plain","optional":true}},"description_kind":"plain"}},"resource_schemas":{"framework_example":{"version":0,"block":{"attributes":{"id":{"type":"string","description":"Example identifier","description_kind":"markdown","computed":true}},"description":"Example resource","description_kind":"markdown"}}},"data_source_schemas":{"framework_example":{"version":0,"block":{"attributes":{"id":{"type":"string","description":"Example identifier","description_kind":"markdown","computed":true}},"description":"Example data source","description_kind":"markdown"}}},"functions":{"example":{"description":"Echoes given argument as result","summary":"Example function","return_type":"string","parameters":[{"name":"input","description":"String to echo","type":"string"}]}},"resource_identity_schemas":{"framework_example":{"version":0,"attributes":{"number":{"type":"number","description":"A specific number","optional_for_import":true},"string":{"type":"string","required_for_import":true}}}}}}}
diff --git a/testdata/identity/state.json b/testdata/identity/state.json
new file mode 100644
index 0000000..d6b0739
--- /dev/null
+++ b/testdata/identity/state.json
@@ -0,0 +1 @@
+{"format_version":"1.0","terraform_version":"1.12.0","values":{"root_module":{"resources":[{"address":"corner_bigint.number","mode":"managed","type":"corner_bigint","name":"number","provider_name":"registry.terraform.io/hashicorp/corner","schema_version":0,"values":{"id":"5","int64":5,"number":5},"sensitive_values":{}},{"address":"corner_user.user","mode":"managed","type":"corner_user","name":"user","provider_name":"registry.terraform.io/hashicorp/corner","schema_version":0,"values":{"age":999,"email":"a@example.com","id":"a@example.com","name":"test"},"sensitive_values":{},"identity_schema_version":0,"identity":{"age":999,"email":"a@example.com","name":"test"}}]}}}