You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -113,46 +118,56 @@ First off, let's look at main:
113
118
114
119
```rust
115
120
fnmain() {
116
-
serve_plugin(&Len::new(), JsonSerializer)
121
+
serve_plugin(&LenPlugin, JsonSerializer)
117
122
}
118
123
```
119
124
120
-
In main, we just call a single function `serve_plugin`. This will do the work of calling into our plugin, handling the JSON serialization/deserialization, and sending values and errors back to Nu for us. To start it up, we pass it something that implements the `Plugin` trait and something that implements the `PluginEncoder` trait. We're given a choice of serialization formats that Nu supports. Ordinarily plugins written in Rust should use `MsgPackSerializer`, but here we select JSON to demonstrate how the communication protocol works further on in this tutorial.
125
+
In `main()`, we just call a single function `serve_plugin`. This will do the work of calling into our plugin, handling the JSON serialization/deserialization, and sending values and errors back to Nu for us. To start it up, we pass it something that implements the `Plugin` trait and something that implements the `PluginEncoder` trait. We're given a choice of serialization formats that Nu supports. Ordinarily plugins written in Rust should use `MsgPackSerializer` as it is considerably faster, but here we select JSON to demonstrate how the communication protocol works further on in this tutorial.
121
126
122
-
Next, above main, is this implementation of the `Plugin` trait for our particular plugin. Here, we'll implement the Plugin trait for our type, Len, which we'll see more of soon. Let's take a look at how we implement this trait:
127
+
Above `main()` is the implementation of the `SimplePluginCommand` trait for the `len` command that our plugin will expose, which is represented by the `Len` type. We use `SimplePluginCommand` rather than `PluginCommand` in order to simplify our implementation and avoid [handling streams](#using-streams-in-plugins). Let's take a look at how we implement this trait:
123
128
124
129
```rust
125
-
implPluginforLen {
126
-
fnsignature(&self) ->Vec<PluginSignature> {
127
-
vec![PluginSignature::build("len")
130
+
implSimplePluginCommandforLen {
131
+
typePlugin=LenPlugin;
132
+
133
+
// ...
134
+
}
135
+
```
136
+
137
+
We first specify the plugin type our command expects. This allows us to receive a reference to it in `run()`, which we can use for shared state between commands.
138
+
139
+
```rust
140
+
implSimplePluginCommandforLen {
141
+
// ...
142
+
143
+
fnsignature(&self) ->PluginSignature {
144
+
PluginSignature::build("len")
128
145
.usage("calculates the length of its input")
129
146
.input_type(Type::String)
130
147
.output_type(Type::Int)
131
-
]
132
148
}
133
149
134
150
// ...
135
151
}
136
152
```
137
153
138
-
There are two methods required for this implementation. The first is the `signature` part, which is run by Nu when it first starts up. This tells Nu the basic information about the plugin: its name, the parameters it takes, the description, what kind of plugin it is, and defines the input and output types.
154
+
There are two methods required for this implementation. The first is the `signature` part, which is run by Nu when the plugin is registered. This tells Nu the basic information about the command: its name, the parameters it takes, the description, what kind of plugin it is, and defines the input and output types.
155
+
139
156
Here, we tell Nu that the name is "len", give it a basic description for `help` to display and declare that we expect to be passed a string and will return an integer.
140
157
141
-
Next, in the `run` implementation, we describe how to do work as values flow into this plugin. Here, we receive a `Value` type that we expect to be a string.
142
-
We also return either `Value` or an error.
158
+
Next, in the `run` implementation, we describe how to do work as values flow into this plugin. Here, we receive a `Value` type that we expect to be a string. We also return either `Value` or an error.
143
159
144
160
```rust
145
-
implPluginforLen {
161
+
implSimplePluginCommandforLen {
146
162
// ...
147
163
148
164
fnrun(
149
165
&self,
150
-
name:&str,
166
+
_plugin:&LenPlugin,
151
167
_engine:&EngineInterface,
152
168
call:&EvaluatedCall,
153
169
input:&Value,
154
170
) ->Result<Value, LabeledError> {
155
-
assert_eq!(name, "len");
156
171
letspan=input.span();
157
172
matchinput {
158
173
Value::String { val, .. } =>Ok(
@@ -172,26 +187,35 @@ We use Rust's pattern matching to check the type of the `Value` coming in, and t
172
187
173
188
Our `Len` command doesn't require any parameters, but if it did we'd get them from the `EvaluatedCall`.
174
189
175
-
Next, let's look at `Len` itself to see what it's doing:
176
-
177
190
```rust
178
191
structLen;
192
+
```
193
+
194
+
`Len` is defined as a unit struct, with no fields, and this is the most common type definition for a command in a plugin. However, you may choose to keep state here if you want to - every call of `len` shares the same reference.
179
195
180
-
implLen {
181
-
fnnew() ->Self {
182
-
Self
196
+
Above that, let's have a look at the definition of `LenPlugin`, which implements the `Plugin` trait:
We create a very simple `Len`, in fact, it has no structure at all. Instead, it's just a placeholder that will let us implement the plugin.
210
+
Again, we use a unit struct for `LenPlugin`, but this is the recommended place to put plugin state if needed. All commands also get a reference to the plugin type. This is what we eventually pass to `serve_plugin()` in `main()`.
188
211
189
-
The `new`method is optional, it's just a convenient way to create a new value of the `Len` type to pass into `serve_plugin` later.
212
+
The only required method in `Plugin`is `commands()`, which initializes the plugin's commands. A boxed `dyn` reference is used so that we can keep all of the different command types in the single list. Dispatch by command name is automatically handled in `serve_plugin()` by looking at the name defined in the signature - in our case, that's `len`. A plugin can contain many commands, so if you end up adding more, just add them to the list returned by `commands()`.
The default `Plugin` trait that we just implemented for our plugin does not support streaming input or output. If we want to extend our plugin to support determining the lengths of lists, it would be helpful to not have to consume an entire list that is a stream. We can do this by implementing `StreamingPlugin` instead.
253
+
The `SimplePluginCommand` trait that we just implemented for our plugin does not support streaming input or output. If we want to extend our plugin to support determining the lengths of lists, it would be helpful to not have to consume an entire list that is a stream. We can do this by implementing `PluginCommand` instead.
230
254
231
255
```rust
232
256
// add these imports:
233
-
usenu_plugin::StreamingPlugin;
234
257
usenu_protocol::{IntoPipelineData, PipelineData};
235
258
// ...
236
259
237
-
// change Plugin to StreamingPlugin:
238
-
implStreamingPluginforLen {
239
-
fnsignature(&self) ->Vec<PluginSignature> {
260
+
// change SimplePluginCommand to PluginCommand:
261
+
implPluginCommandforLen {
262
+
typePlugin=LenPlugin;
263
+
264
+
fnsignature(&self) ->PluginSignature {
240
265
// ... add the list type to the signature
241
-
vec![PluginSignature::build("len")
266
+
PluginSignature::build("len")
242
267
.usage("calculates the length of its input")
243
268
.input_output_types(vec![
244
269
(Type::String, Type::Int),
245
270
(Type::List(Type::Any.into()), Type::Int),
246
271
])
247
-
]
248
272
}
249
273
250
274
// ... and change input and output types to PipelineData
251
275
fnrun(
252
276
&self,
253
-
name:&str,
277
+
_plugin:&LenPlugin,
254
278
_engine:&EngineInterface,
255
279
call:&EvaluatedCall,
256
280
input:PipelineData,
257
281
) ->Result<PipelineData, LabeledError> {
258
-
assert_eq!(name, "len");
259
282
// Check if the input is a stream or list
260
283
matchinput {
261
284
PipelineData::ListStream(..) |
@@ -341,20 +364,30 @@ The plugin configuration can be retrieved with [`EngineInterface::get_plugin_con
0 commit comments