nu_plugin_protocol/plugin_custom_value/
mod.rs

1use std::{cmp::Ordering, path::Path};
2
3use nu_protocol::{CustomValue, ShellError, Span, Spanned, Value, ast::Operator, casing::Casing};
4use nu_utils::SharedCow;
5
6use serde::{Deserialize, Serialize};
7
8#[cfg(test)]
9mod tests;
10
11/// An opaque container for a custom value that is handled fully by a plugin.
12///
13/// This is the only type of custom value that is allowed to cross the plugin serialization
14/// boundary.
15///
16/// The plugin is responsible for ensuring that local plugin custom values are converted to and from
17/// [`PluginCustomValue`] on the boundary.
18///
19/// The engine is responsible for adding tracking the source of the custom value, ensuring that only
20/// [`PluginCustomValue`] is contained within any values sent, and that the source of any values
21/// sent matches the plugin it is being sent to.
22///
23/// Most of the [`CustomValue`] methods on this type will result in a panic. The source must be
24/// added (see `nu_plugin_engine::PluginCustomValueWithSource`) in order to implement the
25/// functionality via plugin calls.
26#[derive(Clone, Debug, Serialize, Deserialize)]
27pub struct PluginCustomValue(SharedCow<SharedContent>);
28
29/// Content shared across copies of a plugin custom value.
30#[derive(Clone, Debug, Serialize, Deserialize)]
31struct SharedContent {
32    /// The name of the type of the custom value as defined by the plugin (`type_name()`)
33    name: String,
34    /// The bincoded representation of the custom value on the plugin side
35    data: Vec<u8>,
36    /// True if the custom value should notify the source if all copies of it are dropped.
37    ///
38    /// This is not serialized if `false`, since most custom values don't need it.
39    #[serde(default, skip_serializing_if = "is_false")]
40    notify_on_drop: bool,
41}
42
43fn is_false(b: &bool) -> bool {
44    !b
45}
46
47#[typetag::serde]
48impl CustomValue for PluginCustomValue {
49    fn clone_value(&self, span: Span) -> Value {
50        self.clone().into_value(span)
51    }
52
53    fn type_name(&self) -> String {
54        self.name().to_owned()
55    }
56
57    fn to_base_value(&self, _span: Span) -> Result<Value, ShellError> {
58        panic!("to_base_value() not available on plugin custom value without source");
59    }
60
61    fn follow_path_int(
62        &self,
63        _self_span: Span,
64        _index: usize,
65        _path_span: Span,
66        _optional: bool,
67    ) -> Result<Value, ShellError> {
68        panic!("follow_path_int() not available on plugin custom value without source");
69    }
70
71    fn follow_path_string(
72        &self,
73        _self_span: Span,
74        _column_name: String,
75        _path_span: Span,
76        _optional: bool,
77        _casing: Casing,
78    ) -> Result<Value, ShellError> {
79        panic!("follow_path_string() not available on plugin custom value without source");
80    }
81
82    fn partial_cmp(&self, _other: &Value) -> Option<Ordering> {
83        panic!("partial_cmp() not available on plugin custom value without source");
84    }
85
86    fn operation(
87        &self,
88        _lhs_span: Span,
89        _operator: Operator,
90        _op_span: Span,
91        _right: &Value,
92    ) -> Result<Value, ShellError> {
93        panic!("operation() not available on plugin custom value without source");
94    }
95
96    fn save(
97        &self,
98        _path: Spanned<&Path>,
99        _value_span: Span,
100        _save_span: Span,
101    ) -> Result<(), ShellError> {
102        panic!("save() not available on plugin custom value without source");
103    }
104
105    fn as_any(&self) -> &dyn std::any::Any {
106        self
107    }
108
109    fn as_mut_any(&mut self) -> &mut dyn std::any::Any {
110        self
111    }
112}
113
114impl PluginCustomValue {
115    /// Create a new [`PluginCustomValue`].
116    pub fn new(name: String, data: Vec<u8>, notify_on_drop: bool) -> PluginCustomValue {
117        PluginCustomValue(SharedCow::new(SharedContent {
118            name,
119            data,
120            notify_on_drop,
121        }))
122    }
123
124    /// Create a [`Value`] containing this custom value.
125    pub fn into_value(self, span: Span) -> Value {
126        Value::custom(Box::new(self), span)
127    }
128
129    /// The name of the type of the custom value as defined by the plugin (`type_name()`)
130    pub fn name(&self) -> &str {
131        &self.0.name
132    }
133
134    /// The bincoded representation of the custom value on the plugin side
135    pub fn data(&self) -> &[u8] {
136        &self.0.data
137    }
138
139    /// True if the custom value should notify the source if all copies of it are dropped.
140    pub fn notify_on_drop(&self) -> bool {
141        self.0.notify_on_drop
142    }
143
144    /// Count the number of shared copies of this [`PluginCustomValue`].
145    pub fn ref_count(&self) -> usize {
146        SharedCow::ref_count(&self.0)
147    }
148
149    /// Serialize a custom value into a [`PluginCustomValue`]. This should only be done on the
150    /// plugin side.
151    pub fn serialize_from_custom_value(
152        custom_value: &dyn CustomValue,
153        span: Span,
154    ) -> Result<PluginCustomValue, ShellError> {
155        let name = custom_value.type_name();
156        let notify_on_drop = custom_value.notify_plugin_on_drop();
157        rmp_serde::to_vec(custom_value)
158            .map(|data| PluginCustomValue::new(name, data, notify_on_drop))
159            .map_err(|err| ShellError::CustomValueFailedToEncode {
160                msg: err.to_string(),
161                span,
162            })
163    }
164
165    /// Deserialize a [`PluginCustomValue`] into a `Box<dyn CustomValue>`. This should only be done
166    /// on the plugin side.
167    pub fn deserialize_to_custom_value(
168        &self,
169        span: Span,
170    ) -> Result<Box<dyn CustomValue>, ShellError> {
171        rmp_serde::from_slice::<Box<dyn CustomValue>>(self.data()).map_err(|err| {
172            ShellError::CustomValueFailedToDecode {
173                msg: err.to_string(),
174                span,
175            }
176        })
177    }
178    /// Convert all plugin-native custom values to [`PluginCustomValue`] within the given `value`,
179    /// recursively. This should only be done on the plugin side.
180    pub fn serialize_custom_values_in(value: &mut Value) -> Result<(), ShellError> {
181        value.recurse_mut(&mut |value| {
182            let span = value.span();
183            match value {
184                Value::Custom { val, .. } => {
185                    if val.as_any().downcast_ref::<PluginCustomValue>().is_some() {
186                        // Already a PluginCustomValue
187                        Ok(())
188                    } else {
189                        let serialized = Self::serialize_from_custom_value(&**val, span)?;
190                        *value = Value::custom(Box::new(serialized), span);
191                        Ok(())
192                    }
193                }
194                _ => Ok(()),
195            }
196        })
197    }
198
199    /// Convert all [`PluginCustomValue`]s to plugin-native custom values within the given `value`,
200    /// recursively. This should only be done on the plugin side.
201    pub fn deserialize_custom_values_in(value: &mut Value) -> Result<(), ShellError> {
202        value.recurse_mut(&mut |value| {
203            let span = value.span();
204            match value {
205                Value::Custom { val, .. } => {
206                    if let Some(val) = val.as_any().downcast_ref::<PluginCustomValue>() {
207                        let deserialized = val.deserialize_to_custom_value(span)?;
208                        *value = Value::custom(deserialized, span);
209                        Ok(())
210                    } else {
211                        // Already not a PluginCustomValue
212                        Ok(())
213                    }
214                }
215                _ => Ok(()),
216            }
217        })
218    }
219
220    /// Render any custom values in the `Value` using `to_base_value()`
221    pub fn render_to_base_value_in(value: &mut Value) -> Result<(), ShellError> {
222        value.recurse_mut(&mut |value| {
223            let span = value.span();
224            match value {
225                Value::Custom { val, .. } => {
226                    *value = val.to_base_value(span)?;
227                    Ok(())
228                }
229                _ => Ok(()),
230            }
231        })
232    }
233}