Skip to content

Commit e933431

Browse files
committed
Refactor go helper
1 parent 6afa12d commit e933431

File tree

6 files changed

+233
-262
lines changed

6 files changed

+233
-262
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
*.exe
22
*.md
33
*.bat
4+
*staticSchema.go

mof/mof.go

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
package main
2+
3+
import (
4+
"encoding/base64"
5+
"encoding/json"
6+
"fmt"
7+
"io/ioutil"
8+
"os"
9+
"strings"
10+
"github.com/qri-io/jsonschema"
11+
)
12+
13+
//go:generate go run generate-static-schema.go ../mof.schema.json
14+
15+
func main() {
16+
switch os.Args[1] {
17+
case "validate":
18+
if len(os.Args) != 3 {
19+
fmt.Println("Invalid arguments to `mof validate`")
20+
PrintHelp()
21+
}
22+
filename := os.Args[2]
23+
if err := ValidateFile(filename); err != nil {
24+
fmt.Printf("%s is not a valid MOF file\nThe error is:\n", filename)
25+
fmt.Println(err)
26+
} else {
27+
fmt.Printf("Success! %s conforms to the MathOptFormat schema", filename)
28+
}
29+
case "summarize":
30+
summary, err := SummarizeSchema()
31+
if err != nil {
32+
fmt.Println(err)
33+
} else {
34+
fmt.Println(summary)
35+
}
36+
case "help":
37+
PrintHelp()
38+
default:
39+
fmt.Println("Invalid arguments to `mof`.")
40+
PrintHelp()
41+
}
42+
}
43+
44+
func PrintHelp() {
45+
fmt.Println("mof [arg] [args...]")
46+
fmt.Println()
47+
fmt.Println("mof validate filename.json")
48+
fmt.Println(" Validate the file `filename.json` using the MathOptFormat schema")
49+
fmt.Println("mof summarize")
50+
fmt.Println(" Print a summary of the functions and sets supported by MathOptFormat")
51+
}
52+
53+
func ValidateFile(filename string) error {
54+
schemaBytes, err := base64.StdEncoding.DecodeString(jsonSchema64)
55+
if err != nil {
56+
fmt.Println("Unable to decode JSON schema")
57+
return err
58+
}
59+
rs := &jsonschema.RootSchema{}
60+
if err := json.Unmarshal(schemaBytes, rs); err != nil {
61+
fmt.Println("Unable to unmarshall schema")
62+
return err
63+
}
64+
modelData, err := ioutil.ReadFile(filename)
65+
if err != nil {
66+
fmt.Printf("Unable to read %s\n", filename)
67+
return err
68+
}
69+
if errs, _ := rs.ValidateBytes(modelData); len(errs) > 0 {
70+
fmt.Printf("Error validating file")
71+
for _, err = range errs {
72+
fmt.Println(err.Error())
73+
}
74+
return errs[0]
75+
}
76+
return nil
77+
}
78+
79+
func SummarizeSchema() (string, error) {
80+
schemaBytes, err := base64.StdEncoding.DecodeString(jsonSchema64)
81+
if err != nil {
82+
fmt.Println("Unable to decode JSON schema")
83+
return "", err
84+
}
85+
var data map[string]interface{}
86+
if err := json.Unmarshal(schemaBytes, &data); err != nil {
87+
fmt.Println("Unable to unmarshall schema")
88+
return "", err
89+
}
90+
operators, leaves := summarizeNonlinear(data)
91+
summary :=
92+
"## Sets\n\n" +
93+
"### Scalar Sets\n\n" +
94+
summarize(data, "scalar_sets") + "\n" +
95+
"### Vector Sets\n\n" +
96+
summarize(data, "vector_sets") + "\n" +
97+
"## Functions\n\n" +
98+
"### Scalar Functions\n\n" +
99+
summarize(data, "scalar_functions") + "\n" +
100+
"### Vector Functions\n\n" +
101+
summarize(data, "vector_functions") + "\n" +
102+
"### Nonlinear functions\n\n" +
103+
"#### Leaf nodes\n\n" +
104+
leaves + "\n" +
105+
"#### Operators\n\n" +
106+
operators
107+
return summary, nil
108+
}
109+
110+
type Object struct {
111+
Head string
112+
Description string
113+
Example string
114+
}
115+
116+
func oneOfToObject(data map[string]interface{}) []Object {
117+
val, ok := data["description"]
118+
var description string
119+
if ok {
120+
description = strings.ReplaceAll(val.(string), "|", "\\|")
121+
} else {
122+
description = ""
123+
}
124+
vals, ok := data["examples"]
125+
var example string
126+
if !ok {
127+
example = ""
128+
} else {
129+
example = vals.([]interface{})[0].(string)
130+
}
131+
properties := data["properties"].(map[string]interface{})
132+
head := properties["head"].(map[string]interface{})
133+
if val, ok := head["const"]; ok {
134+
return []Object{Object{
135+
Head: val.(string), Description: description, Example: example}}
136+
} else if val, ok := head["enum"]; ok {
137+
objects := []Object{}
138+
for _, name := range val.([]interface{}) {
139+
objects = append(objects, Object{
140+
Head: name.(string), Description: description, Example: example})
141+
}
142+
return objects
143+
}
144+
return []Object{}
145+
}
146+
147+
func summarize(data map[string]interface{}, key string) string {
148+
summary :=
149+
"| Name | Description | Example |\n" +
150+
"| ---- | ----------- | ------- |\n"
151+
definitions := data["definitions"].(map[string]interface{})
152+
keyData := definitions[key].(map[string]interface{})
153+
for _, oneOf := range keyData["oneOf"].([]interface{}) {
154+
for _, o := range oneOfToObject(oneOf.(map[string]interface{})) {
155+
summary = summary + fmt.Sprintf(
156+
"| `\"%s\"` | %s | %s |\n", o.Head, o.Description, o.Example)
157+
}
158+
}
159+
return summary
160+
}
161+
162+
func summarizeNonlinear(data map[string]interface{}) (string, string) {
163+
definitions := data["definitions"].(map[string]interface{})
164+
nonlinearTerm := definitions["NonlinearTerm"].(map[string]interface{})
165+
operators :=
166+
"| Name | Arity |\n" +
167+
"| ---- | ----- |\n"
168+
169+
leaves :=
170+
"| Name | Description | Example |\n" +
171+
"| ---- | ----------- | ------- |\n"
172+
173+
for _, term := range nonlinearTerm["oneOf"].([]interface{}) {
174+
oneOf := term.(map[string]interface{})
175+
objects := oneOfToObject(oneOf)
176+
switch oneOf["description"] {
177+
case "Unary operators":
178+
for _, f := range objects {
179+
operators = operators + fmt.Sprintf(
180+
"| `\"%s\"` | Unary |\n", f.Head)
181+
}
182+
case "Binary operators":
183+
for _, f := range objects {
184+
operators = operators + fmt.Sprintf(
185+
"| `\"%s\"` | Binary |\n", f.Head)
186+
}
187+
case "N-ary operators":
188+
for _, f := range objects {
189+
operators = operators + fmt.Sprintf(
190+
"| `\"%s\"` | N-ary |\n", f.Head)
191+
}
192+
default:
193+
if len(objects) == 1 {
194+
leaves = leaves + fmt.Sprintf(
195+
"| `\"%s\"` | %s | %s |\n",
196+
objects[0].Head, objects[0].Description, objects[0].Example)
197+
} else {
198+
fmt.Printf("Unsupported object: %s\n", oneOf)
199+
}
200+
}
201+
}
202+
return operators, leaves
203+
}

mof/mof_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package main
2+
3+
import (
4+
"io/ioutil"
5+
"testing"
6+
)
7+
8+
func TestExamples(t *testing.T) {
9+
files, err := ioutil.ReadDir("../examples")
10+
if err != nil {
11+
t.Errorf("%s", err)
12+
}
13+
for _, file := range files {
14+
if err := ValidateFile("../examples/" + file.Name()); err != nil {
15+
t.Errorf("%s failed to validate", file.Name())
16+
}
17+
}
18+
}
19+
20+
func TestSummary(t *testing.T) {
21+
summary, err := SummarizeSchema()
22+
if err != nil || len(summary) < 100 {
23+
t.Errorf("Failed to summarize schema: %s", err)
24+
}
25+
}
26+
27+
func TestHelp(t *testing.T) {
28+
PrintHelp()
29+
}

src/mof/mof.go

Lines changed: 0 additions & 62 deletions
This file was deleted.

0 commit comments

Comments
 (0)