mirror of
https://github.com/hanzoai/o11y-foundry.git
synced 2026-10-30 18:26:45 +00:00
158 lines
3.2 KiB
Go
158 lines
3.2 KiB
Go
package domain
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"slices"
|
|
|
|
"github.com/hanzoai/o11y-foundry/internal/errors"
|
|
"gopkg.in/ini.v1"
|
|
)
|
|
|
|
func init() {
|
|
ini.PrettyFormat = false
|
|
}
|
|
|
|
var _ StructuredMaterial = INIMaterial{}
|
|
|
|
type INIMaterial struct {
|
|
path string
|
|
contents []byte
|
|
}
|
|
|
|
func NewINIMaterial(contents []byte, path string) (INIMaterial, error) {
|
|
cfg, err := ini.LoadSources(ini.LoadOptions{AllowShadows: true}, contents)
|
|
if err != nil {
|
|
return INIMaterial{}, errors.Wrapf(err, errors.TypeInvalidInput, "failed to create INI material for path %q: contents are not valid INI", path)
|
|
}
|
|
|
|
data := make(map[string]map[string]any)
|
|
|
|
for _, section := range cfg.Sections() {
|
|
if section.Name() == ini.DefaultSection && len(section.Keys()) == 0 {
|
|
continue
|
|
}
|
|
|
|
sectionData := make(map[string]any)
|
|
for _, key := range section.Keys() {
|
|
vals := key.ValueWithShadows()
|
|
|
|
if len(vals) > 1 {
|
|
sectionData[key.Name()] = vals
|
|
} else {
|
|
sectionData[key.Name()] = key.String()
|
|
}
|
|
}
|
|
data[section.Name()] = sectionData
|
|
}
|
|
|
|
jsonContents, err := json.Marshal(data)
|
|
if err != nil {
|
|
return INIMaterial{}, errors.Wrapf(err, errors.TypeInternal, "failed to convert INI material to canonical JSON for path %q", path)
|
|
}
|
|
|
|
return INIMaterial{
|
|
path: path,
|
|
contents: jsonContents,
|
|
}, nil
|
|
}
|
|
|
|
func MustNewINIMaterial(contents []byte, path string) INIMaterial {
|
|
material, err := NewINIMaterial(contents, path)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return material
|
|
}
|
|
|
|
func (m INIMaterial) Path() string {
|
|
return m.path
|
|
}
|
|
|
|
func (m INIMaterial) JSONContents() []byte {
|
|
return m.contents
|
|
}
|
|
|
|
func (m INIMaterial) HasMultipleDocuments() bool {
|
|
return false
|
|
}
|
|
|
|
func (m INIMaterial) FmtContents() []byte {
|
|
var data map[string]map[string]any
|
|
if err := json.Unmarshal(m.contents, &data); err != nil {
|
|
return nil
|
|
}
|
|
|
|
cfg, err := ini.LoadSources(ini.LoadOptions{AllowShadows: true, PreserveSurroundedQuote: true}, []byte(""))
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
for _, sName := range getSortedKeys(data) {
|
|
section, _ := cfg.NewSection(sName)
|
|
|
|
for _, kName := range getSortedKeys(data[sName]) {
|
|
if err := writeEntry(section, kName, data[sName][kName]); err != nil {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
if _, err := cfg.WriteTo(&buf); err != nil {
|
|
return nil
|
|
}
|
|
|
|
return buf.Bytes()
|
|
}
|
|
|
|
func (m INIMaterial) CloneWithJSONContents(contents []byte) StructuredMaterial {
|
|
return INIMaterial{
|
|
contents: contents,
|
|
path: m.path,
|
|
}
|
|
}
|
|
|
|
func (m INIMaterial) GetBytes(path string) ([]byte, error) {
|
|
return getBytes(m.contents, path)
|
|
}
|
|
|
|
func (m INIMaterial) GetStringSlice(path string) ([]string, error) {
|
|
return getStringSlice(m.contents, path)
|
|
}
|
|
|
|
func writeEntry(sec *ini.Section, key string, value any) error {
|
|
if vals, ok := value.([]any); ok {
|
|
for i, v := range vals {
|
|
strVal := fmt.Sprint(v)
|
|
if i == 0 {
|
|
if _, err := sec.NewKey(key, strVal); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if err := sec.Key(key).AddShadow(strVal); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if _, err := sec.NewKey(key, fmt.Sprint(value)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getSortedKeys[V any](m map[string]V) []string {
|
|
keys := make([]string, 0, len(m))
|
|
for k := range m {
|
|
keys = append(keys, k)
|
|
}
|
|
slices.Sort(keys)
|
|
return keys
|
|
}
|