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