nu_plugin_engine/plugin_custom_value_with_source/
mod.rs1use std::{cmp::Ordering, path::Path, sync::Arc};
2
3use nu_plugin_core::util::with_custom_values_in;
4use nu_plugin_protocol::PluginCustomValue;
5use nu_protocol::{
6 CustomValue, IntoSpanned, ShellError, Span, Spanned, Value, ast::Operator, casing::Casing,
7 shell_error::generic::GenericError,
8};
9use serde::Serialize;
10
11use crate::{PluginInterface, PluginSource};
12
13#[cfg(test)]
14mod tests;
15
16#[derive(Debug, Clone)]
20pub struct PluginCustomValueWithSource {
21 inner: PluginCustomValue,
22
23 source: Arc<PluginSource>,
25}
26
27impl PluginCustomValueWithSource {
28 pub fn new(inner: PluginCustomValue, source: Arc<PluginSource>) -> PluginCustomValueWithSource {
30 PluginCustomValueWithSource { inner, source }
31 }
32
33 pub fn into_value(self, span: Span) -> Value {
35 Value::custom(Box::new(self), span)
36 }
37
38 pub fn source(&self) -> &Arc<PluginSource> {
41 &self.source
42 }
43
44 pub fn without_source(self) -> PluginCustomValue {
46 self.inner.clone()
48 }
49
50 fn get_plugin(&self, span: Option<Span>, for_op: &str) -> Result<PluginInterface, ShellError> {
52 let wrap_err = |err: ShellError| {
53 let error = if let Some(span) = span {
54 GenericError::new(
55 format!(
56 "Unable to spawn plugin `{}` to {for_op}",
57 self.source.name()
58 ),
59 err.to_string(),
60 span,
61 )
62 .with_inner([err])
63 } else {
64 GenericError::new_internal(
65 format!(
66 "Unable to spawn plugin `{}` to {for_op}",
67 self.source.name()
68 ),
69 err.to_string(),
70 )
71 .with_inner([err])
72 };
73 ShellError::Generic(error)
74 };
75
76 self.source
77 .clone()
78 .persistent(span)
79 .and_then(|p| p.get_plugin(None))
80 .map_err(wrap_err)
81 }
82
83 pub fn add_source(value: &mut Box<dyn CustomValue>, source: &Arc<PluginSource>) {
85 if let Some(custom_value) = value.as_any().downcast_ref::<PluginCustomValue>() {
86 *value = Box::new(custom_value.clone().with_source(source.clone()));
87 }
88 }
89
90 pub fn add_source_in(value: &mut Value, source: &Arc<PluginSource>) -> Result<(), ShellError> {
92 with_custom_values_in(value, |custom_value| {
93 Self::add_source(custom_value.item, source);
94 Ok::<_, ShellError>(())
95 })
96 }
97
98 pub fn remove_source(value: &mut Box<dyn CustomValue>) {
101 if let Some(custom_value) = value.as_any().downcast_ref::<PluginCustomValueWithSource>() {
102 *value = Box::new(custom_value.clone().without_source());
103 }
104 }
105
106 pub fn remove_source_in(value: &mut Value) -> Result<(), ShellError> {
108 with_custom_values_in(value, |custom_value| {
109 Self::remove_source(custom_value.item);
110 Ok::<_, ShellError>(())
111 })
112 }
113
114 pub fn verify_source(&self, span: Span, source: &PluginSource) -> Result<(), ShellError> {
116 if self.source.is_compatible(source) {
117 Ok(())
118 } else {
119 Err(ShellError::CustomValueIncorrectForPlugin {
120 name: self.name().to_owned(),
121 span,
122 dest_plugin: source.name().to_owned(),
123 src_plugin: Some(self.source.name().to_owned()),
124 })
125 }
126 }
127
128 pub fn verify_source_of_custom_value(
131 value: Spanned<&dyn CustomValue>,
132 source: &PluginSource,
133 ) -> Result<(), ShellError> {
134 if let Some(custom_value) = value
135 .item
136 .as_any()
137 .downcast_ref::<PluginCustomValueWithSource>()
138 {
139 custom_value.verify_source(value.span, source)
140 } else {
141 Err(ShellError::CustomValueIncorrectForPlugin {
143 name: value.item.type_name(),
144 span: value.span,
145 dest_plugin: source.name().to_owned(),
146 src_plugin: None,
147 })
148 }
149 }
150}
151
152impl std::ops::Deref for PluginCustomValueWithSource {
153 type Target = PluginCustomValue;
154
155 fn deref(&self) -> &PluginCustomValue {
156 &self.inner
157 }
158}
159
160impl Serialize for PluginCustomValueWithSource {
162 fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
163 where
164 S: serde::Serializer,
165 {
166 use serde::ser::Error;
167 Err(Error::custom(
168 "can't serialize PluginCustomValueWithSource, remove the source first",
169 ))
170 }
171}
172
173impl CustomValue for PluginCustomValueWithSource {
174 fn clone_value(&self, span: Span) -> Value {
175 self.clone().into_value(span)
176 }
177
178 fn type_name(&self) -> String {
179 self.name().to_owned()
180 }
181
182 fn to_base_value(&self, span: Span) -> Result<Value, ShellError> {
183 self.get_plugin(Some(span), "get base value")?
184 .custom_value_to_base_value(self.clone().into_spanned(span))
185 }
186
187 fn follow_path_int(
188 &self,
189 self_span: Span,
190 index: usize,
191 path_span: Span,
192 optional: bool,
193 ) -> Result<Value, ShellError> {
194 self.get_plugin(Some(self_span), "follow cell path")?
195 .custom_value_follow_path_int(
196 self.clone().into_spanned(self_span),
197 index.into_spanned(path_span),
198 optional,
199 )
200 }
201
202 fn follow_path_string(
203 &self,
204 self_span: Span,
205 column_name: String,
206 path_span: Span,
207 optional: bool,
208 casing: Casing,
209 ) -> Result<Value, ShellError> {
210 self.get_plugin(Some(self_span), "follow cell path")?
211 .custom_value_follow_path_string(
212 self.clone().into_spanned(self_span),
213 column_name.into_spanned(path_span),
214 optional,
215 casing,
216 )
217 }
218
219 fn partial_cmp(&self, other: &Value) -> Option<Ordering> {
220 self.get_plugin(Some(other.span()), "perform comparison")
221 .and_then(|plugin| {
222 plugin.custom_value_partial_cmp(self.clone(), other.clone(), other.span())
223 })
224 .unwrap_or_else(|err| {
225 log::warn!(
227 "Error in partial_cmp on plugin custom value (source={source:?}): {err}",
228 source = self.source
229 );
230 None
231 })
232 .map(|ordering| ordering.into())
233 }
234
235 fn operation(
236 &self,
237 lhs_span: Span,
238 operator: Operator,
239 op_span: Span,
240 right: &Value,
241 ) -> Result<Value, ShellError> {
242 self.get_plugin(Some(lhs_span), "invoke operator")?
243 .custom_value_operation(
244 self.clone().into_spanned(lhs_span),
245 operator.into_spanned(op_span),
246 right.clone(),
247 )
248 }
249
250 fn save(
251 &self,
252 path: Spanned<&Path>,
253 value_span: Span,
254 save_span: Span,
255 ) -> Result<(), ShellError> {
256 self.get_plugin(Some(value_span), "save")?
257 .custom_value_save(self.clone().into_spanned(value_span), path, save_span)
258 }
259
260 fn as_any(&self) -> &dyn std::any::Any {
261 self
262 }
263
264 fn as_mut_any(&mut self) -> &mut dyn std::any::Any {
265 self
266 }
267
268 #[doc(hidden)]
269 fn typetag_name(&self) -> &'static str {
270 "PluginCustomValueWithSource"
271 }
272
273 #[doc(hidden)]
274 fn typetag_deserialize(&self) {}
275}
276
277impl Drop for PluginCustomValueWithSource {
278 fn drop(&mut self) {
279 if self.notify_on_drop() && self.inner.ref_count() == 1 {
282 self.get_plugin(None, "drop")
283 .and_then(|plugin| plugin.custom_value_dropped(self.inner.clone()))
285 .unwrap_or_else(|err| {
286 let name = self.name();
288 log::warn!("Failed to notify drop of custom value ({name}): {err}")
289 });
290 }
291 }
292}
293
294pub trait WithSource {
296 fn with_source(self, source: Arc<PluginSource>) -> PluginCustomValueWithSource;
298}
299
300impl WithSource for PluginCustomValue {
301 fn with_source(self, source: Arc<PluginSource>) -> PluginCustomValueWithSource {
302 PluginCustomValueWithSource::new(self, source)
303 }
304}