Add support for JSON output from `workspace list` (#198)
diff --git a/workspace_list.go b/workspace_list.go
new file mode 100644
index 0000000..282b739
--- /dev/null
+++ b/workspace_list.go
@@ -0,0 +1,41 @@
+// Copyright IBM Corp. 2019, 2026
+// SPDX-License-Identifier: MPL-2.0
+
+package tfjson
+
+// WorkspaceListOutput represents JSON output from terraform workspace list
+// (available from 1.16 onwards)
+type WorkspaceListOutput struct {
+	FormatVersion string `json:"format_version"`
+
+	Workspaces  []WorkspaceListEntry `json:"workspaces"`
+	Diagnostics []Diagnostic         `json:"diagnostics"`
+}
+
+// WorkspaceListEntry represents a single workspace entry in the list of workspaces
+type WorkspaceListEntry struct {
+	Name      string `json:"name"`
+	IsCurrent bool   `json:"is_current"`
+}
+
+// CurrentWorkspace will return details about the workspace that's currently selected.
+// The result may be nil if:
+// 1) the command output is empty, i.e. no workspaces exist yet.
+// 2) or, if the output doesn't specify a selected workspace.
+// Number 2 may happen if the default workspace is selected but not created yet.
+//
+// This method doesn't return any errors when nil is returned, as there are valid conditions where
+// nil is returned. Calling code should look for diagnostics in the receiver WorkspaceListOutput to
+// see if an explicit error has occurred or not.
+func (wlo *WorkspaceListOutput) CurrentWorkspace() *WorkspaceListEntry {
+	if wlo == nil {
+		return nil
+	}
+
+	for _, ws := range wlo.Workspaces {
+		if ws.IsCurrent {
+			return &ws
+		}
+	}
+	return nil
+}
diff --git a/workspace_list_test.go b/workspace_list_test.go
new file mode 100644
index 0000000..d470cd9
--- /dev/null
+++ b/workspace_list_test.go
@@ -0,0 +1,199 @@
+// Copyright IBM Corp. 2019, 2026
+// SPDX-License-Identifier: MPL-2.0
+
+package tfjson
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+)
+
+func TestWorkspaceListOutput_basic(t *testing.T) {
+	output := `{
+  "workspaces": [
+    {
+      "name": "default",
+      "is_current": true
+    },
+    {
+      "name": "other",
+      "is_current": false
+    }
+  ],
+  "diagnostics": []
+}`
+	var parsed WorkspaceListOutput
+	if err := json.Unmarshal([]byte(output), &parsed); err != nil {
+		t.Fatal(err)
+	}
+
+	expected := &WorkspaceListOutput{
+		Workspaces: []WorkspaceListEntry{
+			{
+				Name:      "default",
+				IsCurrent: true,
+			},
+			{
+				Name:      "other",
+				IsCurrent: false,
+			},
+		},
+		Diagnostics: []Diagnostic{},
+	}
+	if diff := cmp.Diff(expected, &parsed); diff != "" {
+		t.Fatalf("output mismatch: %s", diff)
+	}
+}
+
+func TestWorkspaceListOutput_warning(t *testing.T) {
+	output := `{
+  "workspaces": [
+    {
+      "name": "default",
+      "is_current": true
+    },
+    {
+      "name": "other",
+      "is_current": false
+    }
+  ],
+  "diagnostics": [
+    {
+      "severity": "warning",
+      "summary": "Warning: Consider yourself warned"
+    }
+  ]
+}`
+	var parsed WorkspaceListOutput
+	if err := json.Unmarshal([]byte(output), &parsed); err != nil {
+		t.Fatal(err)
+	}
+
+	expected := &WorkspaceListOutput{
+		Workspaces: []WorkspaceListEntry{
+			{
+				Name:      "default",
+				IsCurrent: true,
+			},
+			{
+				Name:      "other",
+				IsCurrent: false,
+			},
+		},
+		Diagnostics: []Diagnostic{
+			{
+				Severity: "warning",
+				Summary:  "Warning: Consider yourself warned",
+			},
+		},
+	}
+	if diff := cmp.Diff(expected, &parsed); diff != "" {
+		t.Fatalf("output mismatch: %s", diff)
+	}
+}
+
+func TestWorkspaceListOutput_error(t *testing.T) {
+	output := `{
+  "workspaces": [],
+  "diagnostics": [
+    {
+      "severity": "error",
+      "summary": "Error: Something went wrong"
+    }
+  ]
+}`
+	var parsed WorkspaceListOutput
+	if err := json.Unmarshal([]byte(output), &parsed); err != nil {
+		t.Fatal(err)
+	}
+
+	expected := &WorkspaceListOutput{
+		Workspaces: []WorkspaceListEntry{},
+		Diagnostics: []Diagnostic{
+			{
+				Severity: "error",
+				Summary:  "Error: Something went wrong",
+			},
+		},
+	}
+	if diff := cmp.Diff(expected, &parsed); diff != "" {
+		t.Fatalf("output mismatch: %s", diff)
+	}
+}
+
+func TestWorkspaceListOutput_CurrentWorkspace(t *testing.T) {
+	t.Run("returns current workspace", func(t *testing.T) {
+		wlo := &WorkspaceListOutput{
+			Workspaces: []WorkspaceListEntry{
+				{
+					Name:      "default",
+					IsCurrent: true,
+				},
+				{
+					Name:      "other",
+					IsCurrent: false,
+				},
+			},
+			Diagnostics: []Diagnostic{},
+		}
+
+		current := wlo.CurrentWorkspace()
+		if current == nil {
+			t.Fatal("unexpected nil result")
+		}
+		if current.Name != "default" {
+			t.Fatalf("wanted %q, got: %q", "default", current.Name)
+		}
+	})
+	t.Run("returns nil when there isn't a current workspace", func(t *testing.T) {
+		// This scenario could happen if custom workspaces exist and the
+		// default workspace is selected, but there hasn't been an apply
+		// yet so no default workspace actually exists yet...
+
+		wlo := &WorkspaceListOutput{
+			Workspaces: []WorkspaceListEntry{
+				{
+					Name:      "default",
+					IsCurrent: false,
+				},
+				{
+					Name:      "other",
+					IsCurrent: false,
+				},
+			},
+			Diagnostics: []Diagnostic{},
+		}
+
+		current := wlo.CurrentWorkspace()
+		if current != nil {
+			t.Fatalf("expected nil result, got: %#v", current)
+		}
+	})
+
+	t.Run("returns nil when there are no workspaces", func(t *testing.T) {
+		// This scenario could happen if there are no custom workspaces yet
+		// and an apply hasn't yet happened in the default workspace.
+		// E.g. user creates an empty Terraform project and immediately performs
+		// `terraform workspace list -json`
+
+		wlo := &WorkspaceListOutput{
+			Workspaces: []WorkspaceListEntry{},
+			Diagnostics: []Diagnostic{
+				{
+					// Mimicking `warnNoEnvsExistDiag` from the Core repo:
+					// https://github.com/hashicorp/terraform/blob/527402d3fe2de2363c4587e7abd1a3b23669ca25/internal/command/workspace_command.go#L158
+					Severity: "warning",
+					Summary:  "Terraform cannot find any existing workspaces.",
+					Detail:   "The \"default\" workspace is selected in your working directory. You can create this workspace by running \"terraform init\", by using the \"terraform workspace new\" subcommand or by including the \"-or-create\" flag with the \"terraform workspace select\" subcommand.",
+				},
+			},
+		}
+
+		current := wlo.CurrentWorkspace()
+		if current != nil {
+			t.Fatalf("expected nil result, got: %#v", current)
+		}
+	})
+}