mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-08 00:03:07 +00:00
fix(parsers): more robust pwsh param parsing (#4884)
This commit is contained in:
@@ -9,7 +9,7 @@ use regex_lite::Regex;
|
||||
|
||||
use serde_json::json;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
use windmill_parser::{Arg, MainArgSignature, Typ};
|
||||
|
||||
pub fn parse_bash_sig(code: &str) -> anyhow::Result<MainArgSignature> {
|
||||
@@ -84,36 +84,94 @@ fn parse_bash_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
|
||||
Ok(Some(args))
|
||||
}
|
||||
|
||||
enum ParserState {
|
||||
Normal,
|
||||
InSingleQuote,
|
||||
InDoubleQuote,
|
||||
}
|
||||
fn split_pwsh_args(code: &str) -> Vec<&str> {
|
||||
let mut chars = code.char_indices().peekable();
|
||||
let mut state = ParserState::Normal;
|
||||
let mut splits = vec![];
|
||||
let mut last_idx = 0;
|
||||
while let Some((idx, char)) = chars.next() {
|
||||
match (&state, char) {
|
||||
(ParserState::Normal, '\'') => {
|
||||
state = ParserState::InSingleQuote;
|
||||
}
|
||||
(ParserState::Normal, '"') => {
|
||||
state = ParserState::InDoubleQuote;
|
||||
}
|
||||
(ParserState::InSingleQuote, '\'') => {
|
||||
state = ParserState::Normal;
|
||||
}
|
||||
(ParserState::InDoubleQuote, '"') => {
|
||||
state = ParserState::Normal;
|
||||
}
|
||||
(ParserState::Normal, ',') => {
|
||||
splits.push(&code[last_idx..idx]);
|
||||
last_idx = idx + 1; // skip the comma
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if last_idx < code.len() {
|
||||
splits.push(&code[last_idx..]);
|
||||
}
|
||||
|
||||
splits
|
||||
}
|
||||
|
||||
fn parse_powershell_file(code: &str) -> anyhow::Result<Option<Vec<Arg>>> {
|
||||
let param_wrapper = RE_POWERSHELL_PARAM.captures(code);
|
||||
let mut args = vec![];
|
||||
if let Some(param_wrapper) = param_wrapper {
|
||||
let param_wrapper = param_wrapper.get(1).unwrap().as_str();
|
||||
for cap in RE_POWERSHELL_ARGS.captures_iter(param_wrapper) {
|
||||
let typ = cap
|
||||
.get(1)
|
||||
.map(|x| x.as_str().to_string())
|
||||
.unwrap_or("string".to_string());
|
||||
let name = cap.get(2).unwrap().as_str().to_string();
|
||||
let default = cap
|
||||
.get(3)
|
||||
.or(cap.get(4))
|
||||
.map(|x| json!(x.as_str().to_string()));
|
||||
let params = split_pwsh_args(param_wrapper);
|
||||
for param in params {
|
||||
if let Some(cap) = RE_POWERSHELL_ARGS.captures(param) {
|
||||
let typ = cap.get(1).map(|x| x.as_str().to_string());
|
||||
let name = cap.get(2).unwrap().as_str().to_string();
|
||||
|
||||
args.push(Arg {
|
||||
name: name,
|
||||
typ: match typ.as_str() {
|
||||
"string" => Typ::Str(None),
|
||||
"int" | "long" => Typ::Int,
|
||||
"decimal" | "double" | "single" => Typ::Float,
|
||||
"datetime" | "DateTime" => Typ::Datetime,
|
||||
_ => Typ::Str(None),
|
||||
},
|
||||
default: default.clone(),
|
||||
otyp: None,
|
||||
has_default: default.is_some(),
|
||||
oidx: None,
|
||||
});
|
||||
let mut parsed_typ = if let Some(typ) = typ {
|
||||
match typ.as_str() {
|
||||
"string" => Some(Typ::Str(None)),
|
||||
"int" | "long" => Some(Typ::Int),
|
||||
"decimal" | "double" | "single" => Some(Typ::Float),
|
||||
"datetime" | "DateTime" => Some(Typ::Datetime),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let default = if let Some(x) = cap.get(3) {
|
||||
Some(json!(x.as_str().to_string()))
|
||||
} else if let Some(x) = cap.get(4) {
|
||||
if parsed_typ.is_none() {
|
||||
if x.as_str().parse::<i64>().is_ok() {
|
||||
parsed_typ = Some(Typ::Int);
|
||||
} else if x.as_str().parse::<f64>().is_ok() {
|
||||
parsed_typ = Some(Typ::Float);
|
||||
}
|
||||
}
|
||||
serde_json::Number::from_str(x.as_str())
|
||||
.ok()
|
||||
.map(serde_json::Value::Number)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
args.push(Arg {
|
||||
name: name,
|
||||
typ: parsed_typ.unwrap_or(Typ::Str(None)),
|
||||
default: default.clone(),
|
||||
otyp: None,
|
||||
has_default: default.is_some(),
|
||||
oidx: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Some(args))
|
||||
@@ -192,4 +250,77 @@ non_required="${5:-}"
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_powershell_sig() -> anyhow::Result<()> {
|
||||
let code = r#"param($Msg, [string]$Msg2, $Dflt = "default value, with comma", [int]$Nb = 3 , $Nb2 = 5.0, $Nb3 = 5, $Wahoo = $env:WAHOO)"#;
|
||||
assert_eq!(
|
||||
parse_powershell_sig(code)?,
|
||||
MainArgSignature {
|
||||
star_args: false,
|
||||
star_kwargs: false,
|
||||
args: vec![
|
||||
Arg {
|
||||
otyp: None,
|
||||
name: "Msg".to_string(),
|
||||
typ: Typ::Str(None),
|
||||
default: None,
|
||||
has_default: false,
|
||||
oidx: None
|
||||
},
|
||||
Arg {
|
||||
otyp: None,
|
||||
name: "Msg2".to_string(),
|
||||
typ: Typ::Str(None),
|
||||
default: None,
|
||||
has_default: false,
|
||||
oidx: None
|
||||
},
|
||||
Arg {
|
||||
otyp: None,
|
||||
name: "Dflt".to_string(),
|
||||
typ: Typ::Str(None),
|
||||
default: Some(json!("default value, with comma")),
|
||||
has_default: true,
|
||||
oidx: None
|
||||
},
|
||||
Arg {
|
||||
otyp: None,
|
||||
name: "Nb".to_string(),
|
||||
typ: Typ::Int,
|
||||
default: Some(json!(3)),
|
||||
has_default: true,
|
||||
oidx: None
|
||||
},
|
||||
Arg {
|
||||
otyp: None,
|
||||
name: "Nb2".to_string(),
|
||||
typ: Typ::Float,
|
||||
default: Some(json!(5.0)),
|
||||
has_default: true,
|
||||
oidx: None
|
||||
},
|
||||
Arg {
|
||||
otyp: None,
|
||||
name: "Nb3".to_string(),
|
||||
typ: Typ::Int,
|
||||
default: Some(json!(5)),
|
||||
has_default: true,
|
||||
oidx: None
|
||||
},
|
||||
Arg {
|
||||
otyp: None,
|
||||
name: "Wahoo".to_string(),
|
||||
typ: Typ::Str(None),
|
||||
default: None,
|
||||
has_default: false,
|
||||
oidx: None
|
||||
}
|
||||
],
|
||||
no_main_func: None,
|
||||
has_preprocessor: None
|
||||
}
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+4
-4
@@ -72,7 +72,7 @@
|
||||
"windmill-parser-wasm-go": "^1.429.0",
|
||||
"windmill-parser-wasm-php": "^1.429.0",
|
||||
"windmill-parser-wasm-py": "^1.429.0",
|
||||
"windmill-parser-wasm-regex": "^1.429.0",
|
||||
"windmill-parser-wasm-regex": "^1.435.2",
|
||||
"windmill-parser-wasm-rust": "^1.429.0",
|
||||
"windmill-parser-wasm-ts": "^1.429.0",
|
||||
"windmill-parser-wasm-yaml": "^1.429.0",
|
||||
@@ -13660,9 +13660,9 @@
|
||||
"integrity": "sha512-cqc+tblQVHVrc8wNA4esVYD1Dv59XQJ4mHXFFPa8Lx5UjXv7FO/0VQxyQuRyXaP/V6J6DDy0oeN107eFNLxrBg=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-regex": {
|
||||
"version": "1.429.0",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-regex/-/windmill-parser-wasm-regex-1.429.0.tgz",
|
||||
"integrity": "sha512-rjgrgSYjEBU2+RB6ffFR+nmvEJdiqVKYiwugzHQE+VgVTYMJ0jLBE370n5dBjDj5KPsF+Sfmj6DIUK69uu9wSA=="
|
||||
"version": "1.435.2",
|
||||
"resolved": "https://registry.npmjs.org/windmill-parser-wasm-regex/-/windmill-parser-wasm-regex-1.435.2.tgz",
|
||||
"integrity": "sha512-kQShKPNjXok8jV7b7llfvKhypHfqePwiKgTHm8MlzBt4BZC87xrQ7TH0eS9h7OSK9Q98H9fIiUr2AbN8zTCraA=="
|
||||
},
|
||||
"node_modules/windmill-parser-wasm-rust": {
|
||||
"version": "1.429.0",
|
||||
|
||||
@@ -128,8 +128,8 @@
|
||||
"ol": "^7.4.0",
|
||||
"openai": "^4.57.2",
|
||||
"p-limit": "^6.1.0",
|
||||
"pdfjs-dist": "4.8.69",
|
||||
"panzoom": "^9.4.3",
|
||||
"pdfjs-dist": "4.8.69",
|
||||
"quill": "^1.3.7",
|
||||
"rfc4648": "^1.5.3",
|
||||
"svelte-carousel": "^1.0.25",
|
||||
@@ -145,7 +145,7 @@
|
||||
"windmill-parser-wasm-go": "^1.429.0",
|
||||
"windmill-parser-wasm-php": "^1.429.0",
|
||||
"windmill-parser-wasm-py": "^1.429.0",
|
||||
"windmill-parser-wasm-regex": "^1.429.0",
|
||||
"windmill-parser-wasm-regex": "^1.435.2",
|
||||
"windmill-parser-wasm-rust": "^1.429.0",
|
||||
"windmill-parser-wasm-ts": "^1.429.0",
|
||||
"windmill-parser-wasm-yaml": "^1.429.0",
|
||||
|
||||
Reference in New Issue
Block a user