package admin import ( "bytes" "context" "errors" "fmt" "reflect" "strings" "proxy-pool/internal/config" "proxy-pool/internal/domain/adminstate" ) var ErrInvalidConfigurationLoader = errors.New("invalid admin configuration loader") type FileConfigurationLoader struct { path string resolver config.Resolver } func NewFileConfigurationLoader(path string, resolver config.Resolver) (*FileConfigurationLoader, error) { if strings.TrimSpace(path) != path || path == "" || len(path) > adminstate.MaxSourceBytes || nilInterface(resolver) { return nil, ErrInvalidConfigurationLoader } return &FileConfigurationLoader{path: path, resolver: resolver}, nil } func (loader *FileConfigurationLoader) LoadConfiguration(ctx context.Context) (LoadedConfiguration, error) { if loader == nil || ctx == nil { return LoadedConfiguration{}, ErrInvalidConfigurationLoader } if err := ctx.Err(); err != nil { return LoadedConfiguration{}, err } content, err := loader.resolver.ReadFile(loader.path) if err != nil { return LoadedConfiguration{}, errors.Join(ErrUnavailable, fmt.Errorf("read configuration %q: %w", loader.path, err)) } if err := ctx.Err(); err != nil { return LoadedConfiguration{}, err } resolver := &observedConfigurationResolver{Resolver: loader.resolver} configuration, err := config.LoadResolved(bytes.NewReader(content), resolver) if err != nil { if resolver.readErr != nil { return LoadedConfiguration{}, errors.Join(ErrUnavailable, fmt.Errorf("load configuration %q: %w", loader.path, err)) } return LoadedConfiguration{}, errors.Join(ErrInvalidConfiguration, fmt.Errorf("load configuration %q: %w", loader.path, err)) } if err := ctx.Err(); err != nil { return LoadedConfiguration{}, err } return LoadedConfiguration{Value: configuration, Source: loader.path}, nil } type observedConfigurationResolver struct { config.Resolver readErr error } func (resolver *observedConfigurationResolver) ReadFile(path string) ([]byte, error) { content, err := resolver.Resolver.ReadFile(path) if err != nil { resolver.readErr = err } return content, err } func nilInterface(value any) bool { if value == nil { return true } reflected := reflect.ValueOf(value) switch reflected.Kind() { case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice: return reflected.IsNil() default: return false } }