Skip to main content

shannon_nu_protocol/pipeline/
pipeline_data.rs

1#[cfg(feature = "os")]
2use crate::process::ExitStatusGuard;
3use crate::{
4    ByteStream, ByteStreamSource, ByteStreamType, Config, ListStream, OutDest, PipelineMetadata,
5    Range, ShellError, Signals, Span, Type, Value,
6    ast::{Call, PathMember},
7    engine::{EngineState, Stack},
8    shell_error::{generic::GenericError, io::IoError},
9};
10use std::{borrow::Cow, io::Write, ops::Deref, panic::Location};
11
12const LINE_ENDING_PATTERN: &[char] = &['\r', '\n'];
13
14/// The foundational abstraction for input and output to commands
15///
16/// This represents either a single Value or a stream of values coming into the command or leaving a command.
17///
18/// A note on implementation:
19///
20/// We've tried a few variations of this structure. Listing these below so we have a record.
21///
22/// * We tried always assuming a stream in Nushell. This was a great 80% solution, but it had some rough edges.
23///   Namely, how do you know the difference between a single string and a list of one string. How do you know
24///   when to flatten the data given to you from a data source into the stream or to keep it as an unflattened
25///   list?
26///
27/// * We tried putting the stream into Value. This had some interesting properties as now commands "just worked
28///   on values", but lead to a few unfortunate issues.
29///
30/// The first is that you can't easily clone Values in a way that felt largely immutable. For example, if
31/// you cloned a Value which contained a stream, and in one variable drained some part of it, then the second
32/// variable would see different values based on what you did to the first.
33///
34/// To make this kind of mutation thread-safe, we would have had to produce a lock for the stream, which in
35/// practice would have meant always locking the stream before reading from it. But more fundamentally, it
36/// felt wrong in practice that observation of a value at runtime could affect other values which happen to
37/// alias the same stream. By separating these, we don't have this effect. Instead, variables could get
38/// concrete list values rather than streams, and be able to view them without non-local effects.
39///
40/// * A balance of the two approaches is what we've landed on: Values are thread-safe to pass, and we can stream
41///   them into any sources. Streams are still available to model the infinite streams approach of original
42///   Nushell.
43#[derive(Debug)]
44pub enum PipelineData {
45    Empty,
46    Value(Value, Option<PipelineMetadata>),
47    ListStream(ListStream, Option<PipelineMetadata>),
48    ByteStream(ByteStream, Option<PipelineMetadata>),
49}
50
51impl PipelineData {
52    pub const fn empty() -> PipelineData {
53        PipelineData::Empty
54    }
55
56    pub fn value(val: Value, metadata: impl Into<Option<PipelineMetadata>>) -> Self {
57        PipelineData::Value(val, metadata.into())
58    }
59
60    pub fn list_stream(stream: ListStream, metadata: impl Into<Option<PipelineMetadata>>) -> Self {
61        PipelineData::ListStream(stream, metadata.into())
62    }
63
64    pub fn byte_stream(stream: ByteStream, metadata: impl Into<Option<PipelineMetadata>>) -> Self {
65        PipelineData::ByteStream(stream, metadata.into())
66    }
67
68    /// Returns a clone of the metadata if it exists.
69    ///
70    /// Note: This performs a deep clone of heap-allocated structures.
71    /// Use [`.metadata_ref()`](Self::metadata_ref), [`.metadata_mut()`](Self::metadata_mut)
72    /// or [`.take_metadata()`](Self::take_metadata) to avoid unnecessary allocations.
73    pub fn metadata(&self) -> Option<PipelineMetadata> {
74        self.metadata_ref().cloned()
75    }
76
77    /// Returns a reference to the metadata if it exists.
78    pub fn metadata_ref(&self) -> Option<&PipelineMetadata> {
79        match self {
80            PipelineData::Empty => None,
81            PipelineData::Value(_, meta)
82            | PipelineData::ListStream(_, meta)
83            | PipelineData::ByteStream(_, meta) => meta.as_ref(),
84        }
85    }
86
87    /// Returns a mutable reference to the metadata if it exists.
88    pub fn metadata_mut(&mut self) -> Option<&mut PipelineMetadata> {
89        match self {
90            PipelineData::Empty => None,
91            PipelineData::Value(_, meta)
92            | PipelineData::ListStream(_, meta)
93            | PipelineData::ByteStream(_, meta) => meta.as_mut(),
94        }
95    }
96
97    /// Take the metadata out of pipeline if it exists.
98    pub fn take_metadata(&mut self) -> Option<PipelineMetadata> {
99        match self {
100            PipelineData::Empty => None,
101            PipelineData::Value(_, meta)
102            | PipelineData::ListStream(_, meta)
103            | PipelineData::ByteStream(_, meta) => meta.take(),
104        }
105    }
106
107    pub fn set_metadata(mut self, metadata: Option<PipelineMetadata>) -> Self {
108        match &mut self {
109            PipelineData::Empty => {}
110            PipelineData::Value(_, meta)
111            | PipelineData::ListStream(_, meta)
112            | PipelineData::ByteStream(_, meta) => *meta = metadata,
113        }
114        self
115    }
116
117    pub fn is_nothing(&self) -> bool {
118        matches!(self, PipelineData::Value(Value::Nothing { .. }, ..))
119            || matches!(self, PipelineData::Empty)
120    }
121
122    /// PipelineData doesn't always have a Span, but we can try!
123    pub fn span(&self) -> Option<Span> {
124        match self {
125            PipelineData::Empty => None,
126            PipelineData::Value(value, ..) => Some(value.span()),
127            PipelineData::ListStream(stream, ..) => Some(stream.span()),
128            PipelineData::ByteStream(stream, ..) => Some(stream.span()),
129        }
130    }
131
132    /// Change the span of the [`PipelineData`].
133    ///
134    /// Returns `Value(Nothing)` with the given span if it was [`PipelineData::empty()`].
135    pub fn with_span(self, span: Span) -> Self {
136        match self {
137            PipelineData::Empty => PipelineData::value(Value::nothing(span), None),
138            PipelineData::Value(value, metadata) => {
139                PipelineData::value(value.with_span(span), metadata)
140            }
141            PipelineData::ListStream(stream, metadata) => {
142                PipelineData::list_stream(stream.with_span(span), metadata)
143            }
144            PipelineData::ByteStream(stream, metadata) => {
145                PipelineData::byte_stream(stream.with_span(span), metadata)
146            }
147        }
148    }
149
150    /// Get a type that is representative of the `PipelineData`.
151    ///
152    /// The type returned here makes no effort to collect a stream, so it may be a different type
153    /// than would be returned by [`Value::get_type()`] on the result of
154    /// [`.into_value()`](Self::into_value).
155    ///
156    /// Specifically, a `ListStream` results in `list<any>` rather than
157    /// the fully complete [`list`](Type::List) type (which would require knowing the contents),
158    /// and a `ByteStream` with [unknown](crate::ByteStreamType::Unknown) type results in
159    /// [`any`](Type::Any) rather than [`string`](Type::String) or [`binary`](Type::Binary).
160    pub fn get_type(&self) -> Type {
161        match self {
162            PipelineData::Empty => Type::Nothing,
163            PipelineData::Value(value, _) => value.get_type(),
164            PipelineData::ListStream(_, _) => Type::list(Type::Any),
165            PipelineData::ByteStream(stream, _) => stream.type_().into(),
166        }
167    }
168
169    /// Determine if the `PipelineData` is a [subtype](https://en.wikipedia.org/wiki/Subtyping) of `other`.
170    ///
171    /// This check makes no effort to collect a stream, so it may be a different result
172    /// than would be returned by calling [`Value::is_subtype_of()`] on the result of
173    /// [`.into_value()`](Self::into_value).
174    ///
175    /// A `ListStream` acts the same as an empty list type: it is a subtype of any [`list`](Type::List)
176    /// or [`table`](Type::Table) type. After converting to a value, it may become a more specific type.
177    /// For example, a `ListStream` is a subtype of `list<int>` and `list<string>`.
178    /// If calling [`.into_value()`](Self::into_value) results in a `list<int>`,
179    /// then the value would not be a subtype of `list<string>`, in contrast to the original `ListStream`.
180    ///
181    /// A `ByteStream` is a subtype of [`string`](Type::String) if it is coercible into a string.
182    /// Likewise, a `ByteStream` is a subtype of [`binary`](Type::Binary) if it is coercible into a binary value.
183    pub fn is_subtype_of(&self, other: &Type) -> bool {
184        match (self, other) {
185            (_, Type::Any) => true,
186            (data, Type::OneOf(oneof)) => oneof.iter().any(|t| data.is_subtype_of(t)),
187            (PipelineData::Empty, Type::Nothing) => true,
188            (PipelineData::Value(val, ..), ty) => val.is_subtype_of(ty),
189
190            // a list stream could be a list with any type, including a table
191            (PipelineData::ListStream(..), Type::List(..) | Type::Table(..)) => true,
192
193            (PipelineData::ByteStream(stream, ..), Type::String)
194                if stream.type_().is_string_coercible() =>
195            {
196                true
197            }
198            (PipelineData::ByteStream(stream, ..), Type::Binary)
199                if stream.type_().is_binary_coercible() =>
200            {
201                true
202            }
203
204            (PipelineData::Empty, _) => false,
205            (PipelineData::ListStream(..), _) => false,
206            (PipelineData::ByteStream(..), _) => false,
207        }
208    }
209
210    pub fn into_value(self, span: Span) -> Result<Value, ShellError> {
211        match self {
212            PipelineData::Empty => Ok(Value::nothing(span)),
213            PipelineData::Value(value, ..) => {
214                if value.span() == Span::unknown() {
215                    Ok(value.with_span(span))
216                } else {
217                    Ok(value)
218                }
219            }
220            PipelineData::ListStream(stream, ..) => stream.into_value(),
221            PipelineData::ByteStream(stream, ..) => stream.into_value(),
222        }
223    }
224
225    /// Converts any `Value` variant that can be represented as a stream into its stream variant.
226    ///
227    /// This means that lists and ranges are converted into list streams, and strings and binary are
228    /// converted into byte streams.
229    ///
230    /// Returns an `Err` with the original stream if the variant couldn't be converted to a stream
231    /// variant. If the variant is already a stream variant, it is returned as-is.
232    pub fn try_into_stream(self, engine_state: &EngineState) -> Result<PipelineData, PipelineData> {
233        let span = self.span().unwrap_or(Span::unknown());
234        match self {
235            PipelineData::ListStream(..) | PipelineData::ByteStream(..) => Ok(self),
236            PipelineData::Value(Value::List { .. } | Value::Range { .. }, ref metadata) => {
237                let metadata = metadata.clone();
238                Ok(PipelineData::list_stream(
239                    ListStream::new(self.into_iter(), span, engine_state.signals().clone()),
240                    metadata,
241                ))
242            }
243            PipelineData::Value(Value::String { val, .. }, metadata) => {
244                Ok(PipelineData::byte_stream(
245                    ByteStream::read_string(val, span, engine_state.signals().clone()),
246                    metadata,
247                ))
248            }
249            PipelineData::Value(Value::Binary { val, .. }, metadata) => {
250                Ok(PipelineData::byte_stream(
251                    ByteStream::read_binary(val, span, engine_state.signals().clone()),
252                    metadata,
253                ))
254            }
255            PipelineData::Value(Value::Custom { val, internal_span }, metadata) => {
256                match val.to_base_value(internal_span) {
257                    Ok(Value::List { vals, .. }) => Ok(PipelineData::list_stream(
258                        ListStream::new(vals.into_iter(), span, engine_state.signals().clone()),
259                        metadata,
260                    )),
261                    Ok(Value::Range { val, .. }) => Ok(PipelineData::list_stream(
262                        ListStream::new(
263                            val.into_range_iter(span, Signals::empty()),
264                            span,
265                            engine_state.signals().clone(),
266                        ),
267                        metadata,
268                    )),
269                    Ok(other) => Err(PipelineData::value(other, metadata)),
270                    Err(_) => Err(PipelineData::Value(
271                        Value::Custom { val, internal_span },
272                        metadata,
273                    )),
274                }
275            }
276            _ => Err(self),
277        }
278    }
279
280    /// Converts this value into a stream when possible, otherwise returns the original value.
281    ///
282    /// This is a convenience wrapper around [`PipelineData::try_into_stream`] for command code
283    /// paths that can operate on both stream and non-stream input without branching.
284    #[must_use]
285    pub fn into_stream_or_original(self, engine_state: &EngineState) -> PipelineData {
286        self.try_into_stream(engine_state)
287            .unwrap_or_else(|original| original)
288    }
289
290    /// Drain and write this [`PipelineData`] to `dest`.
291    ///
292    /// Values are converted to bytes and separated by newlines if this is a `ListStream`.
293    pub fn write_to(self, mut dest: impl Write) -> Result<(), ShellError> {
294        match self {
295            PipelineData::Empty => Ok(()),
296            PipelineData::Value(value, ..) => {
297                let bytes = value_to_bytes(value)?;
298                dest.write_all(&bytes).map_err(|err| {
299                    IoError::new_internal(err, "Could not write PipelineData to dest")
300                })?;
301                dest.flush().map_err(|err| {
302                    IoError::new_internal(err, "Could not flush PipelineData to dest")
303                })?;
304                Ok(())
305            }
306            PipelineData::ListStream(stream, ..) => {
307                for value in stream {
308                    let bytes = value_to_bytes(value)?;
309                    dest.write_all(&bytes).map_err(|err| {
310                        IoError::new_internal(err, "Could not write PipelineData to dest")
311                    })?;
312                    dest.write_all(b"\n").map_err(|err| {
313                        IoError::new_internal(
314                            err,
315                            "Could not write linebreak after PipelineData to dest",
316                        )
317                    })?;
318                }
319                dest.flush().map_err(|err| {
320                    IoError::new_internal(err, "Could not flush PipelineData to dest")
321                })?;
322                Ok(())
323            }
324            PipelineData::ByteStream(stream, ..) => stream.write_to(dest),
325        }
326    }
327
328    /// Drain this [`PipelineData`] according to the current stdout [`OutDest`]s in `stack`.
329    ///
330    /// For [`OutDest::Pipe`] and [`OutDest::PipeSeparate`], this will return the [`PipelineData`]
331    /// as is. For [`OutDest::Value`], this will collect into a value and return it. For
332    /// [`OutDest::Print`], the [`PipelineData`] is drained and printed. Otherwise, the
333    /// [`PipelineData`] is drained, but only printed if it is the output of an external command.
334    pub fn drain_to_out_dests(
335        self,
336        engine_state: &EngineState,
337        stack: &mut Stack,
338    ) -> Result<Self, ShellError> {
339        match stack.pipe_stdout().unwrap_or(&OutDest::Inherit) {
340            OutDest::Print => {
341                self.print_table(engine_state, stack, false, false)?;
342                Ok(Self::Empty)
343            }
344            OutDest::Pipe | OutDest::PipeSeparate => Ok(self),
345            OutDest::Value => {
346                let metadata = self.metadata();
347                let span = self.span().unwrap_or(Span::unknown());
348                self.into_value(span).map(|val| Self::Value(val, metadata))
349            }
350            OutDest::File(file) => {
351                self.write_to(file.as_ref())?;
352                Ok(Self::Empty)
353            }
354            OutDest::Null | OutDest::Inherit => {
355                self.drain()?;
356                Ok(Self::Empty)
357            }
358        }
359    }
360
361    pub fn drain(self) -> Result<(), ShellError> {
362        match self {
363            Self::Empty => Ok(()),
364            Self::Value(Value::Error { error, .. }, ..) => Err(*error),
365            Self::Value(..) => Ok(()),
366            Self::ListStream(stream, ..) => stream.drain(),
367            Self::ByteStream(stream, ..) => stream.drain(),
368        }
369    }
370
371    /// Try convert from self into iterator
372    ///
373    /// It returns Err if the `self` cannot be converted to an iterator.
374    ///
375    /// The `span` should be the span of the command or operation that would raise an error.
376    pub fn into_iter_strict(self, span: Span) -> Result<PipelineIterator, ShellError> {
377        Ok(PipelineIterator(match self {
378            PipelineData::Value(value, ..) => {
379                let val_span = value.span();
380                match value {
381                    Value::List { vals, .. } => PipelineIteratorInner::ListStream(
382                        ListStream::new(vals.into_iter(), val_span, Signals::empty()).into_iter(),
383                    ),
384                    Value::Binary { val, .. } => PipelineIteratorInner::ListStream(
385                        ListStream::new(
386                            val.into_iter().map(move |x| Value::int(x as i64, val_span)),
387                            val_span,
388                            Signals::empty(),
389                        )
390                        .into_iter(),
391                    ),
392                    Value::Range { val, .. } => PipelineIteratorInner::ListStream(
393                        ListStream::new(
394                            val.into_range_iter(val_span, Signals::empty()),
395                            val_span,
396                            Signals::empty(),
397                        )
398                        .into_iter(),
399                    ),
400                    // Handle iterable custom values by converting to base value first
401                    Value::Custom { ref val, .. } if val.is_iterable() => {
402                        match val.to_base_value(val_span) {
403                            Ok(Value::List { vals, .. }) => PipelineIteratorInner::ListStream(
404                                ListStream::new(vals.into_iter(), val_span, Signals::empty())
405                                    .into_iter(),
406                            ),
407                            Ok(other) => {
408                                return Err(ShellError::OnlySupportsThisInputType {
409                                    exp_input_type: "list, binary, range, or byte stream".into(),
410                                    wrong_type: other.get_type().to_string(),
411                                    dst_span: span,
412                                    src_span: val_span,
413                                });
414                            }
415                            Err(err) => return Err(err),
416                        }
417                    }
418                    // Propagate errors by explicitly matching them before the final case.
419                    Value::Error { error, .. } => return Err(*error),
420                    other => {
421                        return Err(ShellError::OnlySupportsThisInputType {
422                            exp_input_type: "list, binary, range, or byte stream".into(),
423                            wrong_type: other.get_type().to_string(),
424                            dst_span: span,
425                            src_span: val_span,
426                        });
427                    }
428                }
429            }
430            PipelineData::ListStream(stream, ..) => {
431                PipelineIteratorInner::ListStream(stream.into_iter())
432            }
433            PipelineData::Empty => {
434                return Err(ShellError::OnlySupportsThisInputType {
435                    exp_input_type: "list, binary, range, or byte stream".into(),
436                    wrong_type: "null".into(),
437                    dst_span: span,
438                    src_span: span,
439                });
440            }
441            PipelineData::ByteStream(stream, ..) => {
442                if let Some(chunks) = stream.chunks() {
443                    PipelineIteratorInner::ByteStream(chunks)
444                } else {
445                    PipelineIteratorInner::Empty
446                }
447            }
448        }))
449    }
450
451    pub fn collect_string(self, separator: &str, config: &Config) -> Result<String, ShellError> {
452        match self {
453            PipelineData::Empty => Ok(String::new()),
454            PipelineData::Value(value, ..) => Ok(value.to_expanded_string(separator, config)),
455            PipelineData::ListStream(stream, ..) => Ok(stream.into_string(separator, config)),
456            PipelineData::ByteStream(stream, ..) => stream.into_string(),
457        }
458    }
459
460    /// Retrieves string from pipeline data.
461    ///
462    /// As opposed to `collect_string` this raises error rather than converting non-string values.
463    /// The `span` will be used if `ListStream` is encountered since it doesn't carry a span.
464    pub fn collect_string_strict(
465        self,
466        span: Span,
467    ) -> Result<(String, Span, Option<PipelineMetadata>), ShellError> {
468        match self {
469            PipelineData::Empty => Ok((String::new(), span, None)),
470            PipelineData::Value(Value::String { val, .. }, metadata) => Ok((val, span, metadata)),
471            PipelineData::Value(val, ..) => Err(ShellError::TypeMismatch {
472                err_message: "string".into(),
473                span: val.span(),
474            }),
475            PipelineData::ListStream(..) => Err(ShellError::TypeMismatch {
476                err_message: "string".into(),
477                span,
478            }),
479            PipelineData::ByteStream(stream, metadata) => {
480                let span = stream.span();
481                Ok((stream.into_string()?, span, metadata))
482            }
483        }
484    }
485
486    pub fn follow_cell_path(
487        self,
488        cell_path: &[PathMember],
489        head: Span,
490    ) -> Result<Value, ShellError> {
491        match self {
492            // FIXME: there are probably better ways of doing this
493            PipelineData::ListStream(stream, ..) => Value::list(stream.into_iter().collect(), head)
494                .follow_cell_path(cell_path)
495                .map(Cow::into_owned),
496            PipelineData::Value(v, ..) => v.follow_cell_path(cell_path).map(Cow::into_owned),
497            PipelineData::Empty => Err(ShellError::IncompatiblePathAccess {
498                type_name: "empty pipeline".to_string(),
499                span: head,
500            }),
501            PipelineData::ByteStream(stream, ..) => Err(ShellError::IncompatiblePathAccess {
502                type_name: stream.type_().describe().to_owned(),
503                span: stream.span(),
504            }),
505        }
506    }
507
508    /// Simplified mapper to help with simple values also. For full iterator support use `.into_iter()` instead
509    pub fn map<F>(self, mut f: F, signals: &Signals) -> Result<PipelineData, ShellError>
510    where
511        Self: Sized,
512        F: FnMut(Value) -> Value + 'static + Send,
513    {
514        match self {
515            PipelineData::Value(value, metadata) => {
516                let span = value.span();
517                let pipeline = match value {
518                    Value::List { vals, .. } => vals
519                        .into_iter()
520                        .map(f)
521                        .into_pipeline_data(span, signals.clone()),
522                    Value::Range { val, .. } => val
523                        .into_range_iter(span, Signals::empty())
524                        .map(f)
525                        .into_pipeline_data(span, signals.clone()),
526                    Value::Custom { ref val, .. } if val.is_iterable() => {
527                        match val.to_base_value(span)? {
528                            Value::List { vals, .. } => vals
529                                .into_iter()
530                                .map(f)
531                                .into_pipeline_data(span, signals.clone()),
532                            Value::Range { val, .. } => val
533                                .into_range_iter(span, Signals::empty())
534                                .map(f)
535                                .into_pipeline_data(span, signals.clone()),
536                            value => match f(value) {
537                                Value::Error { error, .. } => return Err(*error),
538                                v => v.into_pipeline_data(),
539                            },
540                        }
541                    }
542                    value => match f(value) {
543                        Value::Error { error, .. } => return Err(*error),
544                        v => v.into_pipeline_data(),
545                    },
546                };
547                Ok(pipeline.set_metadata(metadata))
548            }
549            PipelineData::Empty => Ok(PipelineData::empty()),
550            PipelineData::ListStream(stream, metadata) => {
551                Ok(PipelineData::list_stream(stream.map(f), metadata))
552            }
553            PipelineData::ByteStream(stream, metadata) => {
554                Ok(f(stream.into_value()?).into_pipeline_data_with_metadata(metadata))
555            }
556        }
557    }
558
559    /// Simplified flatmapper. For full iterator support use `.into_iter()` instead
560    pub fn flat_map<U, F>(self, mut f: F, signals: &Signals) -> Result<PipelineData, ShellError>
561    where
562        Self: Sized,
563        U: IntoIterator<Item = Value> + 'static,
564        <U as IntoIterator>::IntoIter: 'static + Send,
565        F: FnMut(Value) -> U + 'static + Send,
566    {
567        match self {
568            PipelineData::Empty => Ok(PipelineData::empty()),
569            PipelineData::Value(value, metadata) => {
570                let span = value.span();
571                let pipeline = match value {
572                    Value::List { vals, .. } => vals
573                        .into_iter()
574                        .flat_map(f)
575                        .into_pipeline_data(span, signals.clone()),
576                    Value::Range { val, .. } => val
577                        .into_range_iter(span, Signals::empty())
578                        .flat_map(f)
579                        .into_pipeline_data(span, signals.clone()),
580                    Value::Custom { ref val, .. } if val.is_iterable() => {
581                        match val.to_base_value(span)? {
582                            Value::List { vals, .. } => vals
583                                .into_iter()
584                                .flat_map(f)
585                                .into_pipeline_data(span, signals.clone()),
586                            Value::Range { val, .. } => val
587                                .into_range_iter(span, Signals::empty())
588                                .flat_map(f)
589                                .into_pipeline_data(span, signals.clone()),
590                            value => f(value)
591                                .into_iter()
592                                .into_pipeline_data(span, signals.clone()),
593                        }
594                    }
595                    value => f(value)
596                        .into_iter()
597                        .into_pipeline_data(span, signals.clone()),
598                };
599                Ok(pipeline.set_metadata(metadata))
600            }
601            PipelineData::ListStream(stream, metadata) => Ok(PipelineData::list_stream(
602                stream.modify(|iter| iter.flat_map(f)),
603                metadata,
604            )),
605            PipelineData::ByteStream(stream, metadata) => {
606                // TODO: is this behavior desired / correct ?
607                let span = stream.span();
608                let iter = match String::from_utf8(stream.into_bytes()?) {
609                    Ok(mut str) => {
610                        str.truncate(str.trim_end_matches(LINE_ENDING_PATTERN).len());
611                        f(Value::string(str, span))
612                    }
613                    Err(err) => f(Value::binary(err.into_bytes(), span)),
614                };
615                Ok(iter.into_iter().into_pipeline_data_with_metadata(
616                    span,
617                    signals.clone(),
618                    metadata,
619                ))
620            }
621        }
622    }
623
624    pub fn filter<F>(self, mut f: F, signals: &Signals) -> Result<PipelineData, ShellError>
625    where
626        Self: Sized,
627        F: FnMut(&Value) -> bool + 'static + Send,
628    {
629        match self {
630            PipelineData::Empty => Ok(PipelineData::empty()),
631            PipelineData::Value(value, metadata) => {
632                let span = value.span();
633                let pipeline = match value {
634                    Value::List { vals, .. } => vals
635                        .into_iter()
636                        .filter(f)
637                        .into_pipeline_data(span, signals.clone()),
638                    Value::Range { val, .. } => val
639                        .into_range_iter(span, Signals::empty())
640                        .filter(f)
641                        .into_pipeline_data(span, signals.clone()),
642                    Value::Custom { ref val, .. } if val.is_iterable() => {
643                        match val.to_base_value(span)? {
644                            Value::List { vals, .. } => vals
645                                .into_iter()
646                                .filter(f)
647                                .into_pipeline_data(span, signals.clone()),
648                            Value::Range { val, .. } => val
649                                .into_range_iter(span, Signals::empty())
650                                .filter(f)
651                                .into_pipeline_data(span, signals.clone()),
652                            value => {
653                                if f(&value) {
654                                    value.into_pipeline_data()
655                                } else {
656                                    Value::nothing(span).into_pipeline_data()
657                                }
658                            }
659                        }
660                    }
661                    value => {
662                        if f(&value) {
663                            value.into_pipeline_data()
664                        } else {
665                            Value::nothing(span).into_pipeline_data()
666                        }
667                    }
668                };
669                Ok(pipeline.set_metadata(metadata))
670            }
671            PipelineData::ListStream(stream, metadata) => Ok(PipelineData::list_stream(
672                stream.modify(|iter| iter.filter(f)),
673                metadata,
674            )),
675            PipelineData::ByteStream(stream, metadata) => {
676                // TODO: is this behavior desired / correct ?
677                let span = stream.span();
678                let value = match String::from_utf8(stream.into_bytes()?) {
679                    Ok(mut str) => {
680                        str.truncate(str.trim_end_matches(LINE_ENDING_PATTERN).len());
681                        Value::string(str, span)
682                    }
683                    Err(err) => Value::binary(err.into_bytes(), span),
684                };
685                let value = if f(&value) {
686                    value
687                } else {
688                    Value::nothing(span)
689                };
690                Ok(value.into_pipeline_data_with_metadata(metadata))
691            }
692        }
693    }
694
695    /// Try to convert Value from Value::Range to Value::List.
696    /// This is useful to expand Value::Range into array notation, specifically when
697    /// converting `to json` or `to nuon`.
698    /// `1..3 | to XX -> [1,2,3]`
699    pub fn try_expand_range(self) -> Result<PipelineData, ShellError> {
700        match self {
701            PipelineData::Value(v, metadata) => {
702                let span = v.span();
703                match v {
704                    Value::Range { val, .. } => {
705                        match *val {
706                            Range::IntRange(range) => {
707                                if range.is_unbounded() {
708                                    return Err(ShellError::Generic(
709                                        GenericError::new(
710                                            "Cannot create range",
711                                            "Unbounded ranges are not allowed when converting to this format",
712                                            span,
713                                        )
714                                        .with_help(
715                                            "Consider using ranges with valid start and end point.",
716                                        ),
717                                    ));
718                                }
719                            }
720                            Range::FloatRange(range) => {
721                                if range.is_unbounded() {
722                                    return Err(ShellError::Generic(
723                                        GenericError::new(
724                                            "Cannot create range",
725                                            "Unbounded ranges are not allowed when converting to this format",
726                                            span,
727                                        )
728                                        .with_help(
729                                            "Consider using ranges with valid start and end point.",
730                                        ),
731                                    ));
732                                }
733                            }
734                        }
735                        let range_values: Vec<Value> =
736                            val.into_range_iter(span, Signals::empty()).collect();
737                        Ok(PipelineData::value(Value::list(range_values, span), None))
738                    }
739                    x => Ok(PipelineData::value(x, metadata)),
740                }
741            }
742            _ => Ok(self),
743        }
744    }
745
746    /// Consume and print self data immediately, formatted using table command.
747    ///
748    /// This does not respect the display_output hook. If a value is being printed out by a command,
749    /// this function should be used. Otherwise, `nu_cli::util::print_pipeline` should be preferred.
750    ///
751    /// `no_newline` controls if we need to attach newline character to output.
752    /// `to_stderr` controls if data is output to stderr, when the value is false, the data is output to stdout.
753    pub fn print_table(
754        self,
755        engine_state: &EngineState,
756        stack: &mut Stack,
757        no_newline: bool,
758        to_stderr: bool,
759    ) -> Result<(), ShellError> {
760        match self {
761            // Print byte streams directly as long as they aren't binary.
762            PipelineData::ByteStream(stream, ..) if stream.type_() != ByteStreamType::Binary => {
763                stream.print(to_stderr)
764            }
765            _ => {
766                // If the table function is in the declarations, then we can use it
767                // to create the table value that will be printed in the terminal
768                if let Some(decl_id) = engine_state.table_decl_id {
769                    let command = engine_state.get_decl(decl_id);
770                    if command.block_id().is_some() {
771                        self.write_all_and_flush(engine_state, no_newline, to_stderr)
772                    } else {
773                        let call = Call::new(Span::new(0, 0));
774                        let table = command.run(engine_state, stack, &(&call).into(), self)?;
775                        table.write_all_and_flush(engine_state, no_newline, to_stderr)
776                    }
777                } else {
778                    self.write_all_and_flush(engine_state, no_newline, to_stderr)
779                }
780            }
781        }
782    }
783
784    /// Consume and print self data without any extra formatting.
785    ///
786    /// This does not use the `table` command to format data, and also prints binary values and
787    /// streams in their raw format without generating a hexdump first.
788    ///
789    /// `no_newline` controls if we need to attach newline character to output.
790    /// `to_stderr` controls if data is output to stderr, when the value is false, the data is output to stdout.
791    pub fn print_raw(
792        self,
793        engine_state: &EngineState,
794        no_newline: bool,
795        to_stderr: bool,
796    ) -> Result<(), ShellError> {
797        let span = self.span();
798        if let PipelineData::Value(Value::Binary { val: bytes, .. }, _) = self {
799            if to_stderr {
800                write_all_and_flush(
801                    bytes,
802                    &mut std::io::stderr().lock(),
803                    "stderr",
804                    span,
805                    engine_state.signals(),
806                )?;
807            } else {
808                write_all_and_flush(
809                    bytes,
810                    &mut std::io::stdout().lock(),
811                    "stdout",
812                    span,
813                    engine_state.signals(),
814                )?;
815            }
816            Ok(())
817        } else {
818            self.write_all_and_flush(engine_state, no_newline, to_stderr)
819        }
820    }
821
822    fn write_all_and_flush(
823        self,
824        engine_state: &EngineState,
825        no_newline: bool,
826        to_stderr: bool,
827    ) -> Result<(), ShellError> {
828        let span = self.span();
829        if let PipelineData::ByteStream(stream, ..) = self {
830            // Copy ByteStreams directly
831            stream.print(to_stderr)
832        } else {
833            let config = engine_state.get_config();
834            for item in self {
835                let mut out = if let Value::Error { error, .. } = item {
836                    return Err(*error);
837                } else {
838                    item.to_expanded_string("\n", config)
839                };
840
841                if !no_newline {
842                    out.push('\n');
843                }
844
845                if to_stderr {
846                    write_all_and_flush(
847                        out,
848                        &mut std::io::stderr().lock(),
849                        "stderr",
850                        span,
851                        engine_state.signals(),
852                    )?;
853                } else {
854                    write_all_and_flush(
855                        out,
856                        &mut std::io::stdout().lock(),
857                        "stdout",
858                        span,
859                        engine_state.signals(),
860                    )?;
861                }
862            }
863
864            Ok(())
865        }
866    }
867
868    pub fn unsupported_input_error(
869        self,
870        expected_type: impl Into<String>,
871        span: Span,
872    ) -> ShellError {
873        match self {
874            PipelineData::Empty => ShellError::PipelineEmpty { dst_span: span },
875            PipelineData::Value(value, ..) => ShellError::OnlySupportsThisInputType {
876                exp_input_type: expected_type.into(),
877                wrong_type: value.get_type().get_non_specified_string(),
878                dst_span: span,
879                src_span: value.span(),
880            },
881            PipelineData::ListStream(stream, ..) => ShellError::OnlySupportsThisInputType {
882                exp_input_type: expected_type.into(),
883                wrong_type: "list (stream)".into(),
884                dst_span: span,
885                src_span: stream.span(),
886            },
887            PipelineData::ByteStream(stream, ..) => ShellError::OnlySupportsThisInputType {
888                exp_input_type: expected_type.into(),
889                wrong_type: stream.type_().describe().into(),
890                dst_span: span,
891                src_span: stream.span(),
892            },
893        }
894    }
895
896    // PipelineData might connect to a running process which has an exit status future
897    // Use this method to retrieve that future, it's useful for implementing `pipefail` feature.
898    #[cfg(feature = "os")]
899    pub fn clone_exit_status_future(&self) -> Option<ExitStatusGuard> {
900        match self {
901            PipelineData::Empty | PipelineData::Value(..) | PipelineData::ListStream(..) => None,
902            PipelineData::ByteStream(stream, ..) => match stream.source() {
903                ByteStreamSource::Read(..) | ByteStreamSource::File(..) => None,
904                ByteStreamSource::Child(c) => {
905                    let exit_future = c.clone_exit_status_future();
906                    let ignore_error = c.clone_ignore_error();
907                    Some(ExitStatusGuard::new(exit_future, ignore_error))
908                }
909            },
910        }
911    }
912}
913
914pub fn write_all_and_flush<T>(
915    data: T,
916    destination: &mut impl Write,
917    destination_name: &str,
918    span: Option<Span>,
919    signals: &Signals,
920) -> Result<(), ShellError>
921where
922    T: AsRef<[u8]>,
923{
924    let io_error_map = |err: std::io::Error, location: &Location<'_>| {
925        let context = format!("Writing to {destination_name} failed");
926        match span {
927            None => IoError::new_internal_with_location(err, context, location),
928            Some(span) if span == Span::unknown() => {
929                IoError::new_internal_with_location(err, context, location)
930            }
931            Some(span) => IoError::new_with_additional_context(err, span, None, context),
932        }
933    };
934
935    let span = span.unwrap_or(Span::unknown());
936    const OUTPUT_CHUNK_SIZE: usize = 8192;
937    for chunk in data.as_ref().chunks(OUTPUT_CHUNK_SIZE) {
938        signals.check(&span)?;
939        destination
940            .write_all(chunk)
941            .map_err(|err| io_error_map(err, Location::caller()))?;
942    }
943    destination
944        .flush()
945        .map_err(|err| io_error_map(err, Location::caller()))?;
946    Ok(())
947}
948
949enum PipelineIteratorInner {
950    Empty,
951    Value(Value),
952    ListStream(crate::list_stream::IntoIter),
953    ByteStream(crate::byte_stream::Chunks),
954}
955
956pub struct PipelineIterator(PipelineIteratorInner);
957
958impl IntoIterator for PipelineData {
959    type Item = Value;
960
961    type IntoIter = PipelineIterator;
962
963    fn into_iter(self) -> Self::IntoIter {
964        PipelineIterator(match self {
965            PipelineData::Empty => PipelineIteratorInner::Empty,
966            PipelineData::Value(value, ..) => {
967                let span = value.span();
968                match value {
969                    Value::List { vals, signals, .. } => PipelineIteratorInner::ListStream(
970                        ListStream::new(
971                            vals.into_iter(),
972                            span,
973                            signals.unwrap_or_else(Signals::empty),
974                        )
975                        .into_iter(),
976                    ),
977                    Value::Range { val, signals, .. } => PipelineIteratorInner::ListStream(
978                        ListStream::new(
979                            val.into_range_iter(span, signals.unwrap_or_else(Signals::empty)),
980                            span,
981                            Signals::empty(),
982                        )
983                        .into_iter(),
984                    ),
985                    // Handle iterable custom values by converting to base value first
986                    Value::Custom { ref val, .. } if val.is_iterable() => {
987                        match val.to_base_value(span) {
988                            Ok(Value::List { vals, signals, .. }) => {
989                                PipelineIteratorInner::ListStream(
990                                    ListStream::new(
991                                        vals.into_iter(),
992                                        span,
993                                        signals.unwrap_or_else(Signals::empty),
994                                    )
995                                    .into_iter(),
996                                )
997                            }
998                            Ok(other) => PipelineIteratorInner::Value(other),
999                            Err(err) => PipelineIteratorInner::Value(Value::error(err, span)),
1000                        }
1001                    }
1002                    x => PipelineIteratorInner::Value(x),
1003                }
1004            }
1005            PipelineData::ListStream(stream, ..) => {
1006                PipelineIteratorInner::ListStream(stream.into_iter())
1007            }
1008            PipelineData::ByteStream(stream, ..) => stream.chunks().map_or(
1009                PipelineIteratorInner::Empty,
1010                PipelineIteratorInner::ByteStream,
1011            ),
1012        })
1013    }
1014}
1015
1016impl Iterator for PipelineIterator {
1017    type Item = Value;
1018
1019    fn next(&mut self) -> Option<Self::Item> {
1020        match &mut self.0 {
1021            PipelineIteratorInner::Empty => None,
1022            PipelineIteratorInner::Value(Value::Nothing { .. }, ..) => None,
1023            PipelineIteratorInner::Value(v, ..) => Some(std::mem::take(v)),
1024            PipelineIteratorInner::ListStream(stream, ..) => stream.next(),
1025            PipelineIteratorInner::ByteStream(stream) => stream.next().map(|x| match x {
1026                Ok(x) => x,
1027                Err(err) => Value::error(
1028                    err,
1029                    Span::unknown(), //FIXME: unclear where this span should come from
1030                ),
1031            }),
1032        }
1033    }
1034}
1035
1036pub trait IntoPipelineData {
1037    fn into_pipeline_data(self) -> PipelineData;
1038
1039    fn into_pipeline_data_with_metadata(
1040        self,
1041        metadata: impl Into<Option<PipelineMetadata>>,
1042    ) -> PipelineData;
1043}
1044
1045impl<V> IntoPipelineData for V
1046where
1047    V: Into<Value>,
1048{
1049    fn into_pipeline_data(self) -> PipelineData {
1050        PipelineData::value(self.into(), None)
1051    }
1052
1053    fn into_pipeline_data_with_metadata(
1054        self,
1055        metadata: impl Into<Option<PipelineMetadata>>,
1056    ) -> PipelineData {
1057        PipelineData::value(self.into(), metadata.into())
1058    }
1059}
1060
1061pub trait IntoInterruptiblePipelineData {
1062    fn into_pipeline_data(self, span: Span, signals: Signals) -> PipelineData;
1063    fn into_pipeline_data_with_metadata(
1064        self,
1065        span: Span,
1066        signals: Signals,
1067        metadata: impl Into<Option<PipelineMetadata>>,
1068    ) -> PipelineData;
1069}
1070
1071impl<I> IntoInterruptiblePipelineData for I
1072where
1073    I: IntoIterator + Send + 'static,
1074    I::IntoIter: Send + 'static,
1075    <I::IntoIter as Iterator>::Item: Into<Value>,
1076{
1077    fn into_pipeline_data(self, span: Span, signals: Signals) -> PipelineData {
1078        ListStream::new(self.into_iter().map(Into::into), span, signals).into()
1079    }
1080
1081    fn into_pipeline_data_with_metadata(
1082        self,
1083        span: Span,
1084        signals: Signals,
1085        metadata: impl Into<Option<PipelineMetadata>>,
1086    ) -> PipelineData {
1087        PipelineData::list_stream(
1088            ListStream::new(self.into_iter().map(Into::into), span, signals),
1089            metadata.into(),
1090        )
1091    }
1092}
1093
1094fn value_to_bytes(value: Value) -> Result<Vec<u8>, ShellError> {
1095    let bytes = match value {
1096        Value::String { val, .. } => val.into_bytes(),
1097        Value::Binary { val, .. } => val,
1098        Value::List { vals, .. } => {
1099            let val = vals
1100                .into_iter()
1101                .map(Value::coerce_into_string)
1102                .collect::<Result<Vec<String>, ShellError>>()?
1103                .join("\n")
1104                + "\n";
1105
1106            val.into_bytes()
1107        }
1108        // Propagate errors by explicitly matching them before the final case.
1109        Value::Error { error, .. } => return Err(*error),
1110        value => value.coerce_into_string()?.into_bytes(),
1111    };
1112    Ok(bytes)
1113}
1114
1115/// A wrapper to [`PipelineData`] which can also track exit status.
1116///
1117/// We use exit status tracking to implement the `pipefail` feature.
1118#[derive(Debug)]
1119pub struct PipelineExecutionData {
1120    pub body: PipelineData,
1121    #[cfg(feature = "os")]
1122    pub exit: Vec<Option<ExitStatusGuard>>,
1123}
1124
1125impl Deref for PipelineExecutionData {
1126    type Target = PipelineData;
1127
1128    fn deref(&self) -> &Self::Target {
1129        &self.body
1130    }
1131}
1132
1133impl PipelineExecutionData {
1134    pub fn empty() -> Self {
1135        Self {
1136            body: PipelineData::empty(),
1137            #[cfg(feature = "os")]
1138            exit: vec![],
1139        }
1140    }
1141}
1142
1143impl From<PipelineData> for PipelineExecutionData {
1144    #[cfg(feature = "os")]
1145    fn from(value: PipelineData) -> Self {
1146        let value_span = value.span().unwrap_or_else(Span::unknown);
1147        let exit_status_future = value
1148            .clone_exit_status_future()
1149            .map(|f| f.with_span(value_span));
1150        Self {
1151            body: value,
1152            exit: vec![exit_status_future],
1153        }
1154    }
1155
1156    #[cfg(not(feature = "os"))]
1157    fn from(value: PipelineData) -> Self {
1158        Self { body: value }
1159    }
1160}