package internal
import (
"bytes"
"encoding/json"
"fmt"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
yaml "gopkg.in/yaml.v3"
)
// ConvertProtoToYAML converts any protobuf message to a YAML string with 2-space indentation
func ConvertProtoToYAML(msg proto.Message) (string, error) {
// 1. Wrap the message in a google.protobuf.Any container.
// This is the standard way to ensure the "@type" field is emitted.
anyMsg, err := anypb.New(msg)
if err != nil {
return "", fmt.Errorf("failed to wrap proto message in Any: %w", err)
}
// 2. Marshal the 'Any' message to JSON.
// MarshalOptions can use default settings here.
options := protojson.MarshalOptions{
// Still a good practice to use, but EmitType is not the answer here.
UseProtoNames: false,
}
jsonBytes, err := options.Marshal(anyMsg) // <-- Marshal the 'anyMsg'
if err != nil {
return "", fmt.Errorf("failed to marshal proto to JSON: %w", err)
}
// 2. Unmarshal JSON into a generic Go map
var obj interface{}
if err := json.Unmarshal(jsonBytes, &obj); err != nil {
return "", fmt.Errorf("failed to unmarshal JSON: %w", err)
}
// 3. Marshal the map to YAML with 2-space indentation
var buf bytes.Buffer
encoder := yaml.NewEncoder(&buf)
encoder.SetIndent(2) // set indentation to 2 spaces
if err := encoder.Encode(obj); err != nil {
return "", fmt.Errorf("failed to encode YAML: %w", err)
}
encoder.Close()
return buf.String(), nil
}