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