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    /// Whether `parse_block` ran with `scoped = true` (`enter_scope`).
28    ///
29    /// Distinct from `scope_bindings`: a scoped parse of a let-only file still
30    /// snapshots as `None`. Needed so `source` does not reuse a `source-env` parse.
31    #[serde(skip)]
32    pub parsed_scoped: bool,
33}
34
35impl Block {
36    pub fn len(&self) -> usize {
37        self.pipelines.len()
38    }
39
40    pub fn is_empty(&self) -> bool {
41        self.pipelines.is_empty()
42    }
43
44    pub fn pipe_redirection(
45        &self,
46        working_set: &StateWorkingSet,
47    ) -> (Option<OutDest>, Option<OutDest>) {
48        if let Some(first) = self.pipelines.first() {
49            first.pipe_redirection(working_set)
50        } else {
51            (None, None)
52        }
53    }
54}
55
56impl Default for Block {
57    fn default() -> Self {
58        Self::new()
59    }
60}
61
62impl Block {
63    pub fn new() -> Self {
64        Self {
65            signature: Box::new(Signature::new("")),
66            pipelines: vec![],
67            captures: vec![],
68            redirect_env: false,
69            ir_block: None,
70            span: None,
71            scope_bindings: None,
72            parsed_scoped: false,
73        }
74    }
75
76    pub fn new_with_capacity(capacity: usize) -> Self {
77        Self {
78            signature: Box::new(Signature::new("")),
79            pipelines: Vec::with_capacity(capacity),
80            captures: vec![],
81            redirect_env: false,
82            ir_block: None,
83            span: None,
84            scope_bindings: None,
85            parsed_scoped: false,
86        }
87    }
88
89    pub fn output_type(&self) -> Type {
90        match self.pipelines.last().and_then(|pl| pl.elements.last()) {
91            Some(pe) if pe.redirection.is_none() => pe.expr.ty.clone(),
92            Some(_) => Type::Any,
93            None => Type::Nothing,
94        }
95    }
96
97    /// Replace any `$in` variables in the initial element of pipelines within the block
98    pub fn replace_in_variable(
99        &mut self,
100        working_set: &mut StateWorkingSet<'_>,
101        new_var_id: VarId,
102    ) {
103        for pipeline in self.pipelines.iter_mut() {
104            if let Some(element) = pipeline.elements.first_mut() {
105                element.replace_in_variable(working_set, new_var_id);
106            }
107        }
108    }
109}
110
111impl<T> From<T> for Block
112where
113    T: Iterator<Item = Pipeline>,
114{
115    fn from(pipelines: T) -> Self {
116        Self {
117            signature: Box::new(Signature::new("")),
118            pipelines: pipelines.collect(),
119            captures: vec![],
120            redirect_env: false,
121            ir_block: None,
122            span: None,
123            scope_bindings: None,
124            parsed_scoped: false,
125        }
126    }
127}