nu

package module
v0.0.0-...-fb0eaf7 Latest Latest
Warning

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

Go to latest
Published: Jan 16, 2025 License: MIT Imports: 21 Imported by: 2

README

Go Reference

Nushell Plugin

The aim of this package is to make it simple to create Nushell Plugins using Go.

Status

WIP. Good enough to write simple plugins. See example project which implements commands to convert to/from plist and encode/decode base85.

Nushell protocol 0.101.0. Only message pack encoding is supported.

Unsupported Engine Calls
  • GetConfig
Unsupported Plugin Calls
  • CustomValueOp
Unsupported Values
  • Range (partially, Int ranges are supported, Float ranges are not)
  • CellPath
  • Custom

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrDeclNotFound = errors.New("command not found")

ErrDeclNotFound is returned by the ExecCommand.FindDeclaration method if the command with the given name couldn't be found in the scope of the plugin call.

View Source
var ErrDropStream = errors.New("received Drop stream message")

ErrDropStream is context cancellation (command's OnRun handler) or stream close error when consumer sent Drop message (ie plugin should stop producing into output stream).

View Source
var ErrGoodbye = errors.New("Goodbye")

ErrGoodbye is the exit cause when plugin received Goodbye message.

View Source
var ErrInterrupt = errors.New("received Interrupt signal")

ErrInterrupt is the exit cause when plugin received Interrupt signal.

Functions

This section is empty.

Types

type Block

type Block uint64

Block is Nushell Block Value type.

type Closure

type Closure struct {
	BlockID  uint               `msgpack:"block_id"`
	Captures msgpack.RawMessage `msgpack:"captures"`
}

Closure Value is a reference to a parsed block of Nushell code, with variables captured from scope.

The plugin should not try to inspect the contents of the closure. It is recommended that this is only used as an argument to the ExecCommand.EvalClosure engine call.

type Command

type Command struct {
	Signature PluginSignature `msgpack:"sig"`
	Examples  Examples        `msgpack:"examples"`

	// callback executed on command invocation
	OnRun func(context.Context, *ExecCommand) error `msgpack:"-"`
}

Command describes an command provided by the plugin.

func (Command) Validate

func (c Command) Validate() error

type Config

type Config struct {

	// Logger the Plugin should use. If not provided the plugin will create
	// Error level logger which logs to stderr.
	Logger *slog.Logger

	// if assigned incoming data is also copied to this writer.
	// NB! this writer must not block!
	SniffIn io.Writer

	// if assigned outgoing data is also copied to this writer.
	// NB! this writer must not block!
	SniffOut io.Writer
}

Config is Plugin's configuration, mostly meant to allow debugging.

type Declaration

type Declaration struct {
	// contains filtered or unexported fields
}

Declaration represents Nu command which can be called from plugin. Use ExecCommand.FindDeclaration to obtain the Declaration.

func (Declaration) Call

func (d Declaration) Call(ctx context.Context, args ...EvalArgument) (any, error)

Call implements CallDecl engine call. Use ExecCommand.FindDeclaration to obtain the Declaration.

Note that NamedParams can be used as argument of Call in addition to the Positional, InputValue and other [EvalArgument]s.

Example
// command's OnRun handler
_ = func(ctx context.Context, call *nu.ExecCommand) error {
	// find the builtin "into int" command (Conversions)
	dec, err := call.FindDeclaration(ctx, "into int")
	if err != nil {
		return err
	}
	// following call is the same as executing
	// 'FF' | into int --radix 16
	response, err := dec.Call(ctx, nu.InputValue(nu.Value{Value: "FF"}), nu.NamedParams{"radix": nu.Value{Value: 16}})
	if err != nil {
		return err
	}
	switch data := response.(type) {
	case nu.Value:
		return call.ReturnValue(ctx, data)
	default:
		return fmt.Errorf("unsupported return type %#v", response)
	}
}
Output:

type ErrorLabel

type ErrorLabel struct {
	Text string `msgpack:"text"`
	Span Span   `msgpack:"span"`
}

ErrorLabel is "label" type for LabeledError.

type EvalArgument

type EvalArgument interface {
	// contains filtered or unexported methods
}

EvalArgument is type for ExecCommand.EvalClosure and Declaration.Call optional arguments.

func InputListStream

func InputListStream(arg <-chan Value) EvalArgument
Example

example of a command which sends list stream as a input to closure

_ = &nu.Command{
	Signature: nu.PluginSignature{
		Name: "demo",
		RequiredPositional: nu.PositionalArgs{
			nu.PositionalArg{
				Name:  "closure",
				Desc:  "Closure to be evaluated",
				Shape: syntaxshape.Closure(),
			},
		},
	},
	Examples: nu.Examples{
		{Description: `Closure which adds +1 to each item in input stream and returns stream`, Example: `demo { $in | each {|n| $n + 1} }`},
	},

	OnRun: func(ctx context.Context, call *nu.ExecCommand) error {
		// EvalClosure will block until the closure returns something so generate the
		// input stream in goroutine
		closureIn := make(chan nu.Value)
		go func() {
			defer close(closureIn)
			for v := range 10 {
				closureIn <- nu.Value{Value: v}
			}
		}()

		closureOut, err := call.EvalClosure(ctx, call.Positional[0], nu.InputListStream(closureIn))
		if err != nil {
			return fmt.Errorf("evaluating closure: %w", err)
		}
		switch data := closureOut.(type) {
		case <-chan nu.Value:
			out, err := call.ReturnListStream(ctx)
			if err != nil {
				return fmt.Errorf("opening output stream: %w", err)
			}
			for v := range data {
				out <- v
			}
			close(out)
		default:
			return fmt.Errorf("unexpected closure output type %T", data)
		}
		return nil
	},
}
Output:

func InputRawStream

func InputRawStream(arg io.Reader) EvalArgument

func InputValue

func InputValue(arg Value) EvalArgument

InputValue allows to set single-value input for the call.

func Positional

func Positional(args ...Value) EvalArgument

Positional arguments for the call.

func RedirectStderr

func RedirectStderr() EvalArgument

Whether to redirect stderr if the declared command ends in an external command.

Default is "false", this argument sets it to "true".

func RedirectStdout

func RedirectStdout() EvalArgument

Whether to redirect stdout if the declared command ends in an external command.

Default is "false", this argument sets it to "true".

type Example

type Example struct {
	Example     string `msgpack:"example"`
	Description string `msgpack:"description"`
	Result      *Value `msgpack:"result,omitempty"`
}

type Examples

type Examples []Example

func (*Examples) EncodeMsgpack

func (ex *Examples) EncodeMsgpack(enc *msgpack.Encoder) error

type ExecCommand

type ExecCommand struct {
	Name string

	// Span of the command invocation
	Head Span
	// Values of positional arguments
	Positional []Value
	// Names and values of named arguments
	Named NamedParams

	/*
		Input to the command. Is one of:

		- nil: no input;
		- Value: single value input;
		- <-chan Value: stream of Values;
		- io.ReadCloser: raw stream;
	*/
	Input any
	// contains filtered or unexported fields
}

ExecCommand is passed as argument to the plugin command's OnRun handler.

It allows to make engine calls, access command's input (see Input, Named and Positional fields) and send response (see Return* methods).

func (*ExecCommand) AddEnvVar

func (ec *ExecCommand) AddEnvVar(ctx context.Context, name string, value Value) error

AddEnvVar engine call.

Set an environment variable in the caller's scope. The environment variable can only be propagated to the caller's scope if called before the plugin call response is sent.

func (*ExecCommand) EnterForeground

func (ec *ExecCommand) EnterForeground(ctx context.Context) error

EnterForeground engine call.

Moves the plugin to the foreground group for direct terminal access, in an operating system-defined manner. This should be called when the plugin is going to drive the terminal in raw mode, for example to implement a terminal UI.

This call will fail with an error if the plugin is already in the foreground. The plugin should call LeaveForeground when it no longer needs to be in the foreground.

func (*ExecCommand) EvalClosure

func (ec *ExecCommand) EvalClosure(ctx context.Context, closure Value, args ...EvalArgument) (any, error)

EvalClosure implements EvalClosure engine call.

Pass a Closure and optional arguments to the engine to be evaluated. Returned value follows the same rules as Input field of the ExecCommand (ie it could be nil, Value or stream).

func (*ExecCommand) FindDeclaration

func (ec *ExecCommand) FindDeclaration(ctx context.Context, name string) (*Declaration, error)

FindDeclaration implements FindDecl engine call.

Returns ErrDeclNotFound if the command with the given name couldn't be found in the scope of the plugin call (NB! use errors.Is to check for the error as it might be wrapped into more descriptive error).

In case of success the returned Declaration can be used to call the command.

func (*ExecCommand) FlagValue

func (ec *ExecCommand) FlagValue(name string) (Value, bool)

FlagValue returns value of named parameter/flag.

The returned bool flag indicates was the flag set by user (true) or not (false). When flag was not set by user the default value (defined in plugin signature) is returned. When signature doesn't define default value (or flag is not defined in the signature) then zero Value and false is returned.

For toggle flags (Shape is not assigned in the flag definition) Bool Value is always returned ie if user doesn't provide the flag or "--flagName=false" is used then Value==false is returned.

func (*ExecCommand) GetCurrentDir

func (ec *ExecCommand) GetCurrentDir(ctx context.Context) (string, error)

GetCurrentDir engine call.

Get the current directory path in the caller's scope. This always returns an absolute path.

func (*ExecCommand) GetEnvVar

func (ec *ExecCommand) GetEnvVar(ctx context.Context, name string) (*Value, error)

GetEnvVar engine call.

Get an environment variable from the caller's scope, returns nil if the environment variable is not present.

func (*ExecCommand) GetEnvVars

func (ec *ExecCommand) GetEnvVars(ctx context.Context) (map[string]Value, error)

GetEnvVars engine call.

Get all environment variables from the caller's scope.

func (*ExecCommand) GetHelp

func (ec *ExecCommand) GetHelp(ctx context.Context) (string, error)

GetHelp engine call.

Get fully formatted help text for the current command. This can help with implementing top-level commands that just list their subcommands, rather than implementing any specific functionality.

func (*ExecCommand) GetPluginConfig

func (ec *ExecCommand) GetPluginConfig(ctx context.Context) (*Value, error)

GetPluginConfig engine call.

Get the configuration for the plugin, from its section in $env.config.plugins.NAME if present. Returns nil if there is no configuration for the plugin set.

If the plugin configuration was specified as a closure, the engine will evaluate that closure and return the result, which may cause an error response.

func (*ExecCommand) GetSpanContents

func (ec *ExecCommand) GetSpanContents(ctx context.Context, span Span) ([]byte, error)

GetSpanContents engine call.

Get the contents of a Span from the engine. This can be used for viewing the source code that generated a value.

func (*ExecCommand) LeaveForeground

func (ec *ExecCommand) LeaveForeground(ctx context.Context) error

LeaveForeground engine call - resets the state set by EnterForeground.

func (*ExecCommand) ReturnListStream

func (ec *ExecCommand) ReturnListStream(ctx context.Context) (chan<- Value, error)

ReturnListStream should be used when command returns multiple nu.Values.

When one of the values is [error] engine considers the plugin call to have been failed and prints that error message.

To signal the end of data chan must be closed (even when sending error)!

func (*ExecCommand) ReturnRawStream

func (ec *ExecCommand) ReturnRawStream(ctx context.Context, opts ...RawStreamOption) (io.WriteCloser, error)

ReturnRawStream should be used when command returns raw stream.

To signal the end of data Writer must be closed.

Cancelling the context (ctx) will also "stop" the output stream, ie it signals that the plugin is about to quit and all work has to be abandoned.

func (*ExecCommand) ReturnValue

func (ec *ExecCommand) ReturnValue(ctx context.Context, v Value) error

ReturnValue should be used when command returns single Value.

type Filesize

type Filesize int64

Filesize is Nushell Filesize Value type.

type Flag

type Flag struct {
	Long     string                  `msgpack:"long"`
	Short    string                  `msgpack:"short,omitempty"` // must be single character!
	Shape    syntaxshape.SyntaxShape `msgpack:"arg,omitempty"`
	Required bool                    `msgpack:"required"`
	Desc     string                  `msgpack:"desc"`
	VarId    uint                    `msgpack:"var_id,omitempty"`
	Default  *Value                  `msgpack:"default_value,omitempty"`
}

Flag is a definition of a flag (Shape is unassigned) or named argument (Shape assigned).

type Flags

type Flags []Flag

func (*Flags) EncodeMsgpack

func (flags *Flags) EncodeMsgpack(enc *msgpack.Encoder) error

func (*Flags) Validate

func (flags *Flags) Validate() error

type Glob

type Glob struct {
	Value string
	// If true, the expansion of wildcards is disabled and Value should be treated
	// as a literal path.
	NoExpand bool
}

Glob is Nushell Glob Value type - a filesystem glob, selecting multiple files or directories depending on the expansion of wildcards.

Note that Go stdlib glob implementation doesn't support doublestar / globstar pattern but thirdparty libraries which do exist.

type InOutTypes

type InOutTypes struct {
	In  types.Type
	Out types.Type
}

func (*InOutTypes) EncodeMsgpack

func (iot *InOutTypes) EncodeMsgpack(enc *msgpack.Encoder) error

type IntRange

type IntRange struct {
	Start int64
	Step  int64
	End   int64
	Bound RangeBound // end bound kind of the range
}

IntRange is the IntRange variant of Nushell Range type.

When creating IntRange manually don't forget to assign Step as range with zero stride would be invalid.

Bound defaults to "included" which is also default in Nushell.

To iterate over values in the range use IntRange.All method.

Example
var values []int64
// end bound defaults to Included
rng := IntRange{Start: -1, Step: 2, End: 5}
for v := range rng.All() {
	values = append(values, v)
}
fmt.Printf("Included: %v\n", values)

// exclude end bound
rng.Bound = Excluded
values = slices.Collect(rng.All())
fmt.Printf("Excluded: %v\n", values)
Output:

Included: [-1 1 3 5]
Excluded: [-1 1 3]

func (IntRange) All

func (v IntRange) All() iter.Seq[int64]

All generates all the values in the Range.

Invalid range doesn't generate any values.

func (*IntRange) DecodeMsgpack

func (v *IntRange) DecodeMsgpack(dec *msgpack.Decoder) error

func (*IntRange) EncodeMsgpack

func (v *IntRange) EncodeMsgpack(enc *msgpack.Encoder) error

func (*IntRange) String

func (v *IntRange) String() string

func (IntRange) Validate

func (v IntRange) Validate() error

type LabeledError

type LabeledError struct {
	Msg    string         `msgpack:"msg"`
	Labels []ErrorLabel   `msgpack:"labels,omitempty"`
	Code   string         `msgpack:"code,omitempty"`
	Url    string         `msgpack:"url,omitempty"`
	Help   string         `msgpack:"help,omitempty"`
	Inner  []LabeledError `msgpack:"inner,omitempty"`
}

func AsLabeledError

func AsLabeledError(err error) *LabeledError

AsLabeledError "converts" error to LabeledError - if the error is already LabeledError it will be "unwrapped", otherwise new LabeledError will be created wrapping the err.

func (*LabeledError) Error

func (le *LabeledError) Error() string

Error implements Go "error" interface.

The "Msg" is returned as error message.

type NamedParams

type NamedParams map[string]Value

func (*NamedParams) DecodeMsgpack

func (np *NamedParams) DecodeMsgpack(dec *msgpack.Decoder) error

func (*NamedParams) EncodeMsgpack

func (np *NamedParams) EncodeMsgpack(enc *msgpack.Encoder) error

type Plugin

type Plugin struct {
	// contains filtered or unexported fields
}

Plugin implements Nushell Plugin, single Plugin can implement multiple commands.

The zero value is not usable, the New constructor must be used to create Plugin.

func New

func New(cmd []*Command, version string, cfg *Config) (_ *Plugin, err error)

New creates new Nushell Plugin with given commands.

version: The version of the plugin. SemVer is recommended, but not required.

The cfg may be nil, in that case default configuration will be used.

func (*Plugin) Run

func (p *Plugin) Run(ctx context.Context) error

Run starts the plugin. It is blocking until Plugin exits (ie because plugin engine sent Goodbye message, the ctx was cancelled or unrecoverable error happened).

type PluginSignature

type PluginSignature struct {
	Name string `msgpack:"name"`
	// This should be a single sentence as it is the part shown for example in the completion menu.
	Desc string `msgpack:"description"`
	// Additional documentation of the command.
	Description        string         `msgpack:"extra_description"`
	SearchTerms        []string       `msgpack:"search_terms"`
	Category           string         `msgpack:"category"` // https://docs.rs/nu-protocol/latest/nu_protocol/enum.Category.html
	RequiredPositional PositionalArgs `msgpack:"required_positional"`
	OptionalPositional PositionalArgs `msgpack:"optional_positional,"`
	RestPositional     *PositionalArg `msgpack:"rest_positional,omitempty"`

	// The "help" (short "h") flag will be added automatically when plugin
	// is created, do not use these names for other flags or arguments.
	Named                Flags        `msgpack:"named"`
	InputOutputTypes     []InOutTypes `msgpack:"input_output_types"`
	IsFilter             bool         `msgpack:"is_filter"`
	CreatesScope         bool         `msgpack:"creates_scope"`
	AllowsUnknownArgs    bool         `msgpack:"allows_unknown_args"`
	AllowMissingExamples bool         `msgpack:"allow_variants_without_examples"`
}

func (PluginSignature) Validate

func (sig PluginSignature) Validate() error

type PositionalArg

type PositionalArg struct {
	Name    string                  `msgpack:"name"`
	Desc    string                  `msgpack:"desc"`
	Shape   syntaxshape.SyntaxShape `msgpack:"shape"`
	VarId   uint                    `msgpack:"var_id,omitempty"`
	Default *Value                  `msgpack:"default_value,omitempty"`
}

type PositionalArgs

type PositionalArgs []PositionalArg

func (*PositionalArgs) EncodeMsgpack

func (pa *PositionalArgs) EncodeMsgpack(enc *msgpack.Encoder) error

type RangeBound

type RangeBound uint8
const (
	Included  RangeBound = 0
	Excluded  RangeBound = 1
	Unbounded RangeBound = 2
)

func (RangeBound) String

func (rb RangeBound) String() string

type RawStreamOption

type RawStreamOption interface {
	// contains filtered or unexported methods
}

func BinaryStream

func BinaryStream() RawStreamOption

BinaryStream indicates that the stream contains binary data of unknown encoding, and should be treated as a binary value. See also StringStream.

func BufferSize

func BufferSize(size uint) RawStreamOption

BufferSize allows to hint the desired buffer size (but it is not guaranteed that buffer will be exactly that big). Writes are collected into buffer before sending to the consumer.

func FilePath

func FilePath(fileName string) RawStreamOption

FilePath sets the stream metadata to "DataSource = FilePath" with given file name. The "content type" field of the metadata is set based on the file's extension using system mime type registry.

func StringStream

func StringStream() RawStreamOption

StringStream indicates that the stream contains text data that is valid UTF-8, and should be treated as a string value. See also BinaryStream.

type Record

type Record map[string]Value

type Span

type Span struct {
	Start int `msgpack:"start"`
	End   int `msgpack:"end"`
}

type Value

type Value struct {
	Value any
	Span  Span
}

Value represents Nushell Value.

Generally type switch or type assertion has to be used to access the "underling value", ie

switch data := in.Value.(type) {
case []byte:
	buf = data
case string:
	buf = []byte(data)
default:
	return fmt.Errorf("unsupported Value type %T", data)
}

Incoming data is encoded as follows:

Outgoing values are encoded as:

  • nil -> Nothing
  • int, int8, int16, int32, int64 -> Int
  • uint, uint8, uint16, uint32, uint64 -> Int
  • float64, float32 -> Float
  • bool -> Bool
  • []byte -> Binary
  • string -> String
  • Filesize -> Filesize
  • time.Duration -> Duration
  • time.Time -> Date
  • Record -> Record
  • []Value -> List
  • Glob -> Glob
  • Closure -> Closure
  • Block -> Block
  • IntRange -> Range
  • error -> LabeledError

func (*Value) DecodeMsgpack

func (v *Value) DecodeMsgpack(dec *msgpack.Decoder) error

func (*Value) EncodeMsgpack

func (v *Value) EncodeMsgpack(enc *msgpack.Encoder) error

Directories

Path Synopsis
Package syntaxshape defines type and functions to describe Nu syntax shapes.
Package syntaxshape defines type and functions to describe Nu syntax shapes.

Jump to

Keyboard shortcuts

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