pkg/log/structure/structuredata/serializer.go (140 lines of code) (raw):
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package structuredata
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"gopkg.in/yaml.v2"
)
func ToYaml(s StructureData) (string, error) {
serializable, err := toSerializableObject(s)
if err != nil {
return "", err
}
marshalled, err := yaml.Marshal(serializable)
if err != nil {
return "", err
}
return strings.ReplaceAll(string(marshalled), fieldCommaEscape, ","), nil
}
func ToJson(s StructureData) (string, error) {
serializable, err := toSerializableObject(s)
if err != nil {
return "", err
}
marshalled, err := json.Marshal(serializable)
if err != nil {
return "", err
}
return strings.ReplaceAll(string(marshalled), fieldCommaEscape, ","), nil
}
func toSerializableObject(s StructureData) (any, error) {
ty, err := s.Type()
if err != nil {
return "", err
}
var yamlSourceObject any
switch ty {
case StructuredTypeArray:
value, err := s.Value("")
if err != nil {
return nil, err
}
yamlSourceObject = value
case StructuredTypeMap:
root := newSortedMap()
keys, err := s.Keys()
if err != nil {
return nil, err
}
for _, key := range keys {
err = storeValuesInContainerRecursive(s, &mapValueContainer{target: root}, key)
if err != nil {
return nil, err
}
}
yamlSourceObject = root
case StructuredTypeScalar:
value, err := s.Value("")
if err != nil {
return nil, err
}
yamlSourceObject = value
default:
return nil, fmt.Errorf("unsupported root object")
}
return yamlSourceObject, nil
}
func storeValuesInContainerRecursive(s StructureData, addTo valueContainer, key string) error {
nr, err := s.Value(key)
if err != nil {
return err
}
nrStructure, convertible := nr.(StructureData)
if !convertible {
return fmt.Errorf("unreachable. value result is not a structuredata")
}
ty, err := nrStructure.Type()
if err != nil {
return err
}
if ty == StructuredTypeScalar {
value, err := nrStructure.Value("")
if err != nil {
return err
}
return addTo.Store(key, value)
}
if ty == StructuredTypeMap {
current := newSortedMap()
keys, err := nrStructure.Keys()
if err != nil {
return err
}
for _, key := range keys {
err = storeValuesInContainerRecursive(nrStructure, &mapValueContainer{target: current}, key)
if err != nil {
return err
}
}
return addTo.Store(key, current)
}
if ty == StructuredTypeArray {
keys, err := nrStructure.Keys()
if err != nil {
return err
}
current := make([]any, len(keys))
for _, key := range keys {
err = storeValuesInContainerRecursive(nrStructure, &arrayValueContainer{target: current}, key)
if err != nil {
return err
}
}
return addTo.Store(key, current)
}
return fmt.Errorf("unsupported")
}
type valueContainer interface {
Store(key string, value any) error
}
var _ valueContainer = (*mapValueContainer)(nil)
var _ valueContainer = (*arrayValueContainer)(nil)
type mapValueContainer struct {
target *sortedMap
}
// Store implements valueContainer.
func (c *mapValueContainer) Store(key string, value any) error {
c.target.AddNextField(key, value)
return nil
}
type arrayValueContainer struct {
target []any
}
// Store implements valueContainer.
func (c *arrayValueContainer) Store(key string, value any) error {
index, err := strconv.Atoi(key)
if err != nil {
return err
}
c.target[index] = value
return nil
}