Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions site/src/types/preview.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
// Code generated by 'guts'. DO NOT EDIT.

// From types/diagnostics.go
export interface DiagnosticExtra {
readonly code: string;
}

// From types/diagnostics.go
export type DiagnosticSeverityString = "error" | "warning";

Expand All @@ -13,6 +18,7 @@ export interface FriendlyDiagnostic {
readonly severity: DiagnosticSeverityString;
readonly summary: string;
readonly detail: string;
readonly extra: DiagnosticExtra;
}

// From types/value.go
Expand Down
50 changes: 48 additions & 2 deletions types/diagnostics.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,43 @@ import (
"github.com/hashicorp/hcl/v2"
)

type DiagnosticExtra struct {
Code string `json:"code"`

// If there was a previous extra, store it here for unwrapping.
wrapped any
}

var _ hcl.DiagnosticExtraUnwrapper = DiagnosticExtra{}

func (e DiagnosticExtra) UnwrapDiagnosticExtra() interface{} {
return e.wrapped
}

func ExtractDiagnosticExtra(diag *hcl.Diagnostic) DiagnosticExtra {
// Zero values for a missing extra field is fine.
extra, _ := hcl.DiagnosticExtra[DiagnosticExtra](diag)
return extra
}

func SetDiagnosticExtra(diag *hcl.Diagnostic, extra DiagnosticExtra) {
existing, ok := hcl.DiagnosticExtra[DiagnosticExtra](diag)
Copy link
Contributor

@jaaydenh jaaydenh May 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this code necessary?

if ok { // If an existing extra is present, we will keep the underlying // wrapped. This is not perfect, as any parents are lost. // So try to avoid calling 'SetDiagnosticExtra' more than once. extra.wrapped = existing.wrapped diag.Extra = extra return }

It feels like this code is already chaining the diag.extra so that each extra.wrapped points to the parent extra.

	if diag.Extra != nil {
		extra.wrapped = diag.Extra
	}
	diag.Extra = extra

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll write a test for this to illustrate 👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

func TestDiagnosticExtraExisting(t *testing.T) {

parent -> extra -> child, we lose the parent.
If we did that you mentioned, it would be
parent -> extra -> new(extra) -> child. And the first extra would take priority on the Extract

if ok {
// If an existing extra is present, we will keep the underlying
// wrapped. This is not perfect, as any parents are lost.
// So try to avoid calling 'SetDiagnosticExtra' more than once.
extra.wrapped = existing.wrapped
diag.Extra = extra
return
}

// Maintain any existing extra fields.
if diag.Extra != nil {
extra.wrapped = diag.Extra
}
diag.Extra = extra
}

// Diagnostics is a JSON friendly form of hcl.Diagnostics.
// Data is lost when doing a json marshal.
type Diagnostics hcl.Diagnostics
Expand All @@ -23,11 +60,15 @@ func (d *Diagnostics) UnmarshalJSON(data []byte) error {
severity = hcl.DiagWarning
}

*d = append(*d, &hcl.Diagnostic{
hclDiag := &hcl.Diagnostic{
Severity: severity,
Summary: diag.Summary,
Detail: diag.Detail,
})
}

SetDiagnosticExtra(hclDiag, diag.Extra)

*d = append(*d, hclDiag)
}
return nil
}
Expand All @@ -40,10 +81,13 @@ func (d Diagnostics) MarshalJSON() ([]byte, error) {
severity = DiagnosticSeverityWarning
}

extra := ExtractDiagnosticExtra(diag)

cpy = append(cpy, FriendlyDiagnostic{
Severity: severity,
Summary: diag.Summary,
Detail: diag.Detail,
Extra: extra,
})
}
return json.Marshal(cpy)
Expand All @@ -60,4 +104,6 @@ type FriendlyDiagnostic struct {
Severity DiagnosticSeverityString `json:"severity"`
Summary string `json:"summary"`
Detail string `json:"detail"`

Extra DiagnosticExtra `json:"extra"`
}
32 changes: 31 additions & 1 deletion types/diagnostics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,48 @@ import (
"github.com/coder/preview/types"
)

func TestDiagnosticsJSON(t *testing.T) {
func TestDiagnosticExtra(t *testing.T) {
diag := &hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: "Some summary",
Detail: "Some detail",
}

extra := types.ExtractDiagnosticExtra(diag)
require.Empty(t, extra.Code)

// Set it
extra.Code = "foobar"
types.SetDiagnosticExtra(diag, extra)

extra = types.ExtractDiagnosticExtra(diag)
require.Equal(t, "foobar", extra.Code)

// Set it again
extra.Code = "bazz"
types.SetDiagnosticExtra(diag, extra)

extra = types.ExtractDiagnosticExtra(diag)
require.Equal(t, "bazz", extra.Code)
}

func TestDiagnosticsJSON(t *testing.T) {
diags := types.Diagnostics{
{
Severity: hcl.DiagWarning,
Summary: "Some summary",
Detail: "Some detail",
Extra: types.DiagnosticExtra{
Code: "foobar",
},
},
{
Severity: hcl.DiagError,
Summary: "Some summary",
Detail: "Some detail",
Extra: types.DiagnosticExtra{
Code: "",
},
},
}

Expand Down
Loading