AsRef<str> refactor

This commit is contained in:
Diego Imbert
2025-07-04 00:57:13 +02:00
parent aa5575da79
commit c83718a772
6 changed files with 50 additions and 67 deletions
@@ -5,41 +5,31 @@ use windmill_parser::asset_parser::{
};
use AssetUsageAccessType::*;
pub fn parse_assets<'a>(
input: &'a str,
paths_storage: &'a mut Vec<String>,
) -> anyhow::Result<Vec<ParseAssetsResult<'a>>> {
let ast = Suite::parse(&input, "main.py")
pub fn parse_assets(input: &str) -> anyhow::Result<Vec<ParseAssetsResult<String>>> {
let ast = Suite::parse(input, "main.py")
.map_err(|e| anyhow::anyhow!("Error parsing code: {}", e.to_string()))?;
let mut assets_finder = AssetsFinder { assets: vec![], paths_storage };
let mut assets_finder = AssetsFinder { assets: vec![] };
ast.into_iter()
.for_each(|stmt| assets_finder.visit_stmt(stmt));
for (asset, path) in assets_finder
.assets
.iter_mut()
.zip(assets_finder.paths_storage.iter_mut())
{
asset.path = path;
}
Ok(merge_assets(assets_finder.assets))
}
struct AssetsFinder<'a> {
assets: Vec<ParseAssetsResult<'a>>,
// We have to store paths separately because of lifetime concerns
paths_storage: &'a mut Vec<String>,
struct AssetsFinder {
assets: Vec<ParseAssetsResult<String>>,
}
impl<'a> Visitor for AssetsFinder<'a> {
impl Visitor for AssetsFinder {
// visit_call_expr will not recurse if it detects an asset,
// so this will only be called when no further context was found
fn visit_expr_constant(&mut self, node: ExprConstant) {
match node.value {
Constant::Str(s) => {
if let Some((kind, path)) = parse_asset_syntax(&s) {
self.paths_storage.push(path.to_string());
self.assets
.push(ParseAssetsResult { kind, path: "", access_type: None });
self.assets.push(ParseAssetsResult {
kind,
path: path.to_string(),
access_type: None,
});
}
}
_ => self.generic_visit_expr_constant(node),
@@ -54,7 +44,7 @@ impl<'a> Visitor for AssetsFinder<'a> {
}
}
impl<'a> AssetsFinder<'a> {
impl AssetsFinder {
fn visit_expr_call_inner(&mut self, node: &rustpython_ast::ExprCall) -> Result<(), ()> {
let ident: String = node
.func
@@ -81,9 +71,8 @@ impl<'a> AssetsFinder<'a> {
match &node.args[0] {
Expr::Constant(ExprConstant { value: Constant::Str(value), .. }) => {
let path = parse_asset_syntax(&value).map(|(_, p)| p).unwrap_or(&value);
self.paths_storage.push(path.to_string());
self.assets
.push(ParseAssetsResult { kind, path: "", access_type });
.push(ParseAssetsResult { kind, path: path.to_string(), access_type });
}
_ => return Err(()),
};
@@ -10,7 +10,7 @@ use nom::{
IResult, Parser,
};
pub fn parse_assets<'a>(input: &'a str) -> anyhow::Result<Vec<ParseAssetsResult<'a>>> {
pub fn parse_assets<'a>(input: &str) -> anyhow::Result<Vec<ParseAssetsResult<&str>>> {
let mut assets = Vec::new();
let mut remaining = input;
@@ -26,7 +26,7 @@ pub fn parse_assets<'a>(input: &'a str) -> anyhow::Result<Vec<ParseAssetsResult<
Ok(merge_assets(assets))
}
fn parse_asset(input: &str) -> IResult<&str, ParseAssetsResult> {
fn parse_asset(input: &str) -> IResult<&str, ParseAssetsResult<&str>> {
alt((
parse_s3_object_read.map(|path| ParseAssetsResult {
path,
@@ -1,5 +1,5 @@
use swc_common::{sync::Lrc, FileName, SourceMap};
use swc_ecma_ast::{CallExpr, Expr, Lit, MemberExpr, MemberProp, Str, TsLit};
use swc_ecma_ast::{CallExpr, Expr, Lit, MemberExpr, MemberProp, Str};
use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax, TsSyntax};
use swc_ecma_visit::{Visit, VisitWith};
use windmill_parser::asset_parser::{
@@ -7,10 +7,7 @@ use windmill_parser::asset_parser::{
};
use AssetUsageAccessType::*;
pub fn parse_assets<'a>(
code: &'a str,
paths_storage: &'a mut Vec<String>,
) -> anyhow::Result<Vec<ParseAssetsResult<'a>>> {
pub fn parse_assets(code: &str) -> anyhow::Result<Vec<ParseAssetsResult<String>>> {
let cm: Lrc<SourceMap> = Default::default();
let fm = cm.new_source_file(FileName::Custom("main.ts".into()).into(), code.into());
let lexer = Lexer::new(
@@ -35,34 +32,27 @@ pub fn parse_assets<'a>(
anyhow::anyhow!("Error while parsing code, it is invalid TypeScript: {err_s}, {e:?}")
})?
.body;
let mut assets_finder = AssetsFinder { assets: vec![], paths_storage };
let mut assets_finder = AssetsFinder { assets: vec![] };
assets_finder.visit_module_items(&ast);
for (asset, path) in assets_finder
.assets
.iter_mut()
.zip(assets_finder.paths_storage.iter_mut())
{
asset.path = path;
}
Ok(merge_assets(assets_finder.assets))
}
struct AssetsFinder<'a> {
assets: Vec<ParseAssetsResult<'a>>,
// We have to store paths separately because of lifetime concerns
paths_storage: &'a mut Vec<String>,
struct AssetsFinder {
assets: Vec<ParseAssetsResult<String>>,
}
impl<'a> Visit for AssetsFinder<'a> {
impl Visit for AssetsFinder {
// visit_call_expr will not recurse if it detects an asset,
// so this will only be called when no further context was found
fn visit_lit(&mut self, node: &swc_ecma_ast::Lit) {
match node {
swc_ecma_ast::Lit::Str(str) => {
if let Some((kind, path)) = parse_asset_syntax(str.value.as_str()) {
self.paths_storage.push(path.to_string());
self.assets
.push(ParseAssetsResult { kind, path: "", access_type: None });
self.assets.push(ParseAssetsResult {
kind,
path: path.to_string(),
access_type: None,
});
}
}
_ => <Lit as VisitWith<Self>>::visit_children_with(node, self),
@@ -77,7 +67,7 @@ impl<'a> Visit for AssetsFinder<'a> {
}
}
impl<'a> AssetsFinder<'a> {
impl AssetsFinder {
fn visit_call_expr_inner(&mut self, node: &swc_ecma_ast::CallExpr) -> Result<(), ()> {
let ident = match node.callee.as_expr().map(AsRef::as_ref) {
Some(Expr::Ident(i)) => i.sym.as_str(),
@@ -96,9 +86,8 @@ impl<'a> AssetsFinder<'a> {
match node.args[0].expr.as_ref() {
Expr::Lit(Lit::Str(Str { value, .. })) => {
let path = parse_asset_syntax(&value).map(|(_, p)| p).unwrap_or(&value);
self.paths_storage.push(path.to_string());
self.assets
.push(ParseAssetsResult { kind, path: "", access_type });
.push(ParseAssetsResult { kind, path: path.to_string(), access_type });
}
_ => return Err(()),
}
@@ -181,8 +181,7 @@ pub fn parse_assets_sql(code: &str) -> String {
#[cfg(feature = "ts-parser")]
#[wasm_bindgen]
pub fn parse_assets_ts(code: &str) -> String {
let mut paths_storage = vec![];
if let Ok(r) = windmill_parser_ts::parse_assets(code, &mut paths_storage) {
if let Ok(r) = windmill_parser_ts::parse_assets(code) {
return serde_json::to_string(&r).unwrap();
} else {
return "Invalid".to_string();
@@ -192,8 +191,7 @@ pub fn parse_assets_ts(code: &str) -> String {
#[cfg(feature = "py-parser")]
#[wasm_bindgen]
pub fn parse_assets_py(code: &str) -> String {
let mut paths_storage = vec![];
if let Ok(r) = windmill_parser_py::parse_assets(code, &mut paths_storage) {
if let Ok(r) = windmill_parser_py::parse_assets(code) {
return serde_json::to_string(&r).unwrap();
} else {
return "Invalid".to_string();
@@ -18,20 +18,20 @@ pub enum AssetKind {
}
#[derive(Serialize)]
pub struct ParseAssetsResult<'a> {
pub struct ParseAssetsResult<S: AsRef<str>> {
pub kind: AssetKind,
pub path: &'a str,
pub path: S,
#[serde(skip_serializing_if = "Option::is_none")]
pub access_type: Option<AssetUsageAccessType>, // None in case of ambiguity
}
pub fn merge_assets<'a>(assets: Vec<ParseAssetsResult<'a>>) -> Vec<ParseAssetsResult<'a>> {
let mut arr: Vec<ParseAssetsResult<'a>> = vec![];
pub fn merge_assets<S: AsRef<str>>(assets: Vec<ParseAssetsResult<S>>) -> Vec<ParseAssetsResult<S>> {
let mut arr: Vec<ParseAssetsResult<S>> = vec![];
for asset in assets {
// Remove duplicates
if let Some(existing) = arr
.iter_mut()
.find(|x| x.path == asset.path && x.kind == asset.kind)
.find(|x| x.path.as_ref() == asset.path.as_ref() && x.kind == asset.kind)
{
// merge access types
existing.access_type = match (asset.access_type, existing.access_type) {
@@ -45,7 +45,7 @@ pub fn merge_assets<'a>(assets: Vec<ParseAssetsResult<'a>>) -> Vec<ParseAssetsRe
arr.push(asset);
}
}
arr.sort_by_key(|a| a.path);
arr.sort_by(|a, b| a.path.as_ref().cmp(b.path.as_ref()));
arr
}
+14 -7
View File
@@ -39,16 +39,23 @@ pub struct AssetUsage {
pub access_type: AssetUsageAccessType,
}
pub fn parse_assets<'a>(
input: &'a str,
pub fn parse_assets(
input: &str,
lang: ScriptLang,
paths_storage: &'a mut Vec<String>,
) -> anyhow::Result<Option<Vec<ParseAssetsResult<'a>>>> {
) -> anyhow::Result<Option<Vec<ParseAssetsResult<String>>>> {
let r = match lang {
ScriptLang::Python3 => windmill_parser_py::parse_assets(input, paths_storage),
ScriptLang::DuckDb => windmill_parser_sql::parse_assets(input),
ScriptLang::Python3 => windmill_parser_py::parse_assets(input),
ScriptLang::DuckDb => windmill_parser_sql::parse_assets(input).map(|a| {
a.iter()
.map(|a| ParseAssetsResult {
path: a.path.to_string(),
access_type: a.access_type,
kind: a.kind,
})
.collect()
}),
ScriptLang::Deno | ScriptLang::Bun | ScriptLang::Nativets => {
windmill_parser_ts::parse_assets(input, paths_storage)
windmill_parser_ts::parse_assets(input)
}
_ => return Ok(None),
};