初始化提交

This commit is contained in:
chenxin committed 2026-08-21 16:00:13 +08:00
commit 887c1f449e
963 files changed
+166869

No files matched your search

+79
View File
@@ -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 后文件应删除");
}
}