package executor import ( "context" "fmt" "os" "path/filepath" "strings" "time" "RYP/backend/compiler/models" "RYP/backend/compiler/sandbox" ) const ( defaultPythonImage = "python-runner:latest" defaultCppImage = "cpp-runner:latest" defaultJavaImage = "java-runner:latest" ) // Execute runs the code in the specified language using Docker sandboxing. func Execute(req models.RunRequest) (*models.ExecutionResult, error) { // Create temporary directory for this execution tmpDir, err := os.MkdirTemp("", "ryp_sandbox_*") if err != nil { return nil, fmt.Errorf("failed to create temp directory: %w", err) } // Defer cleanup of the temporary directory defer os.RemoveAll(tmpDir) containerDir := "/sandbox" ctx := context.Background() var result *sandbox.ExecutionResult var execErr error switch normalizeLanguage(req.Language) { case "python": // Write python code to main.py scriptPath := filepath.Join(tmpDir, "main.py") if err := os.WriteFile(scriptPath, []byte(req.Code), 0644); err != nil { return nil, fmt.Errorf("failed to write python file: %w", err) } result, execErr = sandbox.RunWithDocker( ctx, runnerImage("RYP_PYTHON_IMAGE", defaultPythonImage), tmpDir, containerDir, 3*time.Second, []string{"python", "main.py"}, req.CustomInput, ) case "cpp": // Write C++ code to main.cpp srcPath := filepath.Join(tmpDir, "main.cpp") if err := os.WriteFile(srcPath, []byte(req.Code), 0644); err != nil { return nil, fmt.Errorf("failed to write cpp file: %w", err) } // Compile compResult, err := sandbox.RunWithDocker( ctx, runnerImage("RYP_CPP_IMAGE", defaultCppImage), tmpDir, containerDir, 5*time.Second, []string{"g++", "main.cpp", "-o", "main"}, "", ) if err != nil { return nil, fmt.Errorf("compile error: %w", err) } if compResult.ExitCode != 0 { // Compilation failed return &models.ExecutionResult{ Stdout: compResult.Stdout, Stderr: compResult.Stderr, ExitCode: compResult.ExitCode, DurationMs: compResult.DurationMs, }, nil } // Execute result, execErr = sandbox.RunWithDocker( ctx, runnerImage("RYP_CPP_IMAGE", defaultCppImage), tmpDir, containerDir, 3*time.Second, []string{"./main"}, req.CustomInput, ) case "java": // Write Java code to Main.java srcPath := filepath.Join(tmpDir, "Main.java") if err := os.WriteFile(srcPath, []byte(req.Code), 0644); err != nil { return nil, fmt.Errorf("failed to write java file: %w", err) } // Compile compResult, err := sandbox.RunWithDocker( ctx, runnerImage("RYP_JAVA_IMAGE", defaultJavaImage), tmpDir, containerDir, 5*time.Second, []string{"javac", "Main.java"}, "", ) if err != nil { return nil, fmt.Errorf("compile error: %w", err) } if compResult.ExitCode != 0 { // Compilation failed return &models.ExecutionResult{ Stdout: compResult.Stdout, Stderr: compResult.Stderr, ExitCode: compResult.ExitCode, DurationMs: compResult.DurationMs, }, nil } // Execute result, execErr = sandbox.RunWithDocker( ctx, runnerImage("RYP_JAVA_IMAGE", defaultJavaImage), tmpDir, containerDir, 3*time.Second, []string{"java", "Main"}, req.CustomInput, ) default: return nil, fmt.Errorf("unsupported language: %s", req.Language) } if execErr != nil { return nil, fmt.Errorf("execution failed: %w", execErr) } return &models.ExecutionResult{ Stdout: result.Stdout, Stderr: result.Stderr, ExitCode: result.ExitCode, TimeLimitExceeded: result.TimeLimitExceeded, DurationMs: result.DurationMs, }, nil } func normalizeLanguage(language string) string { switch strings.ToLower(strings.TrimSpace(language)) { case "py", "python3": return "python" case "c++", "cpp": return "cpp" default: return strings.ToLower(strings.TrimSpace(language)) } } func runnerImage(envName, fallback string) string { value := strings.TrimSpace(os.Getenv(envName)) if value == "" { return fallback } return value }