nu_plugin_protocol/plugin_custom_value/
mod.rs

1use std::cmp::Ordering;
2
3use nu_protocol::{CustomValue, ShellError, Span, Value, ast::Operator};
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    ) -> Result<Value, ShellError> {
67        panic!("follow_path_int() not available on plugin custom value without source");
68    }
69
70    fn follow_path_string(
71        &self,
72        _self_span: Span,
73        _column_name: String,
74        _path_span: Span,
75    ) -> Result<Value, ShellError> {
76        panic!("follow_path_string() not available on plugin custom value without source");
77    }
78
79    fn partial_cmp(&self, _other: &Value) -> Option<Ordering> {
80        panic!("partial_cmp() not available on plugin custom value without source");
81    }
82
83    fn operation(
84        &self,
85        _lhs_span: Span,
86        _operator: Operator,
87        _op_span: Span,
88        _right: &Value,
89    ) -> Result<Value, ShellError> {
90        panic!("operation() not available on plugin custom value without source");
91    }
92
93    fn as_any(&self) -> &dyn std::any::Any {
94        self
95    }
96
97    fn as_mut_any(&mut self) -> &mut dyn std::any::Any {
98        self
99    }
100}
101
102impl PluginCustomValue {
103    /// Create a new [`PluginCustomValue`].
104    pub fn new(name: String, data: Vec<u8>, notify_on_drop: bool) -> PluginCustomValue {
105        PluginCustomValue(SharedCow::new(SharedContent {
106            name,
107            data,
108            notify_on_drop,
109        }))
110    }
111
112    /// Create a [`Value`] containing this custom value.
113    pub fn into_value(self, span: Span) -> Value {
114        Value::custom(Box::new(self), span)
115    }
116
117    /// The name of the type of the custom value as defined by the plugin (`type_name()`)
118    pub fn name(&self) -> &str {
119        &self.0.name
120    }
121
122    /// The bincoded representation of the custom value on the plugin side
123    pub fn data(&self) -> &[u8] {
124        &self.0.data
125    }
126
127    /// True if the custom value should notify the source if all copies of it are dropped.
128    pub fn notify_on_drop(&self) -> bool {
129        self.0.notify_on_drop
130    }
131
132    /// Count the number of shared copies of this [`PluginCustomValue`].
133    pub fn ref_count(&self) -> usize {
134        SharedCow::ref_count(&self.0)
135    }
136
137    /// Serialize a custom value into a [`PluginCustomValue`]. This should only be done on the
138    /// plugin side.
139    pub fn serialize_from_custom_value(
140        custom_value: &dyn CustomValue,
141        span: Span,
142    ) -> Result<PluginCustomValue, ShellError> {
143        let name = custom_value.type_name();
144        let notify_on_drop = custom_value.notify_plugin_on_drop();
145        rmp_serde::to_vec(custom_value)
146            .map(|data| PluginCustomValue::new(name, data, notify_on_drop))
147            .map_err(|err| ShellError::CustomValueFailedToEncode {
148                msg: err.to_string(),
149                span,
150            })
151    }
152
153    /// Deserialize a [`PluginCustomValue`] into a `Box<dyn CustomValue>`. This should only be done
154    /// on the plugin side.
155    pub fn deserialize_to_custom_value(
156        &self,
157        span: Span,
158    ) -> Result<Box<dyn CustomValue>, ShellError> {
159        rmp_serde::from_slice::<Box<dyn CustomValue>>(self.data()).map_err(|err| {
160            ShellError::CustomValueFailedToDecode {
161                msg: err.to_string(),
162                span,
163            }
164        })
165    }
166    /// Convert all plugin-native custom values to [`PluginCustomValue`] within the given `value`,
167    /// recursively. This should only be done on the plugin side.
168    pub fn serialize_custom_values_in(value: &mut Value) -> Result<(), ShellError> {
169        value.recurse_mut(&mut |value| {
170            let span = value.span();
171            match value {
172                Value::Custom { val, .. } => {
173                    if val.as_any().downcast_ref::<PluginCustomValue>().is_some() {
174                        // Already a PluginCustomValue
175                        Ok(())
176                    } else {
177                        let serialized = Self::serialize_from_custom_value(&**val, span)?;
178                        *value = Value::custom(Box::new(serialized), span);
179                        Ok(())
180                    }
181                }
182                _ => Ok(()),
183            }
184        })
185    }
186
187    /// Convert all [`PluginCustomValue`]s to plugin-native custom values within the given `value`,
188    /// recursively. This should only be done on the plugin side.
189    pub fn deserialize_custom_values_in(value: &mut Value) -> Result<(), ShellError> {
190        value.recurse_mut(&mut |value| {
191            let span = value.span();
192            match value {
193                Value::Custom { val, .. } => {
194                    if let Some(val) = val.as_any().downcast_ref::<PluginCustomValue>() {
195                        let deserialized = val.deserialize_to_custom_value(span)?;
196                        *value = Value::custom(deserialized, span);
197                        Ok(())
198                    } else {
199                        // Already not a PluginCustomValue
200                        Ok(())
201                    }
202                }
203                _ => Ok(()),
204            }
205        })
206    }
207
208    /// Render any custom values in the `Value` using `to_base_value()`
209    pub fn render_to_base_value_in(value: &mut Value) -> Result<(), ShellError> {
210        value.recurse_mut(&mut |value| {
211            let span = value.span();
212            match value {
213                Value::Custom { val, .. } => {
214                    *value = val.to_base_value(span)?;
215                    Ok(())
216                }
217                _ => Ok(()),
218            }
219        })
220    }
221}