package gsp import ( "os" "path/filepath" "sort" "strings" "gopkg.in/yaml.v3" ) var ignoredDirs = map[string]bool{ ".git": true, ".gsp": true, "node_modules": true, "dist": true, "build": true, "bin": true, } func LoadProject(root string) (*Project, error) { absRoot, err := filepath.Abs(root) if err != nil { return nil, err } project := &Project{ Root: absRoot, ByID: map[string]*Unit{}, Duplicates: map[string][]*Unit{}, } manifest, err := loadManifest(absRoot) if err != nil { project.LoadIssues = append(project.LoadIssues, Issue{Level: "error", Code: "manifest_error", File: "gsp.manifest", Message: err.Error()}) } project.Manifest = manifest var files []string for _, scanEntry := range project.Manifest.scanEntries() { scanPath := filepath.Join(absRoot, filepath.FromSlash(scanEntry)) info, statErr := os.Stat(scanPath) if statErr != nil { project.LoadIssues = append(project.LoadIssues, Issue{Level: "warning", Code: "scan_missing", File: scanEntry, Message: "scan path does not exist"}) continue } if !info.IsDir() { if strings.EqualFold(filepath.Ext(scanPath), ".gsp") { files = append(files, scanPath) } continue } walkErr := filepath.WalkDir(scanPath, func(path string, entry os.DirEntry, walkErr error) error { if walkErr != nil { project.LoadIssues = append(project.LoadIssues, Issue{Level: "error", Code: "walk_error", File: path, Message: walkErr.Error()}) return nil } if entry.IsDir() { if ignoredDirs[entry.Name()] { return filepath.SkipDir } return nil } if strings.EqualFold(filepath.Ext(entry.Name()), ".gsp") { files = append(files, path) } return nil }) if walkErr != nil { return nil, walkErr } } sort.Strings(files) for _, file := range files { unit, err := readUnit(absRoot, file) if err != nil { project.LoadIssues = append(project.LoadIssues, Issue{Level: "error", Code: "parse_error", File: relPath(absRoot, file), Message: err.Error()}) continue } project.Units = append(project.Units, unit) if unit.ID == "" { continue } if existing, ok := project.ByID[unit.ID]; ok { project.Duplicates[unit.ID] = append(project.Duplicates[unit.ID], existing, unit) continue } project.ByID[unit.ID] = unit } return project, nil } func readUnit(root, file string) (*Unit, error) { data, err := os.ReadFile(file) if err != nil { return nil, err } var unit Unit if err := yaml.Unmarshal(data, &unit); err != nil { return nil, err } unit.File = relPath(root, file) return &unit, nil } func relPath(root, path string) string { rel, err := filepath.Rel(root, path) if err != nil { return path } return filepath.ToSlash(rel) }