logging: Add query related message types (#169)

diff --git a/logging_query.go b/logging_query.go
new file mode 100644
index 0000000..7ec5912
--- /dev/null
+++ b/logging_query.go
@@ -0,0 +1,51 @@
+// Copyright (c) HashiCorp, Inc.
+// SPDX-License-Identifier: MPL-2.0
+package tfjson
+
+import "encoding/json"
+
+const (
+	MessageListStart         LogMessageType = "list_start"
+	MessageListResourceFound LogMessageType = "list_resource_found"
+	MessageListComplete      LogMessageType = "list_complete"
+)
+
+// ListStartMessage represents "query" result message of type "list_start"
+type ListStartMessage struct {
+	baseLogMessage
+	ListStart ListStartData `json:"list_start"`
+}
+
+type ListStartData struct {
+	Address      string                     `json:"address"`
+	ResourceType string                     `json:"resource_type"`
+	InputConfig  map[string]json.RawMessage `json:"input_config,omitempty"`
+}
+
+// ListResourceFoundMessage represents "query" result message of type "list_resource_found"
+type ListResourceFoundMessage struct {
+	baseLogMessage
+	ListResourceFound ListResourceFoundData `json:"list_resource_found"`
+}
+
+type ListResourceFoundData struct {
+	Address        string                     `json:"address"`
+	DisplayName    string                     `json:"display_name"`
+	Identity       map[string]json.RawMessage `json:"identity"`
+	ResourceType   string                     `json:"resource_type"`
+	ResourceObject map[string]json.RawMessage `json:"resource_object,omitempty"`
+	Config         string                     `json:"config,omitempty"`
+	ImportConfig   string                     `json:"import_config,omitempty"`
+}
+
+// ListCompleteMessage represents "query" result message of type "list_complete"
+type ListCompleteMessage struct {
+	baseLogMessage
+	ListComplete ListCompleteData `json:"list_complete"`
+}
+
+type ListCompleteData struct {
+	Address      string `json:"address"`
+	ResourceType string `json:"resource_type"`
+	Total        int    `json:"total"`
+}
diff --git a/logging_test.go b/logging_test.go
index 851cc2c..e6a1119 100644
--- a/logging_test.go
+++ b/logging_test.go
@@ -3,6 +3,7 @@
 package tfjson
 
 import (
+	"encoding/json"
 	"testing"
 	"time"
 
@@ -97,3 +98,70 @@
 		}
 	}
 }
+
+func TestLogging_query(t *testing.T) {
+	testCases := []struct {
+		rawMessage      string
+		expectedMessage LogMsg
+	}{
+		{
+			`{"@level":"info","@message":"list.concept_pet.pets: Starting query...","@module":"terraform.ui","@timestamp":"2025-08-28T18:07:11.534006+00:00","list_start":{"address":"list.concept_pet.pets","resource_type":"concept_pet"},"type":"list_start"}`,
+			ListStartMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "list.concept_pet.pets: Starting query...",
+					Time: time.Date(2025, 8, 28, 18, 7, 11, 534006000, time.UTC),
+				},
+				ListStart: ListStartData{
+					Address:      "list.concept_pet.pets",
+					ResourceType: "concept_pet",
+					InputConfig:  nil,
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"list.concept_pet.pets: Result found","@module":"terraform.ui","@timestamp":"2025-08-28T18:07:11.534589+00:00","list_resource_found":{"address":"list.concept_pet.pets","display_name":"This is a easy-antelope","identity":{"id":"easy-antelope","legs":6},"resource_type":"concept_pet"},"type":"list_resource_found"}`,
+			ListResourceFoundMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "list.concept_pet.pets: Result found",
+					Time: time.Date(2025, 8, 28, 18, 7, 11, 534589000, time.UTC),
+				},
+				ListResourceFound: ListResourceFoundData{
+					Address:      "list.concept_pet.pets",
+					ResourceType: "concept_pet",
+					DisplayName:  "This is a easy-antelope",
+					Identity: map[string]json.RawMessage{
+						"id":   json.RawMessage(`"easy-antelope"`),
+						"legs": json.RawMessage("6"),
+					},
+				},
+			},
+		},
+		{
+			`{"@level":"info","@message":"list.concept_pet.pets: List complete","@module":"terraform.ui","@timestamp":"2025-08-28T18:07:11.534661+00:00","list_complete":{"address":"list.concept_pet.pets","resource_type":"concept_pet","total":5},"type":"list_complete"}`,
+			ListCompleteMessage{
+				baseLogMessage: baseLogMessage{
+					Lvl:  Info,
+					Msg:  "list.concept_pet.pets: List complete",
+					Time: time.Date(2025, 8, 28, 18, 7, 11, 534661000, time.UTC),
+				},
+				ListComplete: ListCompleteData{
+					Address:      "list.concept_pet.pets",
+					ResourceType: "concept_pet",
+					Total:        5,
+				},
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		msg, err := UnmarshalLogMessage([]byte(tc.rawMessage))
+		if err != nil {
+			t.Fatal(err)
+		}
+		if diff := cmp.Diff(tc.expectedMessage, msg, cmpOpts); diff != "" {
+			t.Fatalf("unexpected message: %s", diff)
+		}
+	}
+}
diff --git a/logging_types.go b/logging_types.go
index e26522a..3e712a0 100644
--- a/logging_types.go
+++ b/logging_types.go
@@ -21,10 +21,17 @@
 	LogMessage{},
 	DiagnosticLogMessage{},
 	UnknownLogMessage{},
+
+	// query
+	ListStartMessage{},
+	ListResourceFoundMessage{},
+	ListCompleteMessage{},
 }
 
 func unmarshalByType(t LogMessageType, b []byte) (LogMsg, error) {
 	switch t {
+
+	// generic
 	case MessageTypeVersion:
 		v := VersionLogMessage{}
 		return v, json.Unmarshal(b, &v)
@@ -34,6 +41,17 @@
 	case MessageTypeDiagnostic:
 		v := DiagnosticLogMessage{}
 		return v, json.Unmarshal(b, &v)
+
+	// query
+	case MessageListStart:
+		v := ListStartMessage{}
+		return v, json.Unmarshal(b, &v)
+	case MessageListResourceFound:
+		v := ListResourceFoundMessage{}
+		return v, json.Unmarshal(b, &v)
+	case MessageListComplete:
+		v := ListCompleteMessage{}
+		return v, json.Unmarshal(b, &v)
 	}
 
 	v := UnknownLogMessage{}