diff --git a/engine/cld/legacy/cli/mcmsv2/mcms_v2.go b/engine/cld/legacy/cli/mcmsv2/mcms_v2.go index 5a07cf94a..2a59d65ff 100644 --- a/engine/cld/legacy/cli/mcmsv2/mcms_v2.go +++ b/engine/cld/legacy/cli/mcmsv2/mcms_v2.go @@ -733,7 +733,10 @@ func buildMCMSv2AnalyzeProposalCmd( } // Set renderer based on format flag - renderer := createRendererFromFormat(format) + renderer, err := createRendererFromFormat(format) + if err != nil { + return fmt.Errorf("failed to create renderer: %w", err) + } cfgv2.proposalCtx.SetRenderer(renderer) var analyzedProposal string @@ -1611,14 +1614,16 @@ func addCallProxyOption( // createRendererFromFormat creates an appropriate renderer based on the format string. // Defaults to markdown renderer for unknown formats. -func createRendererFromFormat(format string) analyzer.Renderer { +func createRendererFromFormat(format string) (analyzer.Renderer, error) { switch format { case "text", "txt": - return analyzer.NewTextRenderer() + return analyzer.NewTextRenderer(), nil case "markdown", "md": - return analyzer.NewMarkdownRenderer() + return analyzer.NewMarkdownRenderer(), nil + case "": + return analyzer.NewMarkdownRenderer(), nil default: - // Default to markdown if format is not specified or invalid - return analyzer.NewMarkdownRenderer() + // error if format is not specified or invalid + return nil, fmt.Errorf("unknown format '%s'", format) } } diff --git a/experimental/analyzer/decoded_call_test.go b/experimental/analyzer/decoded_call_test.go index 865752f95..01d69bfad 100644 --- a/experimental/analyzer/decoded_call_test.go +++ b/experimental/analyzer/decoded_call_test.go @@ -114,7 +114,6 @@ Method: complexCall Inputs: address: 0x0000000000000000000000000000000000000001 chain: ` + "`" + `1 ()` + "`" + ` - data: 0x010203 Outputs: diff --git a/experimental/analyzer/describe_test.go b/experimental/analyzer/describe_test.go index b2614765f..873365f26 100644 --- a/experimental/analyzer/describe_test.go +++ b/experimental/analyzer/describe_test.go @@ -30,7 +30,7 @@ func TestDescribeProposal(t *testing.T) { name: "Empty proposal", operations: []types.Operation{}, expectError: false, - outputContains: []string{""}, + outputContains: []string{"\n"}, }, { name: "Single operation - unsupported chain", @@ -128,7 +128,7 @@ func TestDescribeTimelockProposal(t *testing.T) { name: "Empty proposal", operations: []types.BatchOperation{}, expectError: false, - outputContains: []string{""}, + outputContains: []string{"\n"}, }, { name: "Single batch - unsupported chain", diff --git a/experimental/analyzer/renderer_markdown.go b/experimental/analyzer/renderer_markdown.go index 6e302ca58..be2af1ce9 100644 --- a/experimental/analyzer/renderer_markdown.go +++ b/experimental/analyzer/renderer_markdown.go @@ -608,7 +608,12 @@ func (r *MarkdownRenderer) renderTemplate(tmpl *template.Template, data interfac return fmt.Sprintf("Error rendering template: %v", err) } - return buf.String() + out := buf.String() + if !strings.HasSuffix(out, "\n\n") { // assuming double newline means intentional spacing + out = strings.TrimSuffix(out, "\n") + } + + return out } // isSimpleValue determines if a string represents a simple value that should be displayed without backticks. diff --git a/experimental/analyzer/renderer_text.go b/experimental/analyzer/renderer_text.go index 527e0dd72..82f08f248 100644 --- a/experimental/analyzer/renderer_text.go +++ b/experimental/analyzer/renderer_text.go @@ -4,6 +4,7 @@ import ( "bytes" "embed" "fmt" + "strings" "text/template" "github.com/ethereum/go-ethereum/common/hexutil" @@ -132,8 +133,10 @@ func (r *TextRenderer) RenderDecodedCall(d *DecodedCall, ctx *FieldContext) stri if err := r.decodedCallTmpl.Execute(&buf, data); err != nil { return fmt.Sprintf("Error rendering decoded call: %v", err) } + out := buf.String() + out = strings.TrimSuffix(out, "\n") - return buf.String() + return out } // RenderProposal renders a ProposalReport as plain text using templates @@ -156,7 +159,12 @@ func (r *TextRenderer) RenderProposal(rep *ProposalReport, ctx *FieldContext) st return fmt.Sprintf("Error rendering proposal: %v", err) } - return buf.String() + out := buf.String() + if !strings.HasSuffix(out, "\n\n") { // assuming double newline means intentional spacing + out = strings.TrimSuffix(out, "\n") + } + + return out } // RenderTimelockProposal renders a Timelock ProposalReport as plain text using templates @@ -188,7 +196,12 @@ func (r *TextRenderer) RenderTimelockProposal(rep *ProposalReport, ctx *FieldCon return fmt.Sprintf("Error rendering timelock: %v", err) } - return buf.String() + out := buf.String() + if !strings.HasSuffix(out, "\n\n") { // assuming double newline means intentional spacing + out = strings.TrimSuffix(out, "\n") + } + + return out } // RenderField renders a NamedField as plain text @@ -225,7 +238,7 @@ func (r *TextRenderer) getChainNameOrEmpty(selector uint64) string { // renderFieldValue renders any field value as plain text using templates func (r *TextRenderer) renderFieldValue(field FieldValue) string { var buf bytes.Buffer - + var out string switch f := field.(type) { case AddressField: data := AddressFieldData{Value: f.GetValue()} @@ -233,58 +246,64 @@ func (r *TextRenderer) renderFieldValue(field FieldValue) string { return fmt.Sprintf("Error rendering address field: %v", err) } - return buf.String() + out = buf.String() case ChainSelectorField: if err := r.chainSelectorFieldTmpl.Execute(&buf, f); err != nil { return fmt.Sprintf("Error rendering chain selector field: %v", err) } - return buf.String() + out = buf.String() case BytesField: if err := r.bytesFieldTmpl.Execute(&buf, f); err != nil { return fmt.Sprintf("Error rendering bytes field: %v", err) } - return buf.String() + out = buf.String() case ArrayField: if err := r.arrayFieldTmpl.Execute(&buf, f); err != nil { return fmt.Sprintf("Error rendering array field: %v", err) } - return buf.String() + out = buf.String() case StructField: if err := r.structFieldTmpl.Execute(&buf, f); err != nil { return fmt.Sprintf("Error rendering struct field: %v", err) } - return buf.String() + out = buf.String() case SimpleField: if err := r.simpleFieldTmpl.Execute(&buf, f); err != nil { return fmt.Sprintf("Error rendering simple field: %v", err) } - return buf.String() + out = buf.String() case YamlField: if err := r.yamlFieldTmpl.Execute(&buf, f); err != nil { return fmt.Sprintf("Error rendering yaml field: %v", err) } - return buf.String() + out = buf.String() case NamedField: if err := r.namedFieldTmpl.Execute(&buf, f); err != nil { return fmt.Sprintf("Error rendering named field: %v", err) } - return buf.String() + out = buf.String() default: return fmt.Sprintf("", field.GetType()) } + + if !strings.HasSuffix(out, "\n\n") { // assuming double newline means intentional spacing + out = strings.TrimSuffix(out, "\n") + } + + return out } diff --git a/experimental/analyzer/renderer_text_test.go b/experimental/analyzer/renderer_text_test.go index 4bf6bbec1..7ed48bed3 100644 --- a/experimental/analyzer/renderer_text_test.go +++ b/experimental/analyzer/renderer_text_test.go @@ -142,7 +142,7 @@ func TestTextRenderer_RenderField_ChainSelectorField(t *testing.T) { field := ChainSelectorField{Value: 999999} result := renderer.RenderField(NamedField{Name: "chain", Value: field}, ctx) - assert.Equal(t, "chain: `999999 ()`\n", result) + assert.Equal(t, "chain: `999999 ()`", result) }) } diff --git a/experimental/analyzer/templates/markdown/address_field.tmpl b/experimental/analyzer/templates/markdown/address_field.tmpl index 322e83a0b..f523f595b 100644 --- a/experimental/analyzer/templates/markdown/address_field.tmpl +++ b/experimental/analyzer/templates/markdown/address_field.tmpl @@ -1 +1 @@ -`{{.Value}}` \ No newline at end of file +`{{.Value}}` diff --git a/experimental/analyzer/templates/markdown/array_field.tmpl b/experimental/analyzer/templates/markdown/array_field.tmpl index 71e1366ce..91e725a9a 100644 --- a/experimental/analyzer/templates/markdown/array_field.tmpl +++ b/experimental/analyzer/templates/markdown/array_field.tmpl @@ -1 +1 @@ -array[{{.Length}}]: {{if eq .Length 0}}[]{{else}}[{{range $i, $elem := .Elements}}{{if $i}}, {{end}}{{compactValue $elem $.Context}}{{end}}{{if gt .Length 3}}, … (+{{sub .Length 3}}){{end}}]{{end}} \ No newline at end of file +array[{{.Length}}]: {{if eq .Length 0}}[]{{else}}[{{range $i, $elem := .Elements}}{{if $i}}, {{end}}{{compactValue $elem $.Context}}{{end}}{{if gt .Length 3}}, … (+{{sub .Length 3}}){{end}}]{{end}} diff --git a/experimental/analyzer/templates/markdown/bytes_field.tmpl b/experimental/analyzer/templates/markdown/bytes_field.tmpl index 3a8bfed53..d957e8b98 100644 --- a/experimental/analyzer/templates/markdown/bytes_field.tmpl +++ b/experimental/analyzer/templates/markdown/bytes_field.tmpl @@ -1 +1 @@ -bytes(len={{.Length}}): {{hexPreview .Value 16}} \ No newline at end of file +bytes(len={{.Length}}): {{hexPreview .Value 16}} diff --git a/experimental/analyzer/templates/markdown/proposal.tmpl b/experimental/analyzer/templates/markdown/proposal.tmpl index aac93fba0..1c5d17588 100644 --- a/experimental/analyzer/templates/markdown/proposal.tmpl +++ b/experimental/analyzer/templates/markdown/proposal.tmpl @@ -1,4 +1,4 @@ {{range $i, $op := .Operations}}Operation #{{$i}} Chain selector: {{$op.ChainSelector}} ({{$op.ChainName}}) {{range $call := $op.Calls}}{{indent (renderCall $call $.Context)}}{{end}} -{{end}} \ No newline at end of file +{{end}} diff --git a/experimental/analyzer/templates/markdown/simple_field.tmpl b/experimental/analyzer/templates/markdown/simple_field.tmpl index 067a73c6f..5464e507a 100644 --- a/experimental/analyzer/templates/markdown/simple_field.tmpl +++ b/experimental/analyzer/templates/markdown/simple_field.tmpl @@ -1 +1 @@ -{{if gt (len .Value) 80}}`{{truncateMiddle .Value 80}}` (len={{len .Value}}){{else}}`{{.Value}}`{{end}} \ No newline at end of file +{{if gt (len .Value) 80}}`{{truncateMiddle .Value 80}}` (len={{len .Value}}){{else}}`{{.Value}}`{{end}} diff --git a/experimental/analyzer/templates/markdown/struct_field.tmpl b/experimental/analyzer/templates/markdown/struct_field.tmpl index c64d0b63c..8027b3d9a 100644 --- a/experimental/analyzer/templates/markdown/struct_field.tmpl +++ b/experimental/analyzer/templates/markdown/struct_field.tmpl @@ -1 +1 @@ -struct{ {{.FieldCount}} fields } \ No newline at end of file +struct{ {{.FieldCount}} fields } diff --git a/experimental/analyzer/templates/markdown/summary.tmpl b/experimental/analyzer/templates/markdown/summary.tmpl index acb9715cb..bc7e28741 100644 --- a/experimental/analyzer/templates/markdown/summary.tmpl +++ b/experimental/analyzer/templates/markdown/summary.tmpl @@ -1 +1 @@ -{{if .Type}}{{.Type}}{{if .Length}}(len={{.Length}}){{end}}{{if .Preview}}{{if hasPrefix .Preview ":"}}{{.Preview}}{{else}}: {{.Preview}}{{end}}{{end}}{{else}}`{{.Value}}`{{end}} \ No newline at end of file +{{if .Type}}{{.Type}}{{if .Length}}(len={{.Length}}){{end}}{{if .Preview}}{{if hasPrefix .Preview ":"}}{{.Preview}}{{else}}: {{.Preview}}{{end}}{{end}}{{else}}`{{.Value}}`{{end}} diff --git a/experimental/analyzer/templates/markdown/timelock_proposal.tmpl b/experimental/analyzer/templates/markdown/timelock_proposal.tmpl index ec1294c69..d53b51167 100644 --- a/experimental/analyzer/templates/markdown/timelock_proposal.tmpl +++ b/experimental/analyzer/templates/markdown/timelock_proposal.tmpl @@ -6,4 +6,4 @@ {{end}}{{end}}--- -{{end}} \ No newline at end of file +{{end}} diff --git a/experimental/analyzer/templates/markdown/yaml_field.tmpl b/experimental/analyzer/templates/markdown/yaml_field.tmpl index 3c95937cc..3be2bc3b6 100644 --- a/experimental/analyzer/templates/markdown/yaml_field.tmpl +++ b/experimental/analyzer/templates/markdown/yaml_field.tmpl @@ -4,4 +4,4 @@ {{.Value}} {{- else -}} `{{.Value}}` -{{- end -}} \ No newline at end of file +{{- end -}} diff --git a/experimental/analyzer/templates/text/address_field.tmpl b/experimental/analyzer/templates/text/address_field.tmpl index 5148015dc..6231ccfac 100644 --- a/experimental/analyzer/templates/text/address_field.tmpl +++ b/experimental/analyzer/templates/text/address_field.tmpl @@ -1 +1 @@ -{{.Value}} \ No newline at end of file +{{.Value}} diff --git a/experimental/analyzer/templates/text/array_field.tmpl b/experimental/analyzer/templates/text/array_field.tmpl index 1be6bb484..6faf5ee2c 100644 --- a/experimental/analyzer/templates/text/array_field.tmpl +++ b/experimental/analyzer/templates/text/array_field.tmpl @@ -1 +1 @@ -{{range $i, $element := .GetElements}}{{if $i}}, {{end}}{{renderField $element}}{{end}} \ No newline at end of file +{{range $i, $element := .GetElements}}{{if $i}}, {{end}}{{renderField $element}}{{end}} diff --git a/experimental/analyzer/templates/text/bytes_field.tmpl b/experimental/analyzer/templates/text/bytes_field.tmpl index 0e31ae5dd..cd6c6e90f 100644 --- a/experimental/analyzer/templates/text/bytes_field.tmpl +++ b/experimental/analyzer/templates/text/bytes_field.tmpl @@ -1 +1 @@ -{{hexEncode .GetValue}} \ No newline at end of file +{{hexEncode .GetValue}} diff --git a/experimental/analyzer/templates/text/decoded_call.tmpl b/experimental/analyzer/templates/text/decoded_call.tmpl index c74838673..32037d2a8 100644 --- a/experimental/analyzer/templates/text/decoded_call.tmpl +++ b/experimental/analyzer/templates/text/decoded_call.tmpl @@ -6,4 +6,4 @@ Inputs: {{end}}{{end}}{{if .Outputs}}{{if .Inputs}} {{end}}Outputs: {{range .Outputs}} {{.Name}}: {{.Summary}}{{if .Details}} {{.Details}}{{end}} -{{end}}{{end}} \ No newline at end of file +{{end}}{{end}} diff --git a/experimental/analyzer/templates/text/named_field.tmpl b/experimental/analyzer/templates/text/named_field.tmpl index 5ede69de8..22de0ca10 100644 --- a/experimental/analyzer/templates/text/named_field.tmpl +++ b/experimental/analyzer/templates/text/named_field.tmpl @@ -1,2 +1 @@ {{.Name}}: {{renderField .Value}} - diff --git a/experimental/analyzer/templates/text/simple_field.tmpl b/experimental/analyzer/templates/text/simple_field.tmpl index f56c638c4..dd52c04d7 100644 --- a/experimental/analyzer/templates/text/simple_field.tmpl +++ b/experimental/analyzer/templates/text/simple_field.tmpl @@ -1 +1 @@ -{{.GetValue}} \ No newline at end of file +{{.GetValue}} diff --git a/experimental/analyzer/templates/text/struct_field.tmpl b/experimental/analyzer/templates/text/struct_field.tmpl index 4d74928f8..cb4e00f71 100644 --- a/experimental/analyzer/templates/text/struct_field.tmpl +++ b/experimental/analyzer/templates/text/struct_field.tmpl @@ -1 +1 @@ -{{range $i, $field := .GetFields}}{{if $i}}, {{end}}{{.GetName}}: {{renderField .GetValue}}{{end}} \ No newline at end of file +{{range $i, $field := .GetFields}}{{if $i}}, {{end}}{{.GetName}}: {{renderField .GetValue}}{{end}} diff --git a/experimental/analyzer/templates/text/yaml_field.tmpl b/experimental/analyzer/templates/text/yaml_field.tmpl index f56c638c4..dd52c04d7 100644 --- a/experimental/analyzer/templates/text/yaml_field.tmpl +++ b/experimental/analyzer/templates/text/yaml_field.tmpl @@ -1 +1 @@ -{{.GetValue}} \ No newline at end of file +{{.GetValue}}