cosmos-sdk/core/appconfig/config.go

125 lines
3.5 KiB
Go

package appconfig
import (
"fmt"
"strings"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/types/known/anypb"
"sigs.k8s.io/yaml"
appv1alpha1 "cosmossdk.io/api/cosmos/app/v1alpha1"
"cosmossdk.io/depinject"
"cosmossdk.io/core/internal"
)
// LoadJSON loads an app config in JSON format.
func LoadJSON(bz []byte) depinject.Config {
config := &appv1alpha1.Config{}
err := protojson.Unmarshal(bz, config)
if err != nil {
return depinject.Error(err)
}
return Compose(config)
}
// LoadYAML loads an app config in YAML format.
func LoadYAML(bz []byte) depinject.Config {
j, err := yaml.YAMLToJSON(bz)
if err != nil {
return depinject.Error(err)
}
return LoadJSON(j)
}
// WrapAny marshals a proto message into a proto Any instance
func WrapAny(config protoreflect.ProtoMessage) *anypb.Any {
cfg, err := anypb.New(config)
if err != nil {
panic(err)
}
return cfg
}
// Compose composes a v1alpha1 app config into a container option by resolving
// the required modules and composing their options.
func Compose(appConfig *appv1alpha1.Config) depinject.Config {
opts := []depinject.Config{
depinject.Supply(appConfig),
}
for _, module := range appConfig.Modules {
if module.Name == "" {
return depinject.Error(fmt.Errorf("module is missing name"))
}
if module.Config == nil {
return depinject.Error(fmt.Errorf("module %q is missing a config object", module.Name))
}
msgType, err := protoregistry.GlobalTypes.FindMessageByURL(module.Config.TypeUrl)
if err != nil {
return depinject.Error(err)
}
modules, err := internal.ModulesByProtoMessageName()
if err != nil {
return depinject.Error(err)
}
init, ok := modules[msgType.Descriptor().FullName()]
if !ok {
modDesc := proto.GetExtension(msgType.Descriptor().Options(), appv1alpha1.E_Module).(*appv1alpha1.ModuleDescriptor)
if modDesc == nil {
return depinject.Error(fmt.Errorf("no module registered for type URL %s and that protobuf type does not have the option %s\n\n%s",
module.Config.TypeUrl, appv1alpha1.E_Module.TypeDescriptor().FullName(), dumpRegisteredModules(modules)))
}
return depinject.Error(fmt.Errorf("no module registered for type URL %s, did you forget to import %s\n\n%s",
module.Config.TypeUrl, modDesc.GoImport, dumpRegisteredModules(modules)))
}
config := init.ConfigProtoMessage.ProtoReflect().Type().New().Interface()
err = anypb.UnmarshalTo(module.Config, config, proto.UnmarshalOptions{})
if err != nil {
return depinject.Error(err)
}
opts = append(opts, depinject.Supply(config))
for _, provider := range init.Providers {
opts = append(opts, depinject.ProvideInModule(module.Name, provider))
}
for _, invoker := range init.Invokers {
opts = append(opts, depinject.InvokeInModule(module.Name, invoker))
}
for _, binding := range module.GolangBindings {
opts = append(opts, depinject.BindInterfaceInModule(module.Name, binding.InterfaceType, binding.Implementation))
}
}
for _, binding := range appConfig.GolangBindings {
opts = append(opts, depinject.BindInterface(binding.InterfaceType, binding.Implementation))
}
return depinject.Configs(opts...)
}
func dumpRegisteredModules(modules map[protoreflect.FullName]*internal.ModuleInitializer) string {
var mods []string
for name := range modules {
mods = append(mods, " "+string(name))
}
return fmt.Sprintf("registered modules are:\n%s", strings.Join(mods, "\n"))
}