feat: multiple code execute commands; rust code support (#104)

This commit is contained in:
darmiel
2021-10-18 01:10:04 +02:00
committed by Maas Lalani
parent 99c32cc8fc
commit a065914f73
4 changed files with 104 additions and 20 deletions

View File

@@ -5,7 +5,9 @@ import (
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"time"
)
@@ -88,21 +90,54 @@ func Execute(code Block) Result {
}
}
cmd := exec.Command(language.Command[0], append(language.Command[1:], f.Name())...)
var (
output strings.Builder
exitCode int
)
// replacer for commands
repl := strings.NewReplacer(
"<file>", f.Name(),
// <name>: file name without extension and without path
"<name>", filepath.Base(strings.TrimSuffix(f.Name(), filepath.Ext(f.Name()))),
"<path>", filepath.Dir(f.Name()),
)
// For accuracy of program execution speed, we can't put anything after
// recording the start time or before recording the end time.
start := time.Now()
out, err := cmd.Output()
end := time.Now()
exitCode := 0
if err != nil {
exitCode = 1
var cmds multi
switch t := language.Cmds.(type) {
case base:
cmds = multi{append(t, "<file>")}
case single:
cmds = multi{t}
case multi:
cmds = t
}
for _, c := range cmds {
// replace <file>, <name> and <path> in commands
for i, v := range c {
c[i] = repl.Replace(v)
}
// execute and write output
cmd := exec.Command(c[0], c[1:]...)
out, err := cmd.Output()
output.Write(out)
// update status code
if err != nil {
exitCode = 1
}
}
end := time.Now()
return Result{
Out: string(out),
Out: output.String(),
ExitCode: exitCode,
ExecutionTime: end.Sub(start),
}

View File

@@ -11,6 +11,16 @@ func TestExecute(t *testing.T) {
block code.Block
expected code.Result
}{
{
block: code.Block{
Code: `fn main() { println!("Hello, world!"); }`,
Language: code.Rust,
},
expected: code.Result{
Out: "Hello, world!\n",
ExitCode: 0,
},
},
{
block: code.Block{
Code: `puts "Hello, world!"`,
@@ -107,7 +117,8 @@ func main() {
}
r := code.Execute(tc.block)
if r.Out != tc.expected.Out {
t.Fatalf("invalid output, got %s, want %s", r.Out, tc.expected.Out)
t.Fatalf("invalid output for lang %s, got %s, want %s | %+v",
tc.block.Language, r.Out, tc.expected.Out, r)
}
if r.ExitCode != tc.expected.ExitCode {

View File

@@ -1,8 +1,23 @@
package code
// base: A filename is appended to the end of the arguments.
// Example: base{"py"} -> `py test.py`
// Placeholders <file>, <name> and <path> can be used.
type base []string
// single: Same as base, except that the filename is not appended.
type single []string
// multi: Multiple commands; placeholders can be used
type multi [][]string
// ----
type Language struct {
Extension string
Command []string
// Commands [][]string // placeholders: <name> file name (without extension),
// <file> file name, <path> path without file name
Cmds interface{} // type of multi, single, base
}
// Supported Languages
@@ -15,39 +30,51 @@ const (
Perl = "perl"
Python = "python"
Ruby = "ruby"
Rust = "rust"
)
var Languages = map[string]Language{
Bash: {
Extension: "sh",
Command: []string{"bash"},
Cmds: base{"bash"},
},
Elixir: {
Extension: "exs",
Command: []string{"elixir"},
Cmds: base{"elixir"},
},
Go: {
Extension: "go",
Command: []string{"go", "run"},
Cmds: base{"go", "run"},
},
Javascript: {
Extension: "js",
Command: []string{"node"},
Cmds: base{"node"},
},
Lua: {
Extension: "lua",
Command: []string{"lua"},
Cmds: base{"lua"},
},
Ruby: {
Extension: "rb",
Command: []string{"ruby"},
Cmds: base{"ruby"},
},
Python: {
Extension: "py",
Command: []string{"python"},
Cmds: base{"python"},
},
Perl: {
Extension: "pl",
Command: []string{"perl"},
Cmds: base{"pl"},
},
Rust: {
Extension: "rs",
Cmds: multi{
// compile code
{"rustc", "<file>", "-o", "<path>/<name>.run"},
// grant execute permissions
{"chmod", "+x", "<path>/<name>.run"},
// run compiled file
{"<path>/<name>.run"},
},
},
}