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#[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 pub fn metadata(&self) -> Option<PipelineMetadata> {
74 self.metadata_ref().cloned()
75 }
76
77 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 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 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 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 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 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 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 (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 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 #[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 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 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 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 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 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 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 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 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 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 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 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 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 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 PipelineData::ByteStream(stream, ..) if stream.type_() != ByteStreamType::Binary => {
763 stream.print(to_stderr)
764 }
765 _ => {
766 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 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 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 #[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 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(), ),
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 Value::Error { error, .. } => return Err(*error),
1110 value => value.coerce_into_string()?.into_bytes(),
1111 };
1112 Ok(bytes)
1113}
1114
1115#[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}