package editor import ( "sort" "strings" "time" "pad/internal/browser" "pad/internal/io/pool/mock" ) // populateMockFileSystem seeds the mock filesystem with sample data for testing. // Creates a realistic directory structure with subdirectories, various file types, // and varied sizes/dates to exercise the browser functionality. func populateMockFileSystem(fs *mock.FileSystem) { baseTime := time.Date(2026, 5, 15, 10, 0, 0, 0, time.UTC) // --- Root directories --- dirs := []string{ "/Documents", "/Pictures", "/Music", "/Downloads", "/Projects", "/Projects/pad", "/Projects/goplus", "/Documents/Work", "/Documents/Personal", "/Pictures/Vacation", } for i, d := range dirs { fs.AddDir(d, baseTime.AddDate(0, 0, -i)) } // --- Root files --- rootFiles := []struct { name string size int64 days int }{ {"README.md", 2048, -30}, {"config.yaml", 512, -60}, {"notes.txt", 1024, -15}, {"setup.log", 4096, -7}, {".gitignore", 128, -90}, } for _, f := range rootFiles { fs.AddFile("/"+f.name, make([]byte, f.size), baseTime.AddDate(0, 0, f.days)) } // --- Documents/Work files --- workFiles := []struct { name string size int64 days int }{ {"Q1_Report.docx", 524288, -45}, {"Q2_Report.docx", 614400, -14}, {"Budget_2026.xlsx", 262144, -30}, {"Meeting_Notes.txt", 8192, -1}, {"Presentation.pptx", 1048576, -21}, } for _, f := range workFiles { fs.AddFile("/Documents/Work/"+f.name, make([]byte, f.size), baseTime.AddDate(0, 0, f.days)) } // --- Documents/Personal files --- personalFiles := []struct { name string size int64 days int }{ {"Resume.pdf", 32768, -60}, {"Tax_Return_2025.pdf", 131072, -90}, {"Recipe_Collection.txt", 4096, -120}, {"Letter_to_Friend.txt", 2048, -5}, } for _, f := range personalFiles { fs.AddFile("/Documents/Personal/"+f.name, make([]byte, f.size), baseTime.AddDate(0, 0, f.days)) } // --- Projects/pad source files --- padFiles := []struct { name string size int64 days int }{ {"main.go", 3072, -2}, {"state.go", 4096, -2}, {"logic.go", 5120, -1}, {"browser.go", 2048, -7}, {"types.go", 1536, -7}, {"index.go", 3584, -7}, {"layout.go", 2560, -5}, {"handlers.go", 1024, -3}, {"go.mod", 256, -30}, {"go.sum", 512, -30}, } for _, f := range padFiles { fs.AddFile("/Projects/pad/"+f.name, make([]byte, f.size), baseTime.AddDate(0, 0, f.days)) } // --- Pictures/Vacation files --- vacationFiles := []struct { name string size int64 days int }{ {"IMG_0001.jpg", 3145728, -180}, {"IMG_0002.jpg", 2621440, -180}, {"IMG_0003.jpg", 4194304, -179}, {"IMG_0004.jpg", 3670016, -179}, {"IMG_0005.jpg", 2097152, -178}, } for _, f := range vacationFiles { fs.AddFile("/Pictures/Vacation/"+f.name, make([]byte, f.size), baseTime.AddDate(0, 0, f.days)) } // --- Downloads files --- downloads := []struct { name string size int64 days int }{ {"installer.dmg", 104857600, -10}, {"archive.tar.gz", 52428800, -20}, {"patch.diff", 8192, -3}, } for _, f := range downloads { fs.AddFile("/Downloads/"+f.name, make([]byte, f.size), baseTime.AddDate(0, 0, f.days)) } } // sortModeKey converts a browser.SortMode to its JSON key string. func sortModeKey(mode browser.SortMode) string { switch mode { case 0: // SortModeNameAsc return "name_asc" case 1: // SortModeNameDesc return "name_desc" case 2: // SortModeDateAsc return "date_asc" case 3: // SortModeDateDesc return "date_desc" default: return "" } } // modeCount returns the total number of sort modes. func modeCount() int { return 4 } // buildBrowserIndex converts mock DirEntry results into a browser.DirectoryIndex // with pre-computed position maps for all sort modes. func buildBrowserIndex(entries []mock.DirEntry) *browser.DirectoryIndex { var browserEntries []browser.Entry for _, e := range entries { info, err := e.Info() if err != nil { continue } browserEntries = append(browserEntries, browser.Entry{ Path: e.Name(), Name: e.Name(), Size: info.Size(), ModTime: info.ModTime(), IsDir: info.IsDir(), }) } // Build position maps for all sort modes sortOrders := make(map[string][]int) for mode := browser.SortMode(0); mode < browser.SortMode(modeCount()); mode++ { key := sortModeKey(mode) if key != "" { sortOrders[key] = buildPositionMap(browserEntries, mode) } } return &browser.DirectoryIndex{ Path: "/", EntryCount: len(browserEntries), Entries: browserEntries, SortOrders: sortOrders, } } // buildPositionMap creates a sorted index → raw index mapping. func buildPositionMap(entries []browser.Entry, mode browser.SortMode) []int { n := len(entries) indices := make([]int, n) for i := range indices { indices[i] = i } // Sort indices based on entry comparison cmp := comparator(mode) sort.SliceStable(indices, func(i, j int) bool { a, b := entries[indices[i]], entries[indices[j]] return cmp(a, b) < 0 }) return indices } // comparator returns a comparison function for the given SortMode. // Returns negative if a < b, zero if equal, positive if a > b. func comparator(mode browser.SortMode) func(a, b browser.Entry) int { switch mode { case 0: // SortModeNameAsc return func(a, b browser.Entry) int { if c := strings.Compare(strings.ToLower(a.Name), strings.ToLower(b.Name)); c != 0 { return c } return strings.Compare(a.Name, b.Name) } case 1: // SortModeNameDesc return func(a, b browser.Entry) int { if c := strings.Compare(strings.ToLower(b.Name), strings.ToLower(a.Name)); c != 0 { return c } return strings.Compare(b.Name, a.Name) } case 2: // SortModeDateAsc return func(a, b browser.Entry) int { if c := a.ModTime.Compare(b.ModTime); c != 0 { return c } return strings.Compare(strings.ToLower(a.Name), strings.ToLower(b.Name)) } case 3: // SortModeDateDesc return func(a, b browser.Entry) int { if c := b.ModTime.Compare(a.ModTime); c != 0 { return c } return strings.Compare(strings.ToLower(a.Name), strings.ToLower(b.Name)) } default: return func(a, b browser.Entry) int { return 0 } } }