gosuflow

package module
v0.250507.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 7, 2025 License: 0BSD Imports: 5 Imported by: 2

README

gosuflow: GO StrUct FLOW runner

This package executes linear workflows specially defined via a struct:

type workflow struct {
  somevar1 type1
  somevar2 type2
  ...
  Step1Section struct{}
  step1var type3
  ...

  Step2Section struct{}
  step2var type4
  ...

  StepNSection struct{}
  stepNvar type5
}

func (wf *workflow) Step1(ctx context.Context) error { ... }
func (wf *workflow) Step2(ctx context.Context) error { ... }
...
func (wf *workflow) StepN(ctx context.Context) error { ... }

...
wf := &workflow{somevar1: ..., somevar2: ...}
if err := gosuflow.Run(ctx, wf); err != nil {
  ...
}

gosuflow.Run executes all exported methods the struct has defined. Each method MethodName must have a corresponding MethodNameSection field in the struct. gosuflow executes the methods in the order the sections appear in the struct. By convention each method can only access the fields defined above its section and must initialize the fields belonging to the section. gosuflow bails out after the first error.

Structuring a long workflow into a gosuflow struct makes the workflow's flow and data much clearer for humans. gosuflow.Run just fills the boilerplate of calling the functions in the right order and handling the errors. See the example how a table rendering workflow can be summarized into a gosuflow struct.

See https://pkg.go.dev/github.com/ypsu/gosuflow for the package documentation.

Documentation

Overview

gosuflow implements the GO StrUct FLOW runner.

Example
package main

import (
	"context"
	"fmt"
	"strings"
	"unicode/utf8"

	"github.com/ypsu/gosuflow"
)

type renderTableWorkflow struct {
	headerLabels []string
	data         [][]string

	ExtractDimensionsSection struct{}
	rows, columns            int
	columnWidths             []int

	CenterHeaderLabelsSection struct{}
	centeredHeaderLabels      []string

	GenerateHeaderSection struct{}
	header                string

	GenerateBodySection struct{}
	body                string

	PrintSection struct{}
}

func (wf *renderTableWorkflow) ExtractDimensions(ctx context.Context) error {
	wf.rows, wf.columns = len(wf.data), len(wf.headerLabels)
	wf.columnWidths = make([]int, wf.columns)
	for c, label := range wf.headerLabels {
		wf.columnWidths[c] = utf8.RuneCountInString(label)
	}
	for r, row := range wf.data {
		if len(row) != wf.rows {
			return fmt.Errorf("rendertable.BadRowColumnsCount row=%d columns=%d want=%d", r, len(row), wf.rows)
		}
		for c, v := range row {
			wf.columnWidths[c] = max(wf.columnWidths[c], utf8.RuneCountInString(v))
		}
	}
	return nil
}

func (wf *renderTableWorkflow) CenterHeaderLabels(ctx context.Context) error {
	wf.centeredHeaderLabels = make([]string, wf.columns)
	for c, label := range wf.headerLabels {
		spaces := wf.columnWidths[c] - utf8.RuneCountInString(label) + 2
		wf.centeredHeaderLabels[c] = strings.Repeat(" ", spaces/2) + label + strings.Repeat(" ", spaces/2+spaces%2)
	}
	return nil
}

func (wf *renderTableWorkflow) writeSeparatorLine(w *strings.Builder) {
	for _, c := range wf.columnWidths {
		w.WriteByte('+')
		for range c + 2 {
			w.WriteByte('-')
		}
	}
	w.WriteByte('+')
	w.WriteByte('\n')
}

func (wf *renderTableWorkflow) GenerateHeader(ctx context.Context) error {
	w := &strings.Builder{}
	wf.writeSeparatorLine(w)
	for _, label := range wf.centeredHeaderLabels {
		w.WriteByte('|')
		w.WriteString(label)
	}
	w.WriteByte('|')
	w.WriteByte('\n')
	wf.writeSeparatorLine(w)
	wf.header = w.String()
	return nil
}

func (wf *renderTableWorkflow) GenerateBody(ctx context.Context) error {
	w := &strings.Builder{}
	for _, row := range wf.data {
		for c, label := range row {
			w.WriteByte('|')
			w.WriteByte(' ')
			for range wf.columnWidths[c] - utf8.RuneCountInString(label) {
				w.WriteByte(' ')
			}
			w.WriteString(label)
			w.WriteByte(' ')
		}
		w.WriteByte('|')
		w.WriteByte('\n')
	}
	wf.writeSeparatorLine(w)
	wf.body = w.String()
	return nil
}

func (wf *renderTableWorkflow) Print(ctx context.Context) error {
	fmt.Print(wf.header)
	fmt.Print(wf.body)
	return nil
}

func main() {
	ctx := context.Background()
	wf := &renderTableWorkflow{
		headerLabels: []string{"City", "Population", "Area"},
		data: [][]string{
			[]string{"Dublin", "592,713", "117 km²"},
			[]string{"London", "8,866,180", "1,572 km²"},
			[]string{"Paris", "2,048,472", "105 km²"},
		},
	}
	if err := gosuflow.Run(ctx, wf); err != nil {
		fmt.Printf("Error: %v\n", err)
	}
}
Output:
+--------+------------+-----------+
|  City  | Population |   Area    |
+--------+------------+-----------+
| Dublin |    592,713 |   117 km² |
| London |  8,866,180 | 1,572 km² |
|  Paris |  2,048,472 |   105 km² |
+--------+------------+-----------+

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Run

func Run(ctx context.Context, workflow any) error

Run executes a workflow struct.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL