use crate::cache::CodeFileCache;
use crate::model::{CodeExecutor, CodeScriptExecutionResult, CommandExecutor, LanguageScript};
use anyhow::Result;
use log::{debug, error, info};
use serde_json::Value;
use tokio::fs;
use tokio::process::Command;
pub async fn run_deno_script_with_params<F>(
code: &str,
params: Option<Value>,
timeout_seconds: Option<u64>,
lang: LanguageScript,
prepare_code_fn: F,
) -> Result<CodeScriptExecutionResult>
where
F: Fn(&str, bool) -> String,
{
debug!("开始执行{lang:?}脚本...,执行参数: {params:?}");
let hash = CodeFileCache::obtain_code_hash(code);
let cache_exist = CodeFileCache::check_code_file_cache_exisht(&hash, &lang).await;
let run_code_script_file_tuple = if cache_exist {
let cache_code = CodeFileCache::get_code_file_cache(&hash, &lang).await;
debug!("从缓存中读取代码:hash值 {:?}", hash);
cache_code?
} else {
let wrapped_code = prepare_code_fn(code, true);
CodeFileCache::save_code_file_cache(&hash, &wrapped_code, &lang).await?;
let code_script_file_tuple = CodeFileCache::get_code_file_cache(&hash, &lang).await?;
debug!("创建脚本缓存:hash值 {:?}", hash);
code_script_file_tuple
};
let temp_path = run_code_script_file_tuple.1;
let mut execute_command = Command::new("deno");
execute_command
.arg("run")
.arg("--allow-net")
.arg("--allow-env")
.arg("--allow-read")
.arg("--no-check")
.arg("--v8-flags=--max-heap-size=512")
.arg(&temp_path)
.kill_on_drop(true);
let temp_input_path = if let Some(params) = params {
let params_json = serde_json::to_string(¶ms)?;
let temp_dir = tempfile::TempDir::new()?;
let temp_file_path = temp_dir.path().join("input_params.json");
std::fs::write(&temp_file_path, params_json.as_bytes())?;
std::mem::forget(temp_dir);
execute_command.env("INPUT_JSON_FILE", &temp_file_path);
debug!("使用临时文件传递参数,文件路径: {:?}", temp_file_path);
Some(temp_file_path)
} else {
execute_command.env("INPUT_JSON", "{}");
None
};
debug!("Deno命令[{:?}]: {:?}", lang, execute_command);
let executor = match timeout_seconds {
Some(timeout) => CommandExecutor::with_timeout(execute_command.output(), timeout),
None => CommandExecutor::default(execute_command.output()),
};
info!("执行命令: {:?}", execute_command);
let executor_result = executor.await;
let output = match executor_result {
Ok(cmd_result) => match cmd_result {
Ok(output) => output,
Err(e) => {
error!("Deno命令执行失败 [{lang:?}]: {e:?}");
return Err(e.into());
}
},
Err(e) => {
error!("Deno任务执行异常 [{lang:?}]: {e:?}");
return Err(e.into());
}
};
debug!("标准输出:\n{}", String::from_utf8_lossy(&output.stdout));
debug!("错误输出:\n{}", String::from_utf8_lossy(&output.stderr));
if let Some(temp_file_path) = temp_input_path {
let _ = fs::remove_file(&temp_file_path).await;
if let Some(parent) = temp_file_path.parent() {
let _ = fs::remove_dir(parent).await;
}
debug!("已删除临时文件: {:?}", temp_file_path);
}
CodeExecutor::parse_execution_output(&output.stdout, &output.stderr).await
}