github.com/zntrio/harp/v2@v2.0.9/pkg/template/values/hocon/hocon.go (about)

     1  // Licensed to Elasticsearch B.V. under one or more contributor
     2  // license agreements. See the NOTICE file distributed with
     3  // this work for additional information regarding copyright
     4  // ownership. Elasticsearch B.V. licenses this file to you under
     5  // the Apache License, Version 2.0 (the "License"); you may
     6  // not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing,
    12  // software distributed under the License is distributed on an
    13  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14  // KIND, either express or implied.  See the License for the
    15  // specific language governing permissions and limitations
    16  // under the License.
    17  
    18  package hocon
    19  
    20  import (
    21  	"bytes"
    22  	"encoding/json"
    23  	"fmt"
    24  	"os"
    25  	"path/filepath"
    26  
    27  	"github.com/go-akka/configuration"
    28  	"github.com/go-akka/configuration/hocon"
    29  	"go.uber.org/zap"
    30  
    31  	"github.com/zntrio/harp/v2/pkg/sdk/log"
    32  )
    33  
    34  // Parser is a HOCON parser.
    35  type Parser struct{}
    36  
    37  // Unmarshal unmarshals HOCON files.
    38  func (i *Parser) Unmarshal(p []byte, v interface{}) error {
    39  	// Parse HOCON configuration
    40  	rootCfg := configuration.ParseString(string(p), hoconIncludeCallback).Root()
    41  
    42  	// Visit config tree
    43  	res := visitNode(rootCfg)
    44  
    45  	// Encode as json
    46  	var buf bytes.Buffer
    47  	if err := json.NewEncoder(&buf).Encode(res); err != nil {
    48  		return fmt.Errorf("unable to encode HOCON map to JSON: %w", err)
    49  	}
    50  
    51  	// Decode JSON
    52  	if err := json.Unmarshal(buf.Bytes(), v); err != nil {
    53  		return fmt.Errorf("unable to decode json object as struct: %w", err)
    54  	}
    55  
    56  	return nil
    57  }
    58  
    59  // -----------------------------------------------------------------------------
    60  
    61  func visitNode(node *hocon.HoconValue) interface{} {
    62  	if node.IsArray() {
    63  		nodes := node.GetArray()
    64  
    65  		res := make([]interface{}, len(nodes))
    66  		for i, n := range nodes {
    67  			res[i] = visitNode(n)
    68  		}
    69  
    70  		return res
    71  	}
    72  
    73  	if node.IsObject() {
    74  		obj := node.GetObject()
    75  
    76  		res := map[string]interface{}{}
    77  		keys := obj.GetKeys()
    78  		for _, k := range keys {
    79  			res[k] = visitNode(obj.GetKey(k))
    80  		}
    81  
    82  		return res
    83  	}
    84  
    85  	if node.IsString() {
    86  		return node.GetString()
    87  	}
    88  
    89  	if node.IsEmpty() {
    90  		return nil
    91  	}
    92  
    93  	return nil
    94  }
    95  
    96  func hoconIncludeCallback(filename string) *hocon.HoconRoot {
    97  	files, err := filepath.Glob(filename)
    98  	switch {
    99  	case err != nil:
   100  		log.Bg().Error("hocon: unable to load file glob", zap.Error(err), zap.String("filename", filename))
   101  		return nil
   102  	case len(files) == 0:
   103  		log.Bg().Warn("hocon: unable to load file %s", zap.String("filename", filename))
   104  		return hocon.Parse("", nil)
   105  	default:
   106  		root := hocon.Parse("", nil)
   107  		for _, f := range files {
   108  			data, err := os.ReadFile(filepath.Clean(f))
   109  			if err != nil {
   110  				log.Bg().Error("hocon: unable to load file glob", zap.Error(err))
   111  				return nil
   112  			}
   113  
   114  			node := hocon.Parse(string(data), hoconIncludeCallback)
   115  			if node != nil {
   116  				root.Value().GetObject().Merge(node.Value().GetObject())
   117  				// merge substitutions
   118  				subs := make([]*hocon.HoconSubstitution, 0)
   119  				subs = append(subs, root.Substitutions()...)
   120  				subs = append(subs, node.Substitutions()...)
   121  				root = hocon.NewHoconRoot(root.Value(), subs...)
   122  			}
   123  		}
   124  		return root
   125  	}
   126  }