Skip to content

Commit b753425

Browse files
authored
Merge pull request #49 from argonui/xml
Enable bundle & unbundling of XML
2 parents 60feee4 + 2dd822e commit b753425

17 files changed

+1887
-184
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ func Unbundle(rawlua string) (string, error) {
145145
}
146146

147147
// Bundle grabs all dependencies and creates a single luascript
148-
func Bundle(rawlua string, l file.LuaReader) (string, error) {
148+
func Bundle(rawlua string, l file.TextReader) (string, error) {
149149
if IsBundled(rawlua) {
150150
return rawlua, nil
151151
}

bundler/xmlbundler.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package bundler
2+
3+
import (
4+
"ModCreator/file"
5+
"fmt"
6+
"regexp"
7+
"strings"
8+
)
9+
10+
// BundleXML converts <Include ... >'s into full xml
11+
func BundleXML(rawxml string, xr file.TextReader) (string, error) {
12+
lines := strings.Split(rawxml, "\n")
13+
final := []string{}
14+
inctag := regexp.MustCompile(`(?m)^(.*)<Include src="(.*)"/>`)
15+
16+
for _, v := range lines {
17+
if !inctag.Match([]byte(v)) {
18+
final = append(final, v)
19+
continue
20+
}
21+
subs := inctag.FindSubmatch([]byte(v))
22+
indent := string(subs[1])
23+
name := string(subs[2])
24+
25+
replacement := fmt.Sprintf("%s<!-- include %s -->", indent, name)
26+
final = append(final, replacement)
27+
28+
fname := name
29+
if !strings.HasSuffix(name, ".xml") {
30+
fname = name + ".xml"
31+
}
32+
incXMLRaw, err := xr.EncodeFromFile(fname)
33+
if err != nil {
34+
return "", fmt.Errorf("EncodeFromFile(%s): %v", fname, err)
35+
}
36+
incXMLBundled, err := BundleXML(incXMLRaw, xr)
37+
if err != nil {
38+
return "", fmt.Errorf("BundleXML(<%s>): %v", fname, err)
39+
}
40+
final = append(final, indentString(incXMLBundled, indent))
41+
42+
final = append(final, replacement)
43+
44+
}
45+
return strings.Join(final, "\n"), nil
46+
}
47+
48+
func indentString(s string, indent string) string {
49+
lines := strings.Split(s, "\n")
50+
final := []string{}
51+
for _, v := range lines {
52+
if v == "" {
53+
final = append(final, "")
54+
continue
55+
}
56+
final = append(final, fmt.Sprintf("%s%s", indent, v))
57+
}
58+
59+
return strings.Join(final, "\n")
60+
}
61+
62+
// UnbundleAllXML converts a bundled xml file to mapping of filenames to
63+
// contents
64+
func UnbundleAllXML(rawxml string) (map[string]string, error) {
65+
type inc struct {
66+
name string
67+
start int
68+
}
69+
store := map[string]string{}
70+
inctag := regexp.MustCompile(`(?m)^(.*?)<!-- include (.*) -->`)
71+
stack := []inc{}
72+
xmlarray := strings.Split(rawxml, "\n")
73+
74+
for ln := 0; ln < len(xmlarray); ln++ {
75+
val := xmlarray[ln]
76+
if !inctag.Match([]byte(val)) {
77+
continue
78+
}
79+
submatches := inctag.FindSubmatch([]byte(val))
80+
key := string(submatches[2])
81+
if len(stack) > 0 && stack[0].name == key {
82+
// found end of include, process it
83+
indent := string(submatches[1])
84+
indentedvals := xmlarray[stack[0].start+1 : ln]
85+
store[stack[0].name] = unindentAndJoin(indentedvals, indent)
86+
87+
insertLine := fmt.Sprintf("%s<Include src=\"%s\"/>", indent, stack[0].name)
88+
// remove the include from xmlarray
89+
tmp := append(xmlarray[:stack[0].start], insertLine)
90+
xmlarray = append(tmp, xmlarray[ln+1:]...)
91+
ln = stack[0].start
92+
stack = stack[1:]
93+
} else {
94+
stack = append([]inc{inc{name: key, start: ln}}, stack...)
95+
}
96+
}
97+
if len(stack) != 0 {
98+
return nil, fmt.Errorf("Bundled xml left after finished reading file: %v", stack)
99+
}
100+
store[Rootname] = unindentAndJoin(xmlarray, "")
101+
return store, nil
102+
}
103+
104+
func unindentAndJoin(raw []string, indent string) string {
105+
ret := []string{}
106+
for _, v := range raw {
107+
ret = append(ret, strings.Replace(v, indent, "", 1))
108+
}
109+
return strings.Join(ret, "\n")
110+
}

bundler/xmlbundler_test.go

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package bundler
2+
3+
import (
4+
"ModCreator/tests"
5+
"testing"
6+
7+
"github.com/google/go-cmp/cmp"
8+
)
9+
10+
func TestUnbundleXML(t *testing.T) {
11+
input := `<!-- include Main -->
12+
<Canvas raycastTarget="false">
13+
<Defaults>
14+
<Button color="a"/>
15+
</Defaults>
16+
17+
<!-- include ui/CameraControl -->
18+
<Panel id="CameraControl"
19+
visibility="false"
20+
</Panel>
21+
22+
<!-- include ui/CameraControl -->
23+
<!-- include ui/Shop -->
24+
<Panel id="shop.window" class="drag"
25+
offsetXY="480 -70"
26+
width="260" height="500">
27+
<!-- include deep -->
28+
<Button id="deepinclude">
29+
</Button>
30+
<!-- include deep -->
31+
</Panel>
32+
<!-- include ui/Shop -->
33+
34+
</Canvas>
35+
36+
<!-- include Main -->
37+
`
38+
39+
want := map[string]string{
40+
"__root": `<Include src="Main"/>
41+
`,
42+
"Main": `<Canvas raycastTarget="false">
43+
<Defaults>
44+
<Button color="a"/>
45+
</Defaults>
46+
47+
<Include src="ui/CameraControl"/>
48+
<Include src="ui/Shop"/>
49+
50+
</Canvas>
51+
`,
52+
"deep": `<Button id="deepinclude">
53+
</Button>`,
54+
"ui/Shop": `<Panel id="shop.window" class="drag"
55+
offsetXY="480 -70"
56+
width="260" height="500">
57+
<Include src="deep"/>
58+
</Panel>`,
59+
"ui/CameraControl": `<Panel id="CameraControl"
60+
visibility="false"
61+
</Panel>
62+
`,
63+
}
64+
65+
got, err := UnbundleAllXML(input)
66+
if err != nil {
67+
t.Fatalf("UnbundleAllXML(): %v", err)
68+
}
69+
70+
if diff := cmp.Diff(want, got); diff != "" {
71+
t.Errorf("want != got:\n%v\n", diff)
72+
}
73+
}
74+
75+
func TestBundleXML(t *testing.T) {
76+
want := `<!-- include Main -->
77+
<Canvas raycastTarget="false">
78+
<Defaults>
79+
<Button color="a"/>
80+
</Defaults>
81+
82+
<!-- include ui/CameraControl -->
83+
<Panel id="CameraControl"
84+
visibility="false"
85+
</Panel>
86+
87+
<!-- include ui/CameraControl -->
88+
<!-- include ui/Shop -->
89+
<Panel id="shop.window" class="drag"
90+
offsetXY="480 -70"
91+
width="260" height="500">
92+
<!-- include deep -->
93+
<Button id="deepinclude">
94+
</Button>
95+
<!-- include deep -->
96+
</Panel>
97+
<!-- include ui/Shop -->
98+
99+
</Canvas>
100+
101+
<!-- include Main -->
102+
`
103+
104+
input := map[string]string{
105+
"__root": `<Include src="Main"/>
106+
`,
107+
"Main.xml": `<Canvas raycastTarget="false">
108+
<Defaults>
109+
<Button color="a"/>
110+
</Defaults>
111+
112+
<Include src="ui/CameraControl"/>
113+
<Include src="ui/Shop"/>
114+
115+
</Canvas>
116+
`,
117+
"deep.xml": `<Button id="deepinclude">
118+
</Button>`,
119+
"ui/Shop.xml": `<Panel id="shop.window" class="drag"
120+
offsetXY="480 -70"
121+
width="260" height="500">
122+
<Include src="deep"/>
123+
</Panel>`,
124+
"ui/CameraControl.xml": `<Panel id="CameraControl"
125+
visibility="false"
126+
</Panel>
127+
`,
128+
}
129+
ff := tests.NewFF()
130+
ff.Fs = input
131+
132+
got, err := BundleXML(input[Rootname], ff)
133+
if err != nil {
134+
t.Fatalf("UnbundleAllXML(): %v", err)
135+
}
136+
137+
if diff := cmp.Diff(want, got); diff != "" {
138+
t.Errorf("want != got:\n%v\n", diff)
139+
}
140+
}

file/luaops.go renamed to file/textops.go

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,36 +7,32 @@ import (
77
"path"
88
)
99

10-
const (
11-
expectedSuffix = ".ttslua"
12-
)
13-
14-
// LuaOps allows for arbitrary reads and writes of luascript
15-
type LuaOps struct {
10+
// TextOps allows for arbitrary reads and writes of text files
11+
type TextOps struct {
1612
basepaths []string
1713
writeBasepath string
1814
readFileToBytes func(string) ([]byte, error)
1915
writeBytesToFile func(string, []byte) error
2016
}
2117

22-
// LuaReader serves to describe all ways to read luascripts
23-
type LuaReader interface {
18+
// TextReader serves to describe all ways to read luascripts
19+
type TextReader interface {
2420
EncodeFromFile(string) (string, error)
2521
}
2622

27-
// LuaWriter serves to describe all ways to write luascripts
28-
type LuaWriter interface {
23+
// TextWriter serves to describe all ways to write luascripts
24+
type TextWriter interface {
2925
EncodeToFile(script, file string) error
3026
}
3127

32-
// NewLuaOps initializes our object on a directory
33-
func NewLuaOps(base string) *LuaOps {
34-
return NewLuaOpsMulti([]string{base}, base)
28+
// NewTextOps initializes our object on a directory
29+
func NewTextOps(base string) *TextOps {
30+
return NewTextOpsMulti([]string{base}, base)
3531
}
3632

37-
// NewLuaOpsMulti allows for luascript to be read from multiple directories
38-
func NewLuaOpsMulti(readDirs []string, writeDir string) *LuaOps {
39-
return &LuaOps{
33+
// NewTextOpsMulti allows for luascript to be read from multiple directories
34+
func NewTextOpsMulti(readDirs []string, writeDir string) *TextOps {
35+
return &TextOps{
4036
basepaths: readDirs,
4137
writeBasepath: writeDir,
4238
readFileToBytes: func(s string) ([]byte, error) {
@@ -69,7 +65,7 @@ func NewLuaOpsMulti(readDirs []string, writeDir string) *LuaOps {
6965
}
7066

7167
// EncodeFromFile pulls a file from configs and encodes it as a string.
72-
func (l *LuaOps) EncodeFromFile(filename string) (string, error) {
68+
func (l *TextOps) EncodeFromFile(filename string) (string, error) {
7369
for _, base := range l.basepaths {
7470
p := path.Join(base, filename)
7571
b, err := l.readFileToBytes(p)
@@ -83,7 +79,7 @@ func (l *LuaOps) EncodeFromFile(filename string) (string, error) {
8379
}
8480

8581
// EncodeToFile takes a single string and decodes escape characters; writes it.
86-
func (l *LuaOps) EncodeToFile(script, file string) error {
82+
func (l *TextOps) EncodeToFile(script, file string) error {
8783
p := path.Join(l.writeBasepath, file)
8884
return l.writeBytesToFile(p, []byte(script))
8985
}
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func TestRead(t *testing.T) {
2929
ff := &fakeFiles{
3030
fs: map[string][]byte{"foo/bar": []byte("bar = 2")},
3131
}
32-
l := &LuaOps{
32+
l := &TextOps{
3333
basepaths: []string{"foo"},
3434
readFileToBytes: ff.read,
3535
}
@@ -52,7 +52,7 @@ func TestReadMulti(t *testing.T) {
5252
"foo2/bar": []byte("bar = 2"),
5353
},
5454
}
55-
l := &LuaOps{
55+
l := &TextOps{
5656
basepaths: []string{"foo", "foo2"},
5757
readFileToBytes: ff.read,
5858
}
@@ -75,7 +75,7 @@ func TestReadErr(t *testing.T) {
7575
"foo2/bar": []byte("bar = 2"),
7676
},
7777
}
78-
l := &LuaOps{
78+
l := &TextOps{
7979
basepaths: []string{"foo", "foo2"},
8080
readFileToBytes: ff.read,
8181
}
@@ -90,7 +90,7 @@ func TestWrite(t *testing.T) {
9090
ff := &fakeFiles{
9191
fs: map[string][]byte{},
9292
}
93-
l := LuaOps{
93+
l := TextOps{
9494
writeBasepath: "foo",
9595
writeBytesToFile: ff.write,
9696
}

0 commit comments

Comments
 (0)