2023-02-25 21:53:18 +07:00
|
|
|
/*
|
|
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
2024-01-04 16:28:16 +07:00
|
|
|
* Copyright (c) 2022-2024, daeuniverse Organization <dae@v2raya.org>
|
2023-02-25 21:53:18 +07:00
|
|
|
*/
|
|
|
|
|
|
|
|
package config
|
|
|
|
|
|
|
|
import (
|
|
|
|
"reflect"
|
|
|
|
"sort"
|
2023-04-23 12:27:29 +07:00
|
|
|
|
|
|
|
jsoniter "github.com/json-iterator/go"
|
2023-02-25 21:53:18 +07:00
|
|
|
)
|
|
|
|
|
|
|
|
type Outline struct {
|
|
|
|
Version string `json:"version"`
|
|
|
|
Leaves []string `json:"leaves"`
|
|
|
|
Structure []*OutlineElem `json:"structure"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type OutlineElem struct {
|
2023-03-02 14:25:47 +07:00
|
|
|
Name string `json:"name,omitempty"`
|
|
|
|
Mapping string `json:"mapping,omitempty"`
|
|
|
|
IsArray bool `json:"isArray,omitempty"`
|
|
|
|
DefaultValue string `json:"defaultValue,omitempty"`
|
|
|
|
Required bool `json:"required,omitempty"`
|
|
|
|
Type string `json:"type,omitempty"`
|
|
|
|
Desc string `json:"desc,omitempty"`
|
|
|
|
Structure []*OutlineElem `json:"structure,omitempty"`
|
2023-02-25 21:53:18 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
func ExportOutline(version string) *Outline {
|
|
|
|
// Get structure.
|
|
|
|
t := reflect.TypeOf(Config{})
|
|
|
|
exporter := outlineExporter{
|
|
|
|
leaves: make(map[string]reflect.Type),
|
|
|
|
pkgPathScope: t.PkgPath(),
|
|
|
|
}
|
|
|
|
structure := exporter.exportStruct(t, SectionSummaryDesc, false)
|
|
|
|
// Get string type leaves.
|
|
|
|
var leaves []string
|
|
|
|
for k := range exporter.leaves {
|
|
|
|
leaves = append(leaves, k)
|
|
|
|
}
|
|
|
|
sort.Strings(leaves)
|
|
|
|
|
|
|
|
return &Outline{
|
|
|
|
Version: version,
|
|
|
|
Leaves: leaves,
|
|
|
|
Structure: structure,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExportOutlineJson(version string) string {
|
|
|
|
b, err := jsoniter.MarshalIndent(ExportOutline(version), "", " ")
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return string(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
type outlineExporter struct {
|
|
|
|
leaves map[string]reflect.Type
|
|
|
|
pkgPathScope string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *outlineExporter) exportStruct(t reflect.Type, descSource Desc, inheritSource bool) (outlines []*OutlineElem) {
|
|
|
|
for i := 0; i < t.NumField(); i++ {
|
|
|
|
section := t.Field(i)
|
|
|
|
// Parse desc.
|
|
|
|
var desc string
|
|
|
|
if descSource != nil {
|
|
|
|
desc = descSource[section.Tag.Get("mapstructure")]
|
|
|
|
}
|
|
|
|
// Parse elem type.
|
|
|
|
var isArray bool
|
|
|
|
var typ reflect.Type
|
|
|
|
switch section.Type.Kind() {
|
|
|
|
case reflect.Slice:
|
|
|
|
typ = section.Type.Elem()
|
|
|
|
isArray = true
|
|
|
|
default:
|
|
|
|
typ = section.Type
|
|
|
|
}
|
|
|
|
if typ.Kind() == reflect.Pointer {
|
|
|
|
typ = typ.Elem()
|
|
|
|
}
|
|
|
|
// Parse children.
|
|
|
|
var children []*OutlineElem
|
|
|
|
switch typ.Kind() {
|
|
|
|
case reflect.Struct:
|
|
|
|
var nextDescSource Desc
|
|
|
|
if inheritSource {
|
|
|
|
nextDescSource = descSource
|
|
|
|
} else {
|
|
|
|
nextDescSource = SectionDescription[section.Tag.Get("desc")]
|
|
|
|
}
|
|
|
|
if typ.PkgPath() == "" || typ.PkgPath() == e.pkgPathScope {
|
|
|
|
children = e.exportStruct(typ, nextDescSource, true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(children) == 0 {
|
|
|
|
// Record leaves.
|
|
|
|
e.leaves[typ.String()] = typ
|
|
|
|
}
|
2023-03-02 14:25:47 +07:00
|
|
|
_, required := section.Tag.Lookup("required")
|
2023-02-25 21:53:18 +07:00
|
|
|
outlines = append(outlines, &OutlineElem{
|
2023-03-02 14:25:47 +07:00
|
|
|
Name: section.Name,
|
|
|
|
Mapping: section.Tag.Get("mapstructure"),
|
|
|
|
IsArray: isArray,
|
|
|
|
DefaultValue: section.Tag.Get("default"),
|
|
|
|
Required: required,
|
|
|
|
Type: typ.String(),
|
|
|
|
Desc: desc,
|
|
|
|
Structure: children,
|
2023-02-25 21:53:18 +07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
return outlines
|
|
|
|
}
|