internal/relui: render more output types

Add support for slices and numbers when rendering outputs. Stop
handling artifact slices explicitly in resultDetail in order to support
more slice types.

For golang/go#53382

Change-Id: If16f8f4240e96a1f2f756743236134f8f637b079
Reviewed-on: https://go-review.googlesource.com/c/build/+/413582
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Alex Rakoczy <alex@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Run-TryBot: Alex Rakoczy <alex@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/internal/relui/static/styles.css b/internal/relui/static/styles.css
index d4e9424..3d43d1a 100644
--- a/internal/relui/static/styles.css
+++ b/internal/relui/static/styles.css
@@ -285,7 +285,6 @@
 .TaskList-itemResultDefinition--string {
   display: block;
   -webkit-line-clamp: 3;
-  max-height: 7.5rem;
   overflow: hidden;
   text-overflow: ellipsis;
 }
diff --git a/internal/relui/templates/task_list.html b/internal/relui/templates/task_list.html
index a2882ab..f54c3a2 100644
--- a/internal/relui/templates/task_list.html
+++ b/internal/relui/templates/task_list.html
@@ -135,13 +135,6 @@
           {{.Artifact.StagingPath}}
       </dd>
     </dl>
-  {{else if eq .Kind "Artifacts"}}
-    <dt class="TaskList-itemResultTerm">Filenames</dt>
-    <dd class="TaskList-itemResultDefinition">
-        {{range $artifact := .Artifacts}}
-            {{$artifact.Filename}} <span class="TaskList-itemResultArtifactSize">{{prettySize $artifact.Size}}</span>
-        {{end}}
-    </dd>
   {{else if eq .Kind "Outputs"}}
     {{range $key, $value := .Outputs}}
       <dt class="TaskList-itemResultTerm">
@@ -167,5 +160,33 @@
     <dd class="TaskList-itemResultDefinition TaskList-itemResultDefinition--string">
       {{.String}}
     </dd>
+  {{else if eq .Kind "Slice"}}
+    {{with .Slice}}
+      {{if eq (index . 1).Kind "Artifact"}}
+        <dt class="TaskList-itemResultTerm">Filenames</dt>
+        <dd class="TaskList-itemResultDefinition">
+          {{range $detail := .}}
+            {{with $detail.Artifact}}
+            {{.Filename}} <span class="TaskList-itemResultArtifactSize">{{prettySize .Size}}</span>
+            {{end}}
+          {{end}}
+        </dd>
+      {{else}}
+        {{range $value := .}}
+          <dt class="TaskList-itemResultTerm">
+            {{$value.Kind}}
+          </dt>
+          <dd class="TaskList-itemResultDefinition TaskList-itemResultDefinition--string">
+            {{template "itemResult" $value}}
+          </dd>
+        {{end}}
+      {{end}}
+    {{end}}
+  {{else if eq .Kind "Number"}}
+    {{.Number}}
+  {{else}}
+    {{with .}}
+      {{.Unknown}}
+    {{end}}
   {{end}}
 {{end}}
diff --git a/internal/relui/web.go b/internal/relui/web.go
index 651763e..14c0cd2 100644
--- a/internal/relui/web.go
+++ b/internal/relui/web.go
@@ -366,12 +366,13 @@
 // The UI implementation uses Kind to determine which result type to
 // render.
 type resultDetail struct {
-	Artifact  artifact
-	Artifacts []artifact
-	Outputs   map[string]*resultDetail
-	JSON      map[string]interface{}
-	String    string
-	Unknown   interface{}
+	Artifact artifact
+	Outputs  map[string]*resultDetail
+	JSON     map[string]interface{}
+	String   string
+	Number   float64
+	Slice    []*resultDetail
+	Unknown  interface{}
 }
 
 func (r *resultDetail) Kind() string {
diff --git a/internal/relui/web_test.go b/internal/relui/web_test.go
index 6d4638c..4ec8d90 100644
--- a/internal/relui/web_test.go
+++ b/internal/relui/web_test.go
@@ -820,15 +820,23 @@
 			wantKind: "Outputs",
 		},
 		{
-			desc:     "nested json slice",
-			input:    `{"SomeOutput": [{"Filename": "go.exe"}]}`,
-			want:     &resultDetail{Outputs: map[string]*resultDetail{"SomeOutput": {Artifacts: []artifact{{Filename: "go.exe"}}}}},
+			desc:  "nested json slice",
+			input: `{"SomeOutput": [{"Filename": "go.exe"}]}`,
+			want: &resultDetail{Outputs: map[string]*resultDetail{"SomeOutput": {Slice: []*resultDetail{{
+				Artifact: artifact{Filename: "go.exe"},
+			}}}}},
 			wantKind: "Outputs",
 		},
 		{
-			desc:     "nested json output",
-			input:    `{"SomeOutput": {"OtherOutput": "go.exe"}}`,
-			want:     &resultDetail{Outputs: map[string]*resultDetail{"SomeOutput": {Outputs: map[string]*resultDetail{"OtherOutput": {String: "go.exe"}}}}},
+			desc:  "nested json output",
+			input: `{"SomeOutput": {"OtherOutput": "go.exe", "Next": 123, "Thing": {"foo": "bar"}, "Sauces": ["cranberry", "pizza"]}}`,
+			want: &resultDetail{Outputs: map[string]*resultDetail{
+				"SomeOutput": {Outputs: map[string]*resultDetail{
+					"OtherOutput": {String: "go.exe"},
+					"Next":        {Number: 123},
+					"Thing":       {Outputs: map[string]*resultDetail{"foo": {String: "bar"}}},
+					"Sauces":      {Slice: []*resultDetail{{String: "cranberry"}, {String: "pizza"}}},
+				}}}},
 			wantKind: "Outputs",
 		},
 		{