From a086b858bcc94b24267e8ffe540762f9dababd16 Mon Sep 17 00:00:00 2001 From: "Gustavo L de Mello (Guz)" Date: Wed, 22 Jan 2025 19:35:44 -0300 Subject: [PATCH] refactor(blogo,plugins): move built-in plugins to plugins package --- .../emptysourcer.go} | 13 ++- .../foldingrenderer.go} | 41 +++++----- .../multirenderer.go} | 50 ++++++------ .../multisourcer.go} | 62 +++++++------- .../plaintext.go} | 12 +-- blogo/plugins/plugingroup.go | 0 blogo/plugins/plugins.go | 16 ++++ .../prefixedsourcer.go} | 80 ++++++++++--------- .../prioritylist.go} | 28 ++++--- 9 files changed, 169 insertions(+), 133 deletions(-) rename blogo/{plugin_emptysourcer.go => plugins/emptysourcer.go} (80%) rename blogo/{plugin_foldingrenderer.go => plugins/foldingrenderer.go} (75%) rename blogo/{plugin_multirenderer.go => plugins/multirenderer.go} (74%) rename blogo/{plugin_multisourcer.go => plugins/multisourcer.go} (68%) rename blogo/{plugin_plaintext.go => plugins/plaintext.go} (87%) create mode 100644 blogo/plugins/plugingroup.go create mode 100644 blogo/plugins/plugins.go rename blogo/{plugin_prefixedsourcer.go => plugins/prefixedsourcer.go} (63%) rename blogo/{plugin_prioritylist.go => plugins/prioritylist.go} (68%) diff --git a/blogo/plugin_emptysourcer.go b/blogo/plugins/emptysourcer.go similarity index 80% rename from blogo/plugin_emptysourcer.go rename to blogo/plugins/emptysourcer.go index 3205350..3aa9c6e 100644 --- a/blogo/plugin_emptysourcer.go +++ b/blogo/plugins/emptysourcer.go @@ -13,15 +13,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package blogo +package plugins -import "io/fs" +import ( + "forge.capytal.company/loreddev/x/blogo/fs" + "forge.capytal.company/loreddev/x/blogo/plugin" +) const emptySourcerPluginName = "blogo-empty-sourcer" type emptySourcer struct{} -func NewEmptySourcer() Plugin { +func NewEmptySourcer() plugin.Plugin { return &emptySourcer{} } @@ -35,6 +38,10 @@ func (p *emptySourcer) Source() (fs.FS, error) { type emptyFS struct{} +func (f emptyFS) Metadata() fs.Metadata { + return fs.MetadataMap(map[string]any{}) +} + func (f emptyFS) Open(name string) (fs.File, error) { return nil, fs.ErrNotExist } diff --git a/blogo/plugin_foldingrenderer.go b/blogo/plugins/foldingrenderer.go similarity index 75% rename from blogo/plugin_foldingrenderer.go rename to blogo/plugins/foldingrenderer.go index 5991ce8..8b75b12 100644 --- a/blogo/plugin_foldingrenderer.go +++ b/blogo/plugins/foldingrenderer.go @@ -13,19 +13,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -package blogo +package plugins import ( "bytes" "fmt" "io" "log/slog" + + "forge.capytal.company/loreddev/x/blogo/fs" + "forge.capytal.company/loreddev/x/blogo/plugin" ) const foldingRendererPluginName = "blogo-foldingrenderer-renderer" type foldingRenderer struct { - plugins []RendererPlugin + plugins []plugin.Renderer panicOnInit bool @@ -39,8 +42,8 @@ type FoldingRendererOpts struct { } type FoldingRenderer interface { - PluginWithPlugins - RendererPlugin + plugin.WithPlugins + plugin.Renderer } func NewFoldingRenderer(opts ...FoldingRendererOpts) FoldingRenderer { @@ -55,32 +58,32 @@ func NewFoldingRenderer(opts ...FoldingRendererOpts) FoldingRenderer { opt.Logger = opt.Logger.WithGroup(foldingRendererPluginName) return &foldingRenderer{ - plugins: []RendererPlugin{}, + plugins: []plugin.Renderer{}, log: opt.Logger, } } -func (p *foldingRenderer) Name() string { +func (r *foldingRenderer) Name() string { return foldingRendererPluginName } -func (p *foldingRenderer) Use(plugin Plugin) { - log := p.log.With(slog.String("plugin", plugin.Name())) +func (r *foldingRenderer) Use(p plugin.Plugin) { + log := r.log.With(slog.String("plugin", p.Name())) - if plg, ok := plugin.(RendererPlugin); ok { - p.plugins = append(p.plugins, plg) + if pr, ok := p.(plugin.Renderer); ok { + r.plugins = append(r.plugins, pr) } else { - m := fmt.Sprintf("failed to add plugin %q, since it doesn't implement RendererPlugin", plugin.Name()) + m := fmt.Sprintf("failed to add plugin %q, since it doesn't implement plugin.Renderer", p.Name()) log.Error(m) - if p.panicOnInit { - panic(fmt.Sprintf("%s: %s", multiRendererPluginName, m)) + if r.panicOnInit { + panic(fmt.Sprintf("%s: %s", foldingRendererPluginName, m)) } } } -func (p *foldingRenderer) Render(src File, w io.Writer) error { - if len(p.plugins) == 0 { +func (r *foldingRenderer) Render(src fs.File, w io.Writer) error { + if len(r.plugins) == 0 { _, err := io.Copy(w, src) return err } @@ -90,8 +93,8 @@ func (p *foldingRenderer) Render(src File, w io.Writer) error { return err } - for _, r := range p.plugins { - err := r.Render(f, f) + for _, p := range r.plugins { + err := p.Render(f, f) if err != nil { return err } @@ -106,12 +109,12 @@ func (p *foldingRenderer) Render(src File, w io.Writer) error { } type foldingFile struct { - File + fs.File read *bytes.Buffer writer *bytes.Buffer } -func newFoldignFile(f File) (*foldingFile, error) { +func newFoldignFile(f fs.File) (*foldingFile, error) { var r, w bytes.Buffer if _, err := io.Copy(&r, f); err != nil { diff --git a/blogo/plugin_multirenderer.go b/blogo/plugins/multirenderer.go similarity index 74% rename from blogo/plugin_multirenderer.go rename to blogo/plugins/multirenderer.go index ed2ed76..0c417ed 100644 --- a/blogo/plugin_multirenderer.go +++ b/blogo/plugins/multirenderer.go @@ -13,28 +13,30 @@ // See the License for the specific language governing permissions and // limitations under the License. -package blogo +package plugins import ( "bytes" "errors" "fmt" "io" - "io/fs" "log/slog" + + "forge.capytal.company/loreddev/x/blogo/fs" + "forge.capytal.company/loreddev/x/blogo/plugin" ) var ErrRendererNotSupportedFile = errors.New("this file is not supported by renderer") -const multiRendererPluginName = "blogo-multirenderer-renderer" +const multiRendererName = "blogo-multirenderer-renderer" type MultiRenderer interface { - RendererPlugin - PluginWithPlugins + plugin.Renderer + plugin.WithPlugins } type multiRenderer struct { - renderers []RendererPlugin + renderers []plugin.Renderer skipOnError bool panicOnInit bool @@ -57,10 +59,10 @@ func NewMultiRenderer(opts ...MultiRendererOpts) MultiRenderer { if opt.Logger == nil { opt.Logger = slog.New(slog.NewTextHandler(io.Discard, &slog.HandlerOptions{})) } - opt.Logger = opt.Logger.WithGroup(multiRendererPluginName) + opt.Logger = opt.Logger.WithGroup(multiRendererName) return &multiRenderer{ - renderers: []RendererPlugin{}, + renderers: []plugin.Renderer{}, skipOnError: !opt.NotSkipOnError, panicOnInit: !opt.NotPanicOnInit, @@ -69,40 +71,40 @@ func NewMultiRenderer(opts ...MultiRendererOpts) MultiRenderer { } } -func (p *multiRenderer) Name() string { - return multiRendererPluginName +func (r *multiRenderer) Name() string { + return multiRendererName } -func (p *multiRenderer) Use(plugin Plugin) { - log := p.log.With(slog.String("plugin", plugin.Name())) +func (r *multiRenderer) Use(p plugin.Plugin) { + log := r.log.With(slog.String("plugin", p.Name())) - if plg, ok := plugin.(RendererPlugin); ok { + if pr, ok := p.(plugin.Renderer); ok { log.Debug("Added renderer plugin") - p.renderers = append(p.renderers, plg) + r.renderers = append(r.renderers, pr) } else { - m := fmt.Sprintf("failed to add plugin %q, since it doesn't implement RendererPlugin", plugin.Name()) + m := fmt.Sprintf("failed to add plugin %q, since it doesn't implement plugin.Renderer", p.Name()) log.Error(m) - if p.panicOnInit { - panic(fmt.Sprintf("%s: %s", p.Name(), m)) + if r.panicOnInit { + panic(fmt.Sprintf("%s: %s", r.Name(), m)) } } } -func (p *multiRenderer) Render(f File, w io.Writer) error { - mf := newMultiRendererFile(f) - for _, r := range p.renderers { - log := p.log.With(slog.String("plugin", r.Name())) +func (r *multiRenderer) Render(src fs.File, w io.Writer) error { + mf := newMultiRendererFile(src) + for _, pr := range r.renderers { + log := r.log.With(slog.String("plugin", pr.Name())) log.Debug("Trying to render with plugin") - err := r.Render(f, w) + err := pr.Render(src, w) if err == nil { break } - if !p.skipOnError && !errors.Is(err, ErrRendererNotSupportedFile) { + if !r.skipOnError && !errors.Is(err, ErrRendererNotSupportedFile) { log.Error("Failed to render using plugin", slog.String("error", err.Error())) - return errors.Join(fmt.Errorf("failed to render using plugin %q", p.Name()), err) + return errors.Join(fmt.Errorf("failed to render using plugin %q", pr.Name()), err) } log.Debug("Unable to render using plugin", slog.String("error", err.Error())) diff --git a/blogo/plugin_multisourcer.go b/blogo/plugins/multisourcer.go similarity index 68% rename from blogo/plugin_multisourcer.go rename to blogo/plugins/multisourcer.go index ab3c0e3..a14ec72 100644 --- a/blogo/plugin_multisourcer.go +++ b/blogo/plugins/multisourcer.go @@ -13,25 +13,27 @@ // See the License for the specific language governing permissions and // limitations under the License. -package blogo +package plugins import ( "errors" "fmt" "io" - "io/fs" "log/slog" + + "forge.capytal.company/loreddev/x/blogo/fs" + "forge.capytal.company/loreddev/x/blogo/plugin" ) -const multiSourcerPluginName = "blogo-multisourcer-sourcer" +const multiSourcerName = "blogo-multisourcer-sourcer" type MultiSourcer interface { - SourcerPlugin - PluginWithPlugins + plugin.Sourcer + plugin.WithPlugins } type multiSourcer struct { - sources []SourcerPlugin + sources []plugin.Sourcer panicOnInit bool skipOnSourceError bool @@ -57,10 +59,10 @@ func NewMultiSourcer(opts ...MultiSourcerOpts) MultiSourcer { if opt.Logger == nil { opt.Logger = slog.New(slog.NewTextHandler(io.Discard, &slog.HandlerOptions{})) } - opt.Logger = opt.Logger.WithGroup(multiSourcerPluginName) + opt.Logger = opt.Logger.WithGroup(multiSourcerName) return &multiSourcer{ - sources: []SourcerPlugin{}, + sources: []plugin.Sourcer{}, panicOnInit: !opt.NotPanicOnInit, skipOnSourceError: !opt.NotSkipOnSourceError, @@ -70,36 +72,36 @@ func NewMultiSourcer(opts ...MultiSourcerOpts) MultiSourcer { } } -func (p *multiSourcer) Name() string { - return multiSourcerPluginName +func (s *multiSourcer) Name() string { + return multiSourcerName } -func (p *multiSourcer) Use(plugin Plugin) { - log := p.log.With(slog.String("plugin", plugin.Name())) +func (s *multiSourcer) Use(p plugin.Plugin) { + log := s.log.With(slog.String("plugin", p.Name())) - if plg, ok := plugin.(SourcerPlugin); ok { + if plg, ok := p.(plugin.Sourcer); ok { log.Debug("Added sourcer plugin") - p.sources = append(p.sources, plg) + s.sources = append(s.sources, plg) } else { - m := fmt.Sprintf("failed to add plugin %q, since it doesn't implement SourcerPlugin", plugin.Name()) + m := fmt.Sprintf("failed to add plugin %q, since it doesn't implement plugin.Sourcer", p.Name()) log.Error(m) - if p.panicOnInit { + if s.panicOnInit { panic(fmt.Sprintf("%s: %s", p.Name(), m)) } } } -func (p *multiSourcer) Source() (FS, error) { - log := p.log +func (s *multiSourcer) Source() (fs.FS, error) { + log := s.log - fileSystems := []FS{} + fileSystems := []fs.FS{} - for _, s := range p.sources { - log = log.With(slog.String("plugin", p.Name())) + for _, ps := range s.sources { + log = log.With(slog.String("plugin", ps.Name())) log.Info("Sourcing file system of plugin") - f, err := s.Source() - if err != nil && p.skipOnSourceError { + f, err := ps.Source() + if err != nil && s.skipOnSourceError { log.Error( "Failed to source file system of plugin, skipping", slog.String("error", err.Error()), @@ -115,31 +117,31 @@ func (p *multiSourcer) Source() (FS, error) { fileSystems = append(fileSystems, f) } - f := make([]FS, len(fileSystems), len(fileSystems)) + f := make([]fs.FS, len(fileSystems)) for i := range f { f[i] = fileSystems[i] } return &multiSourcerFS{ fileSystems: f, - skipOnError: p.skipOnFSError, + skipOnError: s.skipOnFSError, }, nil } type multiSourcerFS struct { - fileSystems []FS + fileSystems []fs.FS skipOnError bool } -func (pf *multiSourcerFS) Metadata() Metadata { - var m Metadata +func (pf *multiSourcerFS) Metadata() fs.Metadata { + var m fs.Metadata for _, v := range pf.fileSystems { - m = JoinMetadata(m, v.Metadata()) + m = fs.JoinMetadata(m, v.Metadata()) } return m } -func (mf *multiSourcerFS) Open(name string) (File, error) { +func (mf *multiSourcerFS) Open(name string) (fs.File, error) { for _, f := range mf.fileSystems { file, err := f.Open(name) diff --git a/blogo/plugin_plaintext.go b/blogo/plugins/plaintext.go similarity index 87% rename from blogo/plugin_plaintext.go rename to blogo/plugins/plaintext.go index 7da7d62..b3eafda 100644 --- a/blogo/plugin_plaintext.go +++ b/blogo/plugins/plaintext.go @@ -13,25 +13,27 @@ // See the License for the specific language governing permissions and // limitations under the License. -package blogo +package plugins import ( "errors" "fmt" "io" - "io/fs" + + "forge.capytal.company/loreddev/x/blogo/fs" + "forge.capytal.company/loreddev/x/blogo/plugin" ) -const plainTextPluginName = "blogo-plaintext-renderer" +const plainTextName = "blogo-plaintext-renderer" type painText struct{} -func NewPlainText() Plugin { +func NewPlainText() plugin.Plugin { return &painText{} } func (p *painText) Name() string { - return plainTextPluginName + return plainTextName } func (p *painText) Render(f fs.File, w io.Writer) error { diff --git a/blogo/plugins/plugingroup.go b/blogo/plugins/plugingroup.go new file mode 100644 index 0000000..e69de29 diff --git a/blogo/plugins/plugins.go b/blogo/plugins/plugins.go new file mode 100644 index 0000000..d95b720 --- /dev/null +++ b/blogo/plugins/plugins.go @@ -0,0 +1,16 @@ +// Copyright 2025-present Gustavo "Guz" L. de Mello +// Copyright 2025-present The Lored.dev Contributors +// +// 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 plugins diff --git a/blogo/plugin_prefixedsourcer.go b/blogo/plugins/prefixedsourcer.go similarity index 63% rename from blogo/plugin_prefixedsourcer.go rename to blogo/plugins/prefixedsourcer.go index fababca..39617d2 100644 --- a/blogo/plugin_prefixedsourcer.go +++ b/blogo/plugins/prefixedsourcer.go @@ -13,26 +13,28 @@ // See the License for the specific language governing permissions and // limitations under the License. -package blogo +package plugins import ( "fmt" "io" - "io/fs" "log/slog" "strings" + + "forge.capytal.company/loreddev/x/blogo/fs" + "forge.capytal.company/loreddev/x/blogo/plugin" ) -const prefixedSourcerPluginName = "blogo-prefixedsourcer-sourcer" +const prefixedSourcerName = "blogo-prefixedsourcer-sourcer" type PrefixedSourcer interface { - SourcerPlugin - PluginWithPlugins - UseNamed(string, Plugin) + plugin.Sourcer + plugin.WithPlugins + UseNamed(string, plugin.Plugin) } type prefixedSourcer struct { - sources map[string]SourcerPlugin + sources map[string]plugin.Sourcer prefixSeparator string acceptDuplicated bool @@ -69,10 +71,10 @@ func NewPrefixedSourcer(opts ...PrefixedSourcerOpts) PrefixedSourcer { if opt.Logger == nil { opt.Logger = slog.New(slog.NewTextHandler(io.Discard, &slog.HandlerOptions{})) } - opt.Logger = opt.Logger.WithGroup(prefixedSourcerPluginName) + opt.Logger = opt.Logger.WithGroup(prefixedSourcerName) return &prefixedSourcer{ - sources: map[string]SourcerPlugin{}, + sources: map[string]plugin.Sourcer{}, prefixSeparator: opt.PrefixSeparator, acceptDuplicated: opt.AcceptDuplicated, @@ -85,56 +87,56 @@ func NewPrefixedSourcer(opts ...PrefixedSourcerOpts) PrefixedSourcer { } } -func (p *prefixedSourcer) Name() string { - return prefixedSourcerPluginName +func (s *prefixedSourcer) Name() string { + return prefixedSourcerName } -func (p *prefixedSourcer) Use(plugin Plugin) { - p.UseNamed(plugin.Name(), plugin) +func (s *prefixedSourcer) Use(plugin plugin.Plugin) { + s.UseNamed(plugin.Name(), plugin) } -func (p *prefixedSourcer) UseNamed(prefix string, plugin Plugin) { - log := p.log.With(slog.String("plugin", plugin.Name()), slog.String("prefix", prefix)) +func (s *prefixedSourcer) UseNamed(prefix string, p plugin.Plugin) { + log := s.log.With(slog.String("plugin", p.Name()), slog.String("prefix", prefix)) - var sourcer SourcerPlugin - if plg, ok := plugin.(SourcerPlugin); ok { - sourcer = plg + var sourcer plugin.Sourcer + if ps, ok := p.(plugin.Sourcer); ok { + sourcer = ps } else { - m := fmt.Sprintf("failed to add plugin %q (with prefix %q), since it doesn't implement SourcerPlugin", plugin.Name(), prefix) + m := fmt.Sprintf("failed to add plugin %q (with prefix %q), since it doesn't implement SourcerPlugin", p.Name(), prefix) log.Error(m) - if p.panicOnInit { - panic(fmt.Sprintf("%s: %s", multiRendererPluginName, m)) + if s.panicOnInit { + panic(fmt.Sprintf("%s: %s", prefixedSourcerName, m)) } } - if _, ok := p.sources[prefix]; ok && !p.acceptDuplicated { + if _, ok := s.sources[prefix]; ok && !s.acceptDuplicated { m := fmt.Sprintf( "duplicated prefix (%q) for plugin %q", prefix, - plugin.Name(), + p.Name(), ) log.Error(m) - if p.panicOnInit { - panic(fmt.Sprintf("%s: %s", multiRendererPluginName, m)) + if s.panicOnInit { + panic(fmt.Sprintf("%s: %s", prefixedSourcerName, m)) } return } log.Debug(fmt.Sprintf("Added sourcer plugin, with prefix %q", prefix)) - p.sources[prefix] = sourcer + s.sources[prefix] = sourcer } -func (p *prefixedSourcer) Source() (FS, error) { - log := p.log +func (s *prefixedSourcer) Source() (fs.FS, error) { + log := s.log - fileSystems := make(map[string]FS, len(p.sources)) + fileSystems := make(map[string]fs.FS, len(s.sources)) - for a, s := range p.sources { - log = log.With(slog.String("plugin", p.Name()), slog.String("prefix", a)) + for a, ps := range s.sources { + log = log.With(slog.String("plugin", ps.Name()), slog.String("prefix", a)) log.Info("Sourcing file system of plugin") - f, err := s.Source() - if err != nil && p.skipOnSourceError { + f, err := ps.Source() + if err != nil && s.skipOnSourceError { log.Error( "Failed to source file system of plugin, skipping", slog.String("error", err.Error()), @@ -152,24 +154,24 @@ func (p *prefixedSourcer) Source() (FS, error) { return &prefixedSourcerFS{ fileSystems: fileSystems, - prefixSeparator: p.prefixSeparator, + prefixSeparator: s.prefixSeparator, }, nil } type prefixedSourcerFS struct { - fileSystems map[string]FS + fileSystems map[string]fs.FS prefixSeparator string } -func (pf *prefixedSourcerFS) Metadata() Metadata { - var m Metadata +func (pf *prefixedSourcerFS) Metadata() fs.Metadata { + var m fs.Metadata for _, v := range pf.fileSystems { - m = JoinMetadata(m, v.Metadata()) + m = fs.JoinMetadata(m, v.Metadata()) } return m } -func (pf *prefixedSourcerFS) Open(name string) (File, error) { +func (pf *prefixedSourcerFS) Open(name string) (fs.File, error) { prefix, path, found := strings.Cut(name, pf.prefixSeparator) if !found { return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} diff --git a/blogo/plugin_prioritylist.go b/blogo/plugins/prioritylist.go similarity index 68% rename from blogo/plugin_prioritylist.go rename to blogo/plugins/prioritylist.go index b17c9ea..9c69c49 100644 --- a/blogo/plugin_prioritylist.go +++ b/blogo/plugins/prioritylist.go @@ -13,43 +13,45 @@ // See the License for the specific language governing permissions and // limitations under the License. -package blogo +package plugins import ( "cmp" "slices" + + "forge.capytal.company/loreddev/x/blogo/plugin" ) -const priorityGroupPluginName = "blogo-prioritygroup-group" +const priorityGroupName = "blogo-prioritygroup-group" type priorityGroup struct { - plugins []Plugin + plugins []plugin.Plugin } type PriorityGroup interface { - PluginWithPlugins + plugin.WithPlugins } -func NewPriorityGroup(plugins ...Plugin) PriorityGroup { +func NewPriorityGroup(plugins ...plugin.Plugin) PriorityGroup { return &priorityGroup{plugins} } func (p *priorityGroup) Name() string { - return priorityGroupPluginName + return priorityGroupName } -func (p *priorityGroup) Use(plugin Plugin) { +func (p *priorityGroup) Use(plugin plugin.Plugin) { p.plugins = append(p.plugins, plugin) } -func (p *priorityGroup) Plugins() []Plugin { - slices.SortStableFunc(p.plugins, func(a Plugin, b Plugin) int { +func (p *priorityGroup) Plugins() []plugin.Plugin { + slices.SortStableFunc(p.plugins, func(a plugin.Plugin, b plugin.Plugin) int { return cmp.Compare(p.getPriority(a, b), p.getPriority(b, a)) }) return p.plugins } -func (p *priorityGroup) getPriority(plugin Plugin, cmp Plugin) int { +func (p *priorityGroup) getPriority(plugin plugin.Plugin, cmp plugin.Plugin) int { if plg, ok := plugin.(PluginWithDynamicPriority); ok { return plg.Priority(cmp) } else if plg, ok := plugin.(PluginWithPriority); ok { @@ -60,11 +62,11 @@ func (p *priorityGroup) getPriority(plugin Plugin, cmp Plugin) int { } type PluginWithPriority interface { - Plugin + plugin.Plugin Priority() int } type PluginWithDynamicPriority interface { - Plugin - Priority(Plugin) int + plugin.Plugin + Priority(plugin.Plugin) int }