初始化提交
This commit is contained in:
commit
887c1f449e
963 files changed
+166869
No files matched your search
@@ -0,0 +1,79 @@
|
||||
//! code 模式转译:language + code → argv + 临时脚本文件。
|
||||
//!
|
||||
//! code 模式(`/v1/exec` 的 `code`/`language` 字段)由本模块转译为 argv,复用现有进程执行路径。
|
||||
//! 走脚本文件而非 `-c`,避免多行 / 模块导入 / shebang 问题;脚本经 `tempfile` 写入 OS 临时目录,
|
||||
//! 返回的 `CodeTranslation.script_path`(`TempPath`)由调用方持有至执行结束——drop 时自动删除。
|
||||
//! 流式执行场景须把 `CodeTranslation` move 进 SSE 闭包,确保子进程 open 脚本前文件不被删。
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
/// code 模式转译结果:argv 用于执行,script_path 须由调用方持有至执行结束。
|
||||
pub struct CodeTranslation {
|
||||
pub argv: Vec<String>,
|
||||
/// 临时脚本路径,持有至执行结束(RAII:drop 时自动删除)
|
||||
#[allow(dead_code)]
|
||||
pub script_path: tempfile::TempPath,
|
||||
}
|
||||
|
||||
/// 语言 → (interpreter, 脚本扩展名)。P0 仅支持 python;node/java 留 P2。
|
||||
pub fn interpreter_for(language: &str) -> Result<(&'static str, &'static str), String> {
|
||||
match language {
|
||||
"python" => Ok(("python3", "py")),
|
||||
other => Err(format!("不支持的语言: {other}(P0 仅支持 python)")),
|
||||
}
|
||||
}
|
||||
|
||||
/// 写 code 到临时脚本文件,返回 argv 与 TempPath(调用方持有至执行结束)。
|
||||
pub async fn translate(language: &str, code: &str) -> Result<CodeTranslation, String> {
|
||||
let (interp, ext) = interpreter_for(language)?;
|
||||
// NamedTempFile 是同步 IO,文件小、写一次,无需 tokio 化。
|
||||
let mut tmp = NamedTempFile::with_suffix(format!(".{ext}"))
|
||||
.map_err(|e| format!("创建临时脚本失败: {e}"))?;
|
||||
tmp.write_all(code.as_bytes())
|
||||
.map_err(|e| format!("写入临时脚本失败: {e}"))?;
|
||||
// detach 成 TempPath:文件留在磁盘,TempPath drop 时删除。
|
||||
let script_path = tmp.into_temp_path();
|
||||
let argv = vec![
|
||||
interp.to_string(),
|
||||
script_path.to_string_lossy().into_owned(),
|
||||
];
|
||||
Ok(CodeTranslation { argv, script_path })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn interpreter_for_python_ok_others_rejected() {
|
||||
assert_eq!(interpreter_for("python").unwrap(), ("python3", "py"));
|
||||
assert!(interpreter_for("node").is_err());
|
||||
assert!(interpreter_for("").is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn translate_python_writes_script_and_returns_argv() {
|
||||
let code = "print(1+1)\nimport sys\nprint(sys.argv)";
|
||||
let trans = translate("python", code).await.unwrap();
|
||||
assert_eq!(trans.argv[0], "python3");
|
||||
assert!(trans.argv[1].ends_with(".py"));
|
||||
let on_disk = std::fs::read_to_string(&trans.argv[1]).unwrap();
|
||||
assert_eq!(on_disk, code);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn translate_unsupported_language_errors() {
|
||||
assert!(translate("ruby", "puts 1").await.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn translate_temp_path_keeps_file_until_drop() {
|
||||
let trans = translate("python", "x = 1").await.unwrap();
|
||||
let path = trans.argv[1].clone();
|
||||
assert!(std::path::Path::new(&path).exists(), "持有 TempPath 时文件应存在");
|
||||
drop(trans);
|
||||
assert!(!std::path::Path::new(&path).exists(), "TempPath drop 后文件应删除");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user