From 5c338a27ee22aad6bbfa24fa38442e674329e72e Mon Sep 17 00:00:00 2001 From: Tigor Hutasuhut Date: Thu, 27 Jun 2024 16:47:11 +0700 Subject: [PATCH] update workflow interface --- go.mod | 1 + go.sum | 2 + lib/qbitrun/qbitrun.go | 6 ++- lib/workflow/open.go | 109 +++++++++++++++++++++++++++++++++++++++ lib/workflow/workflow.go | 4 ++ 5 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 lib/workflow/open.go diff --git a/go.mod b/go.mod index e8dc3c9..5a904d9 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/testify v1.9.0 // indirect diff --git a/go.sum b/go.sum index c7da2f0..78a8e26 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= diff --git a/lib/qbitrun/qbitrun.go b/lib/qbitrun/qbitrun.go index 3cec9c9..8d5ffac 100644 --- a/lib/qbitrun/qbitrun.go +++ b/lib/qbitrun/qbitrun.go @@ -1,8 +1,10 @@ package qbitrun +import "github.com/tigorlazuardi/qbitrun/lib/workflow" + type QBitRun struct { context RunnerContext - workflow WorkflowDir + workflow workflow.WorkflowInput } // New creates a QBitRun instance intended for single session. @@ -13,7 +15,7 @@ type QBitRun struct { // Users must not use or modify the []io.ReadCloser in the WorkflowDir after // calling this function, especially if the WorkflowDir is created using // WorkflowFromReadClosers or WorkflowFromReaders. -func New(workflows WorkflowDir, ctx RunnerContext) *QBitRun { +func New(workflows workflow.WorkflowInput, ctx RunnerContext) *QBitRun { return &QBitRun{ context: ctx, workflow: workflows, diff --git a/lib/workflow/open.go b/lib/workflow/open.go new file mode 100644 index 0000000..b13f52d --- /dev/null +++ b/lib/workflow/open.go @@ -0,0 +1,109 @@ +package workflow + +import ( + "errors" + "io" + "io/fs" + "os" + "path/filepath" + + "github.com/sourcegraph/conc/iter" + "gopkg.in/yaml.v3" +) + +type WorkflowFiles []io.ReadCloser + +func (wo WorkflowFiles) Close() error { + var errs []error + for _, rc := range wo { + if err := rc.Close(); err != nil { + errs = append(errs, err) + } + } + return errors.Join(errs...) +} + +func (wo WorkflowFiles) Workflows() ([]Workflow, error) { + var ( + errs = make([]error, len(wo)) + workflows = make([]Workflow, len(wo)) + ) + + iter.ForEachIdx(wo, func(i int, rc *io.ReadCloser) { + var w Workflow + if err := yaml.NewDecoder(*rc).Decode(&w); err != nil { + errs[i] = err + } else { + workflows[i] = w + } + if err := (*rc).Close(); err != nil && errs[i] == nil { + errs[i] = err + } + }) + + return workflows, errors.Join(errs...) +} + +type DirFilter = func(path string, d fs.FileInfo) bool + +// OpenDir opens a directory and returns a WorkflowInput. +// +// Set filter to filter out unwanted files. +func OpenDir(dir string, filters ...DirFilter) (WorkflowFiles, error) { + var files []io.ReadCloser + err := filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + ok := true + for _, filter := range filters { + ok = filter(path, info) + if !ok { + break + } + } + if ok { + file, err := os.Open(path) + if err != nil { + return err + } + files = append(files, file) + } + return nil + }) + if err != nil { + return nil, err + } + + return WorkflowFiles(files), nil +} + +// WorkflowFromReadClosers creates a WorkflowInput from a list of io.ReadCloser. +// +// WorkflowFromReadClosers assumes ownership of the ReadClosers and will close +// all of them after the WorkflowInput is consumed (whether successfully or not). +func WorkflowFromReadClosers(rcs ...io.ReadCloser) WorkflowFiles { + return WorkflowFiles(rcs) +} + +// WorkflowFromReaders creates a WorkflowInput from a list of io.Reader. +// +// If the Reader implements io.ReadCloser, it will be used as is. +// If not, it will be wrapped with io.NopCloser. +// +// WorkflowFromReaders assumes ownership of the Readers and will close (if implements io.ReadCloser) +// all of them after the WorkflowInput is consumed (whether successfully or not). +func WorkflowFromReaders(readers ...io.Reader) WorkflowFiles { + var rcs []io.ReadCloser + for _, r := range readers { + if rc, ok := r.(io.ReadCloser); ok { + rcs = append(rcs, rc) + } else { + rcs = append(rcs, io.NopCloser(r)) + } + } + return WorkflowFiles(rcs) +} diff --git a/lib/workflow/workflow.go b/lib/workflow/workflow.go index 59571e9..4602e10 100644 --- a/lib/workflow/workflow.go +++ b/lib/workflow/workflow.go @@ -1,5 +1,9 @@ package workflow +type WorkflowInput interface { + Workflows() ([]Workflow, error) +} + type Workflow struct { Name string `yaml:"name"` Disabled bool `yaml:"disabled"`