Skip to main content

nu_protocol/ast/
block.rs

1use super::Pipeline;
2use crate::{
3    OutDest, Signature, Span, Type, VarId,
4    engine::{ScopeBindings, StateWorkingSet},
5    ir::IrBlock,
6};
7use serde::{Deserialize, Serialize};
8use std::sync::Arc;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct Block {
12    pub signature: Box<Signature>,
13    pub pipelines: Vec<Pipeline>,
14    pub captures: Vec<(VarId, Span)>,
15    pub redirect_env: bool,
16    /// The block compiled to IR instructions. Not available for subexpressions.
17    pub ir_block: Option<IrBlock>,
18    pub span: Option<Span>, // None option encodes no span to avoid using test_span()
19    /// Local command/module name bindings introduced while parsing this block.
20    ///
21    /// Nested parse scopes discard their name maps on exit; this snapshot lets `scope`
22    /// subcommands report those locals while the block is being evaluated.
23    ///
24    /// Not serialized: only meaningful within the process that parsed the block.
25    #[serde(skip)]
26    pub scope_bindings: Option<Arc<ScopeBindings>>,
27}
28
29impl Block {
30    pub fn len(&self) -> usize {
31        self.pipelines.len()
32    }
33
34    pub fn is_empty(&self) -> bool {
35        self.pipelines.is_empty()
36    }
37
38    pub fn pipe_redirection(
39        &self,
40        working_set: &StateWorkingSet,
41    ) -> (Option<OutDest>, Option<OutDest>) {
42        if let Some(first) = self.pipelines.first() {
43            first.pipe_redirection(working_set)
44        } else {
45            (None, None)
46        }
47    }
48}
49
50impl Default for Block {
51    fn default() -> Self {
52        Self::new()
53    }
54}
55
56impl Block {
57    pub fn new() -> Self {
58        Self {
59            signature: Box::new(Signature::new("")),
60            pipelines: vec![],
61            captures: vec![],
62            redirect_env: false,
63            ir_block: None,
64            span: None,
65            scope_bindings: None,
66        }
67    }
68
69    pub fn new_with_capacity(capacity: usize) -> Self {
70        Self {
71            signature: Box::new(Signature::new("")),
72            pipelines: Vec::with_capacity(capacity),
73            captures: vec![],
74            redirect_env: false,
75            ir_block: None,
76            span: None,
77            scope_bindings: None,
78        }
79    }
80
81    pub fn output_type(&self) -> Type {
82        match self.pipelines.last().and_then(|pl| pl.elements.last()) {
83            Some(pe) if pe.redirection.is_none() => pe.expr.ty.clone(),
84            Some(_) => Type::Any,
85            None => Type::Nothing,
86        }
87    }
88
89    /// Replace any `$in` variables in the initial element of pipelines within the block
90    pub fn replace_in_variable(
91        &mut self,
92        working_set: &mut StateWorkingSet<'_>,
93        new_var_id: VarId,
94    ) {
95        for pipeline in self.pipelines.iter_mut() {
96            if let Some(element) = pipeline.elements.first_mut() {
97                element.replace_in_variable(working_set, new_var_id);
98            }
99        }
100    }
101}
102
103impl<T> From<T> for Block
104where
105    T: Iterator<Item = Pipeline>,
106{
107    fn from(pipelines: T) -> Self {
108        Self {
109            signature: Box::new(Signature::new("")),
110            pipelines: pipelines.collect(),
111            captures: vec![],
112            redirect_env: false,
113            ir_block: None,
114            span: None,
115            scope_bindings: None,
116        }
117    }
118}