// Copyright 2021 storyicon@foxmail.com // // 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. package configmanager import ( "context" "os" "path/filepath" "strings" "sync" "github.com/pkg/errors" "github.com/storyicon/powerproto/pkg/configs" "github.com/storyicon/powerproto/pkg/util/logger" ) // ConfigManager is used to manage config type ConfigManager interface { // GetCompiler is used to get config of specified proto file path GetConfig(ctx context.Context, protoFilePath string) (configs.ConfigItem, error) } // NewConfigManager is used to create ConfigManager func NewConfigManager(log logger.Logger) (ConfigManager, error) { return NewBasicConfigManager(log) } // BasicConfigManager is the basic implement of ConfigManager type BasicConfigManager struct { logger.Logger tree map[string][]configs.ConfigItem treeLock sync.RWMutex } // New is used to create a basic ConfigManager func NewBasicConfigManager(log logger.Logger) (*BasicConfigManager, error) { return &BasicConfigManager{ Logger: log.NewLogger("configmanager"), tree: map[string][]configs.ConfigItem{}, }, nil } // GetCompiler is used to get config of specified proto file path func (b *BasicConfigManager) GetConfig(ctx context.Context, protoFilePath string) (configs.ConfigItem, error) { possiblePath := configs.ListConfigPaths(filepath.Dir(protoFilePath)) for _, configFilePath := range possiblePath { items, err := b.loadConfig(configFilePath) if err != nil { return nil, err } dir := filepath.Dir(configFilePath) for _, config := range items { for _, scope := range config.Config().Scopes { scopePath := filepath.Join(dir, scope) if strings.Contains(protoFilePath, scopePath) { return config, nil } } } } return nil, errors.Errorf("unable to find config: %s", protoFilePath) } func (b *BasicConfigManager) loadConfig(configFilePath string) ([]configs.ConfigItem, error) { b.treeLock.Lock() defer b.treeLock.Unlock() configItems, ok := b.tree[configFilePath] if !ok { fileInfo, err := os.Stat(configFilePath) if err != nil { if os.IsNotExist(err) { b.tree[configFilePath] = nil return nil, nil } return nil, err } if fileInfo.IsDir() { b.tree[configFilePath] = nil return nil, nil } data, err := configs.LoadConfigItems(configFilePath) if err != nil { return nil, errors.WithMessagef(err, "failed to decode: %s", configFilePath) } b.tree[configFilePath] = data return data, nil } return configItems, nil }