use crate::error::{Error, MinusOneResult};
use crate::ps::Powershell;
use crate::ps::Powershell::{Array, PSItem, Raw};
use crate::rule::RuleMut;
use crate::tree::{ControlFlow, Node, NodeMut};
fn find_previous_expr<'a>(
command: &Node<'a, Powershell>,
) -> MinusOneResult<Option<Node<'a, Powershell>>> {
let pipeline = command.parent().ok_or(Error::invalid_child())?;
let mut index = 0;
for pipeline_element in pipeline.range(Some(0), None, Some(2)) {
if &pipeline_element == command {
break;
}
index += 2; }
if index < 2 {
Ok(None)
} else {
Ok(pipeline.child(index - 2))
}
}
#[derive(Default)]
pub struct PSItemInferrator;
impl<'a> RuleMut<'a> for PSItemInferrator {
type Language = Powershell;
fn enter(
&mut self,
_node: &mut NodeMut<'a, Self::Language>,
_flow: ControlFlow,
) -> MinusOneResult<()> {
Ok(())
}
fn leave(
&mut self,
node: &mut NodeMut<'a, Self::Language>,
_flow: ControlFlow,
) -> MinusOneResult<()> {
let view = node.view();
if view.kind() == "variable" && view.text()? == "$_" {
if let Some(script_block_expression) =
view.get_parent_of_types(vec!["script_block_expression"])
{
if let Some(foreach_command) =
script_block_expression.get_parent_of_types(vec!["foreach_command"])
{
if let Some(previous) = find_previous_expr(&foreach_command.parent().unwrap())?
{
match previous.data() {
Some(Array(values)) => {
node.set(PSItem(values.clone()));
}
Some(Raw(value)) => {
node.set(PSItem(vec![value.clone()]));
}
_ => (),
}
}
}
}
}
Ok(())
}
}
#[derive(Default)]
pub struct ForEach;
impl<'a> RuleMut<'a> for ForEach {
type Language = Powershell;
fn enter(
&mut self,
_node: &mut NodeMut<'a, Self::Language>,
_flow: ControlFlow,
) -> MinusOneResult<()> {
Ok(())
}
fn leave(
&mut self,
node: &mut NodeMut<'a, Self::Language>,
_flow: ControlFlow,
) -> MinusOneResult<()> {
let view = node.view();
if view.kind() == "foreach_command"
&& view.child_count() == 2 && view.child(1).unwrap().kind() == "script_block_expression"
{
let script_block_expression = view.child(1).unwrap();
if let Some(previous_command) = find_previous_expr(&view.parent().unwrap())? {
let mut previous_values = Vec::new();
match previous_command.data() {
Some(Array(values)) => previous_values.extend(values.clone()),
Some(Raw(value)) => previous_values.push(value.clone()),
_ => (),
}
let script_block_body = script_block_expression
.child(1)
.ok_or(Error::invalid_child())? .named_child("script_block_body");
if let Some(script_block_body_node) = script_block_body {
if let Some(statement_list) =
script_block_body_node.named_child("statement_list")
{
let mut result = Vec::new();
for i in 0..previous_values.len() {
for child_statement in statement_list.iter() {
if child_statement.kind() == "empty_statement" {
continue;
}
match child_statement.data() {
Some(PSItem(values)) => {
result.push(values[i].clone());
}
Some(Raw(r)) => {
result.push(r.clone());
}
Some(Array(array_value)) => {
for v in array_value {
result.push(v.clone());
}
}
_ => {
return Ok(());
}
}
}
}
if !result.is_empty() {
node.set(Array(result));
}
}
}
}
}
Ok(())
}
}
#[cfg(test)]
mod test {
use crate::ps::array::ParseArrayLiteral;
use crate::ps::build_powershell_tree;
use crate::ps::cast::Cast;
use crate::ps::foreach::{ForEach, PSItemInferrator};
use crate::ps::forward::Forward;
use crate::ps::integer::ParseInt;
use crate::ps::string::ParseString;
use crate::ps::typing::ParseType;
use crate::ps::Powershell::Array;
use crate::ps::Value::{Num, Str};
#[test]
fn test_foreach_transparent() {
let mut tree = build_powershell_tree("(1,2,3) | % {$_}").unwrap();
tree.apply_mut(&mut (
ParseInt::default(),
Forward::default(),
ParseArrayLiteral::default(),
PSItemInferrator::default(),
ForEach::default(),
))
.unwrap();
assert_eq!(
*tree
.root()
.unwrap()
.child(0)
.unwrap()
.child(0)
.unwrap()
.data()
.expect("Inferred type"),
Array(vec![Num(1), Num(2), Num(3)])
);
}
#[test]
fn test_foreach_transparent_with_mixed_array() {
let mut tree = build_powershell_tree("(\"a\",2,3) | % {$_}").unwrap();
tree.apply_mut(&mut (
ParseInt::default(),
Forward::default(),
ParseString::default(),
ParseArrayLiteral::default(),
PSItemInferrator::default(),
ForEach::default(),
))
.unwrap();
assert_eq!(
*tree
.root()
.unwrap()
.child(0)
.unwrap()
.child(0)
.unwrap()
.data()
.expect("Inferred type"),
Array(vec![Str("a".to_string()), Num(2), Num(3)])
);
}
#[test]
fn test_foreach_transparent_with_one_element() {
let mut tree = build_powershell_tree("(1) | % {$_}").unwrap();
tree.apply_mut(&mut (
ParseInt::default(),
Forward::default(),
ParseString::default(),
ParseArrayLiteral::default(),
PSItemInferrator::default(),
ForEach::default(),
))
.unwrap();
assert_eq!(
*tree
.root()
.unwrap()
.child(0)
.unwrap()
.child(0)
.unwrap()
.data()
.expect("Inferred type"),
Array(vec![Num(1)])
);
}
#[test]
fn test_foreach_cast_with_one_element() {
let mut tree = build_powershell_tree("(0x61) | % {[char]$_}").unwrap();
tree.apply_mut(&mut (
ParseInt::default(),
Forward::default(),
ParseString::default(),
ParseArrayLiteral::default(),
PSItemInferrator::default(),
ForEach::default(),
Cast::default(),
ParseType::default(),
))
.unwrap();
assert_eq!(
*tree
.root()
.unwrap()
.child(0)
.unwrap()
.child(0)
.unwrap()
.data()
.expect("Inferred type"),
Array(vec![Str("a".to_string())])
);
}
#[test]
fn test_foreach_cast_with_array() {
let mut tree = build_powershell_tree("(0x61, 0x62) | % {[char]$_}").unwrap();
tree.apply_mut(&mut (
ParseInt::default(),
Forward::default(),
ParseString::default(),
ParseArrayLiteral::default(),
PSItemInferrator::default(),
ForEach::default(),
Cast::default(),
ParseType::default(),
))
.unwrap();
assert_eq!(
*tree
.root()
.unwrap()
.child(0)
.unwrap()
.child(0)
.unwrap()
.data()
.expect("Inferred type"),
Array(vec![Str("a".to_string()), Str("b".to_string())])
);
}
#[test]
fn test_foreach_cast_with_array_and_static_result() {
let mut tree = build_powershell_tree("(0x61, 0x62) | % {'z'; [char]$_}").unwrap();
tree.apply_mut(&mut (
ParseInt::default(),
Forward::default(),
ParseString::default(),
ParseArrayLiteral::default(),
PSItemInferrator::default(),
ForEach::default(),
Cast::default(),
ParseType::default(),
))
.unwrap();
assert_eq!(
*tree
.root()
.unwrap()
.child(0)
.unwrap()
.child(0)
.unwrap()
.data()
.expect("Inferred type"),
Array(vec![
Str("z".to_string()),
Str("a".to_string()),
Str("z".to_string()),
Str("b".to_string())
])
);
}
}