Chromium Code Reviews| Index: server/config/format.go |
| diff --git a/server/config/format.go b/server/config/format.go |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..2c83db4a0c693a646b354f80364d22ec1be34a7f |
| --- /dev/null |
| +++ b/server/config/format.go |
| @@ -0,0 +1,156 @@ |
| +// Copyright 2016 The LUCI Authors. All rights reserved. |
| +// Use of this source code is governed under the Apache License, Version 2.0 |
| +// that can be found in the LICENSE file. |
| + |
| +package config |
| + |
| +import ( |
| + "fmt" |
| + "sync" |
| + |
| + "github.com/luci/luci-go/common/errors" |
| + |
| + "golang.org/x/net/context" |
| +) |
| + |
| +// Formatter applies a transformation to the data's cached representation. |
| +// |
| +// The Formatter is supplied with the content to format, and any additional |
| +// formatter data |
|
iannucci
2017/01/07 20:12:16
This is the format data that comes with the Params
dnj
2017/01/10 03:25:58
It's the data in Item's Content, implemented by th
|
| +type Formatter interface { |
| + FormatItem(c, fd string) (string, error) |
| +} |
| + |
| +// FormatterRegistry is a registry of Resolver key mapped to the Formatter to |
| +// use to format that key. |
| +type FormatterRegistry struct { |
| + registryMu sync.RWMutex |
| + registry map[string]Formatter |
| +} |
| + |
| +// Register registers a Formatter with the FormatBackend. |
| +// |
| +// If the supplied key is already registered, Register will panic. |
| +func (r *FormatterRegistry) Register(rk string, f Formatter) { |
| + if rk == "" { |
| + panic("cannot register empty key") |
| + } |
| + |
| + r.registryMu.Lock() |
| + defer r.registryMu.Unlock() |
| + |
| + if _, ok := r.registry[rk]; ok { |
| + panic(fmt.Errorf("key %q is already registered", rk)) |
| + } |
| + if r.registry == nil { |
| + r.registry = make(map[string]Formatter) |
|
iannucci
2017/01/07 20:12:16
nit: `map[string]Formatter{}` is shorter
dnj
2017/01/10 03:25:58
Done.
|
| + } |
| + r.registry[rk] = f |
| +} |
| + |
| +// Get returns the Formatter associated with the provided Format. |
| +func (r *FormatterRegistry) Get(f string) Formatter { |
| + r.registryMu.RLock() |
| + defer r.registryMu.RUnlock() |
| + return r.registry[f] |
| +} |
| + |
| +// FormatBackend is a Backend implementation that applies Formatter |
| +// transformations to the various Items that pass through it. |
| +// |
| +// Formatter transformations are registered explicitly to the Resolver |
| +// descriptions of the types that they operate on. If an Item is already |
| +// formatted, no further transformations will be applied. |
| +type FormatBackend struct { |
| + // Backend is the underlying Backend that this FormatBackend will pull data |
| + // from. |
| + Backend |
| + |
| + // GetRegistry returns the FormatterRegistry to use. If it returns nil, |
| + // no formatting will be done. |
| + GetRegistry func(context.Context) *FormatterRegistry |
| +} |
| + |
| +// Get implements Backend. |
| +func (b *FormatBackend) Get(c context.Context, configSet, path string, p Params) (*Item, error) { |
| + item, err := b.Backend.Get(c, configSet, path, p) |
| + if err != nil { |
| + return nil, err |
| + } |
| + |
| + if p.Format != "" { |
| + formatter, err := b.getFormatter(c, p.Format) |
| + if err != nil { |
| + return nil, errors.Annotate(err).Reason("failed to get formatter for %(format)q, data %(data)q"). |
| + D("format", p.Format).D("data", p.FormatData).Err() |
| + } |
| + |
| + if err := b.formatItem(item, formatter, p.Format, p.FormatData); err != nil { |
| + return nil, errors.Annotate(err).Reason("failed to format item to %(format)q, data %(data)q"). |
| + D("format", p.Format).D("data", p.FormatData).Err() |
| + } |
| + } |
| + return item, nil |
| +} |
| + |
| +// GetAll implements Backend. |
| +func (b *FormatBackend) GetAll(c context.Context, t GetAllType, path string, p Params) ([]*Item, error) { |
| + items, err := b.Backend.GetAll(c, t, path, p) |
| + if err != nil { |
| + return nil, err |
| + } |
| + |
| + if p.Format != "" { |
| + formatter, err := b.getFormatter(c, p.Format) |
| + if err != nil { |
| + return nil, errors.Annotate(err).Reason("failed to get formatter for %(format)q, data %(data)q"). |
| + D("format", p.Format).D("data", p.FormatData).Err() |
| + } |
| + |
| + lme := errors.NewLazyMultiError(len(items)) |
| + for i, item := range items { |
| + if err := b.formatItem(item, formatter, p.Format, p.FormatData); err != nil { |
| + lme.Assign(i, err) |
| + } |
| + } |
| + |
| + if err := lme.Get(); err != nil { |
| + return nil, errors.Annotate(err).Reason("failed to format items to %(format)q, data %(data)q"). |
| + D("format", p.Format).D("data", p.FormatData).Err() |
| + } |
| + } |
| + return items, nil |
| +} |
| + |
| +func (b *FormatBackend) getFormatter(c context.Context, f string) (Formatter, error) { |
| + if b.GetRegistry == nil { |
| + return nil, errors.New("no formatter registry function installed") |
| + } |
| + |
| + reg := b.GetRegistry(c) |
| + if reg == nil { |
| + return nil, errors.New("formatter registry function returned nil registry") |
| + } |
| + formatter := reg.Get(f) |
| + if formatter == nil { |
| + return nil, errors.Reason("unknown formatter: %(formatter)q").D("formatter", f).Err() |
| + } |
| + return formatter, nil |
| +} |
| + |
| +func (b *FormatBackend) formatItem(it *Item, formatter Formatter, f, fd string) error { |
| + if it.Format != "" { |
| + // Item is already formatted. |
| + return nil |
| + } |
| + |
| + // Item is not formatted, so format it. |
| + content, err := formatter.FormatItem(it.Content, fd) |
| + if err != nil { |
| + return errors.Annotate(err).Reason("failed to format item").Err() |
| + } |
| + it.Content = content |
| + it.Format = f |
| + it.FormatData = fd |
| + return nil |
| +} |