1
0
mirror of https://github.com/go-task/task.git synced 2025-11-29 22:48:03 +02:00

feat: better functional options for reader (#2148)

This commit is contained in:
Pete Davison
2025-04-01 14:51:25 +01:00
committed by GitHub
parent 1939f83ffe
commit cd81d94e18
5 changed files with 266 additions and 203 deletions

View File

@@ -28,14 +28,14 @@ Continue?`
)
type (
// ReaderDebugFunc is a function that is called when the [Reader] wants to
// log debug messages
ReaderDebugFunc func(string)
// ReaderPromptFunc is a function that is called when the [Reader] wants to
// prompt the user in some way
ReaderPromptFunc func(string) error
// ReaderOption is a function that configures a [Reader].
ReaderOption func(*Reader)
// DebugFunc is a function that can be called to log debug messages.
DebugFunc func(string)
// PromptFunc is a function that can be called to prompt the user for input.
PromptFunc func(string) error
// A ReaderOption is any type that can apply a configuration to a [Reader].
ReaderOption interface {
ApplyToReader(*Reader)
}
// A Reader will recursively read Taskfiles from a given [Node] and build a
// [ast.TaskfileGraph] from them.
Reader struct {
@@ -46,8 +46,8 @@ type (
offline bool
timeout time.Duration
tempDir string
debugFunc ReaderDebugFunc
promptFunc ReaderPromptFunc
debugFunc DebugFunc
promptFunc PromptFunc
promptMutex sync.Mutex
}
)
@@ -78,72 +78,113 @@ func NewReader(
// the [Reader].
func (r *Reader) Options(opts ...ReaderOption) {
for _, opt := range opts {
opt(r)
opt.ApplyToReader(r)
}
}
// ReaderWithInsecure allows the [Reader] to make insecure connections when
// reading remote taskfiles. By default, insecure connections are rejected.
func ReaderWithInsecure(insecure bool) ReaderOption {
return func(r *Reader) {
r.insecure = insecure
}
// WithInsecure allows the [Reader] to make insecure connections when reading
// remote taskfiles. By default, insecure connections are rejected.
func WithInsecure(insecure bool) ReaderOption {
return &insecureOption{insecure: insecure}
}
// ReaderWithDownload forces the [Reader] to download a fresh copy of the
// taskfile from the remote source.
func ReaderWithDownload(download bool) ReaderOption {
return func(r *Reader) {
r.download = download
}
type insecureOption struct {
insecure bool
}
// ReaderWithOffline stops the [Reader] from being able to make network
// connections. It will still be able to read local files and cached copies of
// remote files.
func ReaderWithOffline(offline bool) ReaderOption {
return func(r *Reader) {
r.offline = offline
}
func (o *insecureOption) ApplyToReader(r *Reader) {
r.insecure = o.insecure
}
// ReaderWithTimeout sets the [Reader]'s timeout for fetching remote taskfiles.
// By default, the timeout is set to 10 seconds.
func ReaderWithTimeout(timeout time.Duration) ReaderOption {
return func(r *Reader) {
r.timeout = timeout
}
// WithDownload forces the [Reader] to download a fresh copy of the taskfile
// from the remote source.
func WithDownload(download bool) ReaderOption {
return &downloadOption{download: download}
}
// ReaderWithTempDir sets the temporary directory that will be used by the
// [Reader]. By default, the reader uses [os.TempDir].
func ReaderWithTempDir(tempDir string) ReaderOption {
return func(r *Reader) {
r.tempDir = tempDir
}
type downloadOption struct {
download bool
}
// ReaderWithDebugFunc sets the debug function to be used by the [Reader]. If
// set, this function will be called with debug messages. This can be useful if
// the caller wants to log debug messages from the [Reader]. By default, no
// debug function is set and the logs are not written.
func ReaderWithDebugFunc(debugFunc ReaderDebugFunc) ReaderOption {
return func(r *Reader) {
r.debugFunc = debugFunc
}
func (o *downloadOption) ApplyToReader(r *Reader) {
r.download = o.download
}
// ReaderWithPromptFunc sets the prompt function to be used by the [Reader]. If
// set, this function will be called with prompt messages. The function should
// WithOffline stops the [Reader] from being able to make network connections.
// It will still be able to read local files and cached copies of remote files.
func WithOffline(offline bool) ReaderOption {
return &offlineOption{offline: offline}
}
type offlineOption struct {
offline bool
}
func (o *offlineOption) ApplyToReader(r *Reader) {
r.offline = o.offline
}
// WithTimeout sets the [Reader]'s timeout for fetching remote taskfiles. By
// default, the timeout is set to 10 seconds.
func WithTimeout(timeout time.Duration) ReaderOption {
return &timeoutOption{timeout: timeout}
}
type timeoutOption struct {
timeout time.Duration
}
func (o *timeoutOption) ApplyToReader(r *Reader) {
r.timeout = o.timeout
}
// WithTempDir sets the temporary directory that will be used by the [Reader].
// By default, the reader uses [os.TempDir].
func WithTempDir(tempDir string) ReaderOption {
return &tempDirOption{tempDir: tempDir}
}
type tempDirOption struct {
tempDir string
}
func (o *tempDirOption) ApplyToReader(r *Reader) {
r.tempDir = o.tempDir
}
// WithDebugFunc sets the debug function to be used by the [Reader]. If set,
// this function will be called with debug messages. This can be useful if the
// caller wants to log debug messages from the [Reader]. By default, no debug
// function is set and the logs are not written.
func WithDebugFunc(debugFunc DebugFunc) ReaderOption {
return &debugFuncOption{debugFunc: debugFunc}
}
type debugFuncOption struct {
debugFunc DebugFunc
}
func (o *debugFuncOption) ApplyToReader(r *Reader) {
r.debugFunc = o.debugFunc
}
// WithPromptFunc sets the prompt function to be used by the [Reader]. If set,
// this function will be called with prompt messages. The function should
// optionally log the message to the user and return nil if the prompt is
// accepted and the execution should continue. Otherwise, it should return an
// error which describes why the the prompt was rejected. This can then be
// caught and used later when calling the [Reader.Read] method. By default, no
// prompt function is set and all prompts are automatically accepted.
func ReaderWithPromptFunc(promptFunc ReaderPromptFunc) ReaderOption {
return func(r *Reader) {
r.promptFunc = promptFunc
}
// error which describes why the prompt was rejected. This can then be caught
// and used later when calling the [Reader.Read] method. By default, no prompt
// function is set and all prompts are automatically accepted.
func WithPromptFunc(promptFunc PromptFunc) ReaderOption {
return &promptFuncOption{promptFunc: promptFunc}
}
type promptFuncOption struct {
promptFunc PromptFunc
}
func (o *promptFuncOption) ApplyToReader(r *Reader) {
r.promptFunc = o.promptFunc
}
// Read will read the Taskfile defined by the [Reader]'s [Node] and recurse
@@ -292,9 +333,9 @@ func (r *Reader) readNode(node Node) (*ast.Taskfile, error) {
taskfileDecodeErr := &errors.TaskfileDecodeError{}
if errors.As(err, &taskfileDecodeErr) {
snippet := NewSnippet(b,
SnippetWithLine(taskfileDecodeErr.Line),
SnippetWithColumn(taskfileDecodeErr.Column),
SnippetWithPadding(2),
WithLine(taskfileDecodeErr.Line),
WithColumn(taskfileDecodeErr.Column),
WithPadding(2),
)
return nil, taskfileDecodeErr.WithFileInfo(node.Location(), snippet.String())
}