package rbxbuilder import ( "bytes" "ci.itzana.me/itzaname/rbxfile" "ci.itzana.me/itzaname/rbxfile/xml" "encoding/json" "fmt" "os" "path/filepath" "sort" "strings" ) type PlaceBuilder struct { Options *BuildSettings root *rbxfile.Root scripts map[string]string } func (b *PlaceBuilder) loadScript(path string) error { scriptName := strings.TrimSuffix(filepath.Base(path), ".lua") parentPath := filepath.Dir(path) parentName := filepath.Base(parentPath) basePath := strings.TrimSuffix(strings.TrimSuffix(path, parentName+string(os.PathSeparator)+filepath.Base(path)), seperator) if basePath == "" { basePath = parentPath } parentInstance := instanceFromScriptPath(b.root, parentPath) parentBaseInstance := instanceFromScriptPath(b.root, basePath) if parentBaseInstance == nil { return fmt.Errorf("base instance doesn't exist: %s", basePath) } if parentInstance == nil { class := "ModuleScript" path := parentPath + seperator + parentName + ".lua" if override, ok := b.scripts[path]; ok { class = override } script := loadScript(parentName, class, b.Options.Source+path) if script != nil { if err := parentBaseInstance.AddChild(script); err != nil { return err } parentInstance = script } else { if err := parentBaseInstance.AddChild(generateFolder(parentName)); err != nil { return err } parentInstance = script } } if scriptName == parentName { return nil } // Load normal scripts class := "ModuleScript" if override, ok := b.scripts[path]; ok { class = override } script := loadScript(scriptName, class, b.Options.Source+path) if script != nil { if err := parentInstance.AddChild(script); err != nil { return err } } else { return fmt.Errorf("failed to load script: %s", path) } return nil } func (b *PlaceBuilder) scriptTree() ([]string, error) { var filelist []string err := filepath.Walk(b.Options.Source, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() { if filepath.Ext(path) == ".lua" { filelist = append(filelist, strings.TrimPrefix(path, b.Options.Source)) } } return nil }) sort.Slice(filelist, func(i, j int) bool { return strings.Count(filelist[i], string(os.PathSeparator)) < strings.Count(filelist[j], string(os.PathSeparator)) }) return filelist, err } func (b *PlaceBuilder) createScripts() error { tree, err := b.scriptTree() if err != nil { return err } for i := 0; i < len(tree); i++ { if err := b.loadScript(tree[i]); err != nil { return err } } return nil } func (b *PlaceBuilder) loadManifest() error { b.scripts = map[string]string{} file, err := os.Open(b.Options.Source + seperator + "manifest.json") if err != nil { return fmt.Errorf("failed to load manifest file") } defer file.Close() var manifest Manifest if err := json.NewDecoder(file).Decode(&manifest); err != nil { return fmt.Errorf("failed to load manifest file") } for i := 0; i < len(manifest.Override); i++ { b.scripts[manifest.Override[i].Path] = manifest.Override[i].Class } buffer := bytes.NewBuffer(manifest.Template) root, err := xml.Deserialize(buffer, nil) if err != nil { return fmt.Errorf("failed to load template from manifest") } b.root = root return nil } func (b PlaceBuilder) Build() error { if err := b.loadManifest(); err != nil { return err } if err := b.createScripts(); err != nil { return err } if b.Options.Writer != nil { return xml.Serialize(b.Options.Writer, nil, b.root) } file, err := os.OpenFile(b.Options.Output, os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return fmt.Errorf("failed to open output file") } defer file.Close() return xml.Serialize(file, nil, b.root) }