Files
windows_exporter/internal/config/yamlstring/yamlstring.go
2026-10-08 12:57:04 +02:00

102 lines
3.0 KiB
Go

// SPDX-License-Identifier: Apache-2.0
//
// Copyright The Prometheus Authors
// 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.
//go:build windows
// Package yamlstring decodes configuration file blocks whose value is a YAML or
// JSON document kept as a string, like the objects of the performancecounter
// collector.
package yamlstring
import (
"errors"
"fmt"
"io"
"strings"
"go.yaml.in/yaml/v3"
)
// DecodeBlock decodes node, the configuration file block of a collector, into
// target. key must be the only key of the block, and its value a string holding
// a YAML or JSON list, the same value the collector's flag takes. A null value
// leaves target unchanged.
//
// Unknown keys, in the block and in the list, are rejected, like everywhere else
// in the configuration file. The errors are returned as a [yaml.TypeError], so
// the decoder reports them together with the other errors of the file.
func DecodeBlock[T any](node *yaml.Node, key string, target *[]T) error {
if node.Kind != yaml.MappingNode {
return &yaml.TypeError{Errors: []string{
fmt.Sprintf("line %d: cannot unmarshal %s into a block with the key %s", node.Line, node.ShortTag(), key),
}}
}
var errs []string
for i := 0; i+1 < len(node.Content); i += 2 {
name, value := node.Content[i], node.Content[i+1]
if name.Value != key {
errs = append(errs, fmt.Sprintf("line %d: field %s not found, the only field is %s", name.Line, name.Value, key))
continue
}
errs = append(errs, decodeValue(value, key, target)...)
}
if len(errs) > 0 {
return &yaml.TypeError{Errors: errs}
}
return nil
}
// decodeValue decodes the string value of key into target and returns the
// errors, if any. Line numbers inside the string count from its first line.
func decodeValue[T any](value *yaml.Node, key string, target *[]T) []string {
if value.ShortTag() == "!!null" {
return nil
}
if value.Kind != yaml.ScalarNode || value.ShortTag() != "!!str" {
return []string{fmt.Sprintf("line %d: %s must be a string, use %s: |- followed by the list", value.Line, key, key)}
}
decoder := yaml.NewDecoder(strings.NewReader(value.Value))
decoder.KnownFields(true)
var list []T
if err := decoder.Decode(&list); err != nil && !errors.Is(err, io.EOF) {
var typeErr *yaml.TypeError
if !errors.As(err, &typeErr) {
return []string{fmt.Sprintf("line %d: in %s, %s", value.Line, key, err)}
}
errs := make([]string, len(typeErr.Errors))
for i, e := range typeErr.Errors {
errs[i] = fmt.Sprintf("line %d: in %s, %s", value.Line, key, e)
}
return errs
}
*target = list
return nil
}