121 lines
3.9 KiB
Go
121 lines
3.9 KiB
Go
package datastar
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// executeScriptOptions hold script options that will be translated to [SSEEventOptions].
|
|
type executeScriptOptions struct {
|
|
EventID string
|
|
RetryDuration time.Duration
|
|
Attributes []string
|
|
AutoRemove *bool
|
|
}
|
|
|
|
// ExecuteScriptOption configures script execution event that will be sent to the client.
|
|
type ExecuteScriptOption func(*executeScriptOptions) error
|
|
|
|
// WithExecuteScriptEventID configures an optional event ID for the script execution event.
|
|
// The client message field [lastEventId] will be set to this value.
|
|
// If the next event does not have an event ID, the last used event ID will remain.
|
|
//
|
|
// [lastEventId]: https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent/lastEventId
|
|
func WithExecuteScriptEventID(id string) ExecuteScriptOption {
|
|
return func(o *executeScriptOptions) error {
|
|
o.EventID = id
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// WithExecuteScriptRetryDuration overrides the [DefaultSseRetryDuration] for this script
|
|
// execution only.
|
|
func WithExecuteScriptRetryDuration(retryDuration time.Duration) ExecuteScriptOption {
|
|
return func(o *executeScriptOptions) error {
|
|
o.RetryDuration = retryDuration
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// WithExecuteScriptAttributes overrides the default script attribute
|
|
// value `type module`, which renders as `<script type="module">` in client's browser.
|
|
// Each attribute should include a pair of plain words representing the attribute name and value
|
|
// without any formatting.
|
|
func WithExecuteScriptAttributes(attributes ...string) ExecuteScriptOption {
|
|
return func(o *executeScriptOptions) error {
|
|
o.Attributes = attributes
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// WithExecuteScriptAttributeKVs is an alternative option for [WithExecuteScriptAttributes].
|
|
// Even parameters are keys, odd parameters are their values.
|
|
func WithExecuteScriptAttributeKVs(kvs ...string) ExecuteScriptOption {
|
|
return func(o *executeScriptOptions) error {
|
|
if len(kvs)%2 != 0 {
|
|
return errors.New("WithExecuteScriptAttributeKVs option requires an even number of arguments")
|
|
}
|
|
attributes := make([]string, 0, len(kvs)/2)
|
|
for i := 0; i < len(kvs); i += 2 {
|
|
attribute := fmt.Sprintf("%s %s", kvs[i], kvs[i+1])
|
|
attributes = append(attributes, attribute)
|
|
}
|
|
return WithExecuteScriptAttributes(attributes...)(o)
|
|
}
|
|
}
|
|
|
|
// WithExecuteScriptAutoRemove requires the client to eliminate the script element after its execution.
|
|
func WithExecuteScriptAutoRemove(autoremove bool) ExecuteScriptOption {
|
|
return func(o *executeScriptOptions) error {
|
|
o.AutoRemove = &autoremove
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// ExecuteScript runs a script in the client browser. Seperate commands with semicolons.
|
|
func (sse *ServerSentEventGenerator) ExecuteScript(scriptContents string, opts ...ExecuteScriptOption) (err error) {
|
|
options := &executeScriptOptions{
|
|
RetryDuration: DefaultSseRetryDuration,
|
|
Attributes: []string{"type module"},
|
|
}
|
|
for _, opt := range opts {
|
|
if err = opt(options); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
sendOpts := make([]SSEEventOption, 0, 2)
|
|
if options.EventID != "" {
|
|
sendOpts = append(sendOpts, WithSSEEventId(options.EventID))
|
|
}
|
|
|
|
if options.RetryDuration != DefaultSseRetryDuration {
|
|
sendOpts = append(sendOpts, WithSSERetryDuration(options.RetryDuration))
|
|
}
|
|
|
|
dataLines := make([]string, 0, 64)
|
|
if options.AutoRemove != nil && *options.AutoRemove != DefaultExecuteScriptAutoRemove {
|
|
dataLines = append(dataLines, AutoRemoveDatalineLiteral+strconv.FormatBool(*options.AutoRemove))
|
|
}
|
|
dataLinesJoined := strings.Join(dataLines, NewLine)
|
|
|
|
if dataLinesJoined != DefaultExecuteScriptAttributes {
|
|
for _, attribute := range options.Attributes {
|
|
dataLines = append(dataLines, AttributesDatalineLiteral+attribute)
|
|
}
|
|
}
|
|
|
|
for line := range strings.SplitSeq(scriptContents, NewLine) {
|
|
dataLines = append(dataLines, ScriptDatalineLiteral+line)
|
|
}
|
|
|
|
return sse.Send(
|
|
EventTypeExecuteScript,
|
|
dataLines,
|
|
sendOpts...,
|
|
)
|
|
}
|