github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/doc/generate.go (about)

     1  // Copyright 2021 iLogtail Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package doc
    16  
    17  import (
    18  	"encoding/json"
    19  	"fmt"
    20  	"os"
    21  	"path/filepath"
    22  	"reflect"
    23  	"sort"
    24  )
    25  
    26  const (
    27  	topLevel       = "# "
    28  	secondLevel    = "## "
    29  	lf             = "\n"
    30  	markdownSuffix = ".md"
    31  	tableSplitor   = "|"
    32  )
    33  
    34  type FieldConfig struct {
    35  	Name    string
    36  	Type    string
    37  	Comment string
    38  	Default string
    39  }
    40  
    41  // Generate plugin doc to the path/category directory.
    42  func Generate(path string) {
    43  	fileName := path + "/plugin-list" + markdownSuffix
    44  	str := topLevel + "Plugin List" + lf
    45  
    46  	var sortedCategories []string
    47  	for category := range docCenter {
    48  		sortedCategories = append(sortedCategories, category)
    49  	}
    50  	sort.Strings(sortedCategories)
    51  	for _, category := range sortedCategories {
    52  		plugins := docCenter[category]
    53  		home := filepath.Clean(path + "/" + category)
    54  		_ = os.MkdirAll(home, 0750)
    55  		str += "- " + category + lf
    56  		var pluginNames []string
    57  		for name := range plugins {
    58  			pluginNames = append(pluginNames, name)
    59  		}
    60  		sort.Strings(pluginNames)
    61  		for _, name := range pluginNames {
    62  			pluginFile := home + "/" + name + markdownSuffix
    63  			relativeFile := category + "/" + name + markdownSuffix
    64  			generatePluginDoc(pluginFile, name, docCenter[category][name])
    65  			str += "	- [" + name + "](" + relativeFile + ")" + lf
    66  		}
    67  	}
    68  	_ = os.WriteFile(fileName, []byte(str), 0600)
    69  }
    70  
    71  func generatePluginDoc(fileName, pluginType string, doc Doc) {
    72  	str := topLevel + pluginType + lf
    73  	str += secondLevel + "Description" + lf
    74  	str += doc.Description() + lf
    75  	str += secondLevel + "Config" + lf
    76  	if configs := extractDocConfig(doc); len(configs) > 0 {
    77  		str += `|  field   |   type   |   description   | default value   |
    78  | ---- | ---- | ---- | ---- |`
    79  		for _, config := range configs {
    80  			str += lf + tableSplitor + config.Name + tableSplitor + config.Type + tableSplitor + config.Comment + tableSplitor + config.Default + tableSplitor
    81  		}
    82  	}
    83  	_ = os.WriteFile(fileName, []byte(str), 0600)
    84  }
    85  
    86  func extractDocConfig(doc Doc) (configs []*FieldConfig) {
    87  	rt := reflect.TypeOf(doc)
    88  	val := reflect.ValueOf(doc)
    89  	if rt.Kind() == reflect.Ptr {
    90  		rt = rt.Elem()
    91  		val = val.Elem()
    92  	}
    93  	overrideName := func(index int, tag string, field *FieldConfig) {
    94  		name := rt.Field(index).Tag.Get(tag)
    95  		if name != "" {
    96  			field.Name = name
    97  		}
    98  	}
    99  	for i := 0; i < rt.NumField(); i++ {
   100  		if rt.Field(i).PkgPath != "" {
   101  			continue
   102  		}
   103  		field := &FieldConfig{
   104  			Name:    rt.Field(i).Name,
   105  			Comment: rt.Field(i).Tag.Get("comment"),
   106  		}
   107  		if field.Comment == "" {
   108  			continue
   109  		}
   110  
   111  		field.Type = rt.Field(i).Type.String()
   112  
   113  		overrideName(i, "yaml", field)
   114  		overrideName(i, "json", field)
   115  		overrideName(i, "mapstructure", field)
   116  
   117  		bytes, err := json.Marshal(val.Field(i).Interface())
   118  		if err != nil {
   119  			panic(fmt.Errorf("cannot extract default value: %+v", err))
   120  		}
   121  		field.Default = string(bytes)
   122  		configs = append(configs, field)
   123  	}
   124  	return
   125  }