-
Notifications
You must be signed in to change notification settings - Fork 2
/
exec.go
146 lines (122 loc) · 3.97 KB
/
exec.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package main
import (
"bytes"
"encoding/json"
"errors"
"io"
"net/http"
piston "github.com/milindmadhukar/go-piston"
)
// TODO: make this configurable
const USERAGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36"
// TODO: if this is well written, this should be ported to its own package/wrapper
// POST /api/v2/execute
type ExecuteRequest struct {
Language string `json:"language"` // required, language of code
Version string `json:"version"` // required, language of version
Files []File `json:"files"` // required, files of code
Stdin string `json:"stdin"` // input to code
Args []string `json:"args"` // program arguments
CompileTimeout int `json:"compile_timeout"` // max time for compiling; default: 10000 MS
RunTimeout int `json:"run_timeout"` // max run time; default: 3000 MS
CompileMemoryLimit int `json:"compile_memory_limit"` // max memory for compile: -1
RunMemoryLimit int `json:"run_memory_limit"` // max memory for run; default: -1
}
type ExecuteResponse struct {
Language string `json:"language"`
Version string `json:"version"`
Run ExecuteResults `json:"run"`
Message string `json:"message"` // means something bad happened...
}
type ExecuteResults struct {
Stdout string `json:"stdout"`
Stderr string `json:"stderr"`
Output string `json:"output"`
Code int `json:"code"`
Signal string `json:"signal"` // what is this??
}
type File struct {
Name string `json:"name"` // name of upload
Content string `json:"content"` // required, content of file
Encoding string `json:"encoding"` // encoding used; default: utf8; options base64, hex
}
// TODO: runtime endpoints
func Exec(lang string, version string, code string) (string, error) {
execRequest := ExecuteRequest{
Language: lang,
Version: version,
Files: []File{
{
Content: code,
},
},
}
if version == "" {
latest, err := GetLatestVersion(lang)
if err != nil {
return "", err
}
execRequest.Version = latest
}
body, err := json.Marshal(execRequest)
if err != nil {
return "", err
}
res, err := Request("POST", PISTON_URL+"execute", bytes.NewBuffer(body))
if err != nil {
return "", err
}
defer res.Body.Close()
var results ExecuteResponse
decoder := json.NewDecoder(res.Body)
// decoder.DisallowUnknownFields()
err = decoder.Decode(&results)
return results.Run.Output, err
}
func GetRuntimes() (*piston.Runtimes, error) {
httpClient := http.DefaultClient
client := piston.New("", httpClient, PISTON_URL)
runtimes, err := client.GetRuntimes()
if err != nil {
return nil, err
}
return runtimes, nil
}
// TODO: there should be a static list of runtimes which the bot refers to; any issues if the runtimes change??
func GetLatestVersion(language string) (string, error) {
runtimes, err := GetRuntimes()
if err != nil {
return "", err
}
if runtimes == nil {
return "", errors.New("no runtimes found")
}
for _, runtime := range *runtimes {
if language == runtime.Language || isPresent(runtime.Aliases, language) {
return runtime.Version, nil
}
}
return "", errors.New("Could not find a version for the language " + language)
}
// "Borrowed" from that piston wrapper library
// Returns a boolean value checking if a string is found in the slice or not.
func isPresent(slice []string, val string) bool {
for _, item := range slice {
if item == val {
return true
}
}
return false
}
func Request(method string, path string, body io.Reader) (*http.Response, error) {
client := &http.Client{}
req, err := http.NewRequest(method, path, body)
if err != nil {
return nil, err
}
req.Header.Set("User-Agent", USERAGENT)
req.Header.Add("Accept", "application/json")
req.Header.Add("Content-Type", "application/json")
res, err := client.Do(req)
return res, err
}